Signing Platform Web App
On this page
- Sekilas
- Yang dikerjakannya
- A. Menandatangani dokumen di browser
- B. Menjaga orang tetap login, dengan aman
- C. Membuktikan identitas
- D. Mengelola stempel dan sertifikat
- E. Menyetujui sistem yang menandatangani lewatnya
- F. Menjalankan platform bagi administrator
- Antarmuka
- Standar
- Di dalamnya
- Keputusan rancangan
- Spesifikasi
- Terkait
- Istilah
Signing Platform Web App adalah aplikasi browser dari platform penandatanganan dokumen: tempat orang login, membuktikan identitasnya, menaruh stempel di PDF lalu menandatanganinya, mengelola sertifikatnya, dan menyetujui sistem yang menandatangani atas nama mereka — serta tempat administrator menjalankan organisasi, user, peninjau, dan sistem client. Ia single-page application berbahasa Vue dan TypeScript yang berbicara dengan satu backend, yaitu core API platform, dan menjaga seseorang tetap login tanpa pernah menyimpan access token-nya di tempat yang bisa ditemukan script belakangan.
Saya membangunnya sebagai pasangan inti: inti memegang aturan dan data, dan aplikasi ini mengubahnya menjadi layar. Sebagian besar keputusan di dalamnya menyangkut dua urusan yang tak boleh keliru di produk penandatanganan — menjaga sesi tetap aman tanpa membuat orang login berulang-ulang, dan menaruh stempel persis di tempat yang dilihat penanda tangan.
Sekilas
| Bahasa & framework | TypeScript, dengan Vue 3, Pinia, vue-router, vue-i18n, Tailwind CSS, dan Vite; pdf.js untuk dokumen |
|---|---|
| Arsitektur | Feature-Based Frontend Architecture |
| Versi | 2.13.2 |
| Status | Stabil, berjalan di production |
| Bentuk | Single-page application yang dilayani di path-nya sendiri, di samping halaman beranda publik yang dirender di server |
| Kode sumber | Privat |
Yang dikerjakannya
A. Menandatangani dokumen di browser
Dokumen di-upload langsung ke storage lewat link berumur pendek dan diberi penanda tangan dalam tahap berurutan. Penanda tangan membukanya di PDF viewer dengan render beresolusi tinggi, zoom, dan dukungan sentuh, menyeret dan mengubah ukuran stempelnya di halaman, memilih sertifikat, mengisi alasan, lokasi, dan passphrase-nya, lalu menandatangani. Satu halaman publik memverifikasi PDF bertanda tangan apa pun yang diseret, ditempel, atau dipilih dari disk.
B. Menjaga orang tetap login, dengan aman
Sesi bertahan melewati reload dan access token yang kedaluwarsa tanpa meminta password lagi, dan tanpa token itu pernah ditulis ke storage browser. Login bisa dengan password, lewat identity provider eksternal, dengan autentikasi dua faktor, dan dengan perlindungan bot yang tak terlihat.
C. Membuktikan identitas
Sebelum seseorang boleh memegang sertifikat tanda tangan, ia mengirim data pribadinya dan dua foto, lalu menyelesaikan tantangan wajah langsung yang tertanam di halaman. Kartu status dan banner memberi tahu sampai di mana verifikasinya, dan mengingatkan paling banyak sekali per sesi.
D. Mengelola stempel dan sertifikat
Penanda tangan menggambar atau meng-upload gambar tanda tangannya atau membuat stempel QR, mengajukan sertifikat yang dilindungi passphrase miliknya sendiri, dan melihat status tiap sertifikat. Konflik, misalnya sertifikat yang sudah ada, dijelaskan di dialog khusus, bukan sekadar error polos.
E. Menyetujui sistem yang menandatangani lewatnya
Ketika sistem client ingin bertindak atas nama seorang user, user itu melihat persis sistem mana dan izin apa, lalu mengizinkan atau menolak di dialog persetujuan. Sistem client juga bisa mengirim user langsung ke dokumen yang harus ditandatangani dan menerimanya kembali setelah selesai. User bisa melihat dan mencabut setiap sistem yang tertaut ke akunnya.
F. Menjalankan platform bagi administrator
Administrator mendapat dashboard yang disesuaikan dengan role-nya, dan mengelola organisasi beserta unitnya, anggota dan allowlist e-mail hingga seribu alamat, user, peninjau identitas, account manager, sertifikat, dan sistem client. Tindakan yang merusak atau berhak istimewa meminta password administrator sekali lagi.
Antarmuka
| Bidang | Yang bisa dilakukan orang di sana |
|---|---|
| Login | login, mendaftar, verifikasi e-mail, reset password, memakai provider eksternal, menyelesaikan dua faktor |
| Dokumen | upload, menambah penanda tangan bertahap, menaruh stempel, menandatangani, mengunduh file bertanda tangan |
| Verifikasi | memeriksa PDF bertanda tangan apa pun, tanpa akun |
| Profil | data pribadi, password, dua faktor, verifikasi identitas |
| Stempel dan sertifikat | gambar tanda tangan, stempel QR, pengajuan dan status sertifikat |
| Client | memberi persetujuan ke sistem client, meninjau dan mencabutnya |
| Administrasi | dashboard, organisasi, user, peninjau, sertifikat, sistem client — di layar desktop |
Standar
| Standar | Yang dijaminnya di sini |
|---|---|
| OAuth 2.0 dan OpenID Connect dengan PKCE (RFC 7636) | login lewat provider eksternal tanpa client secret di browser, dengan pemeriksaan state, nonce, dan issuer |
| JSON Web Token | access token berumur pendek yang hanya disimpan di memori dan diperbarui diam-diam |
| TOTP (RFC 6238) | autentikasi dua faktor dengan aplikasi authenticator apa pun, disiapkan dari kode QR |
| PDF, dirender dengan pdf.js | dokumen ditampilkan persis seperti yang akan ditandatangani, dengan posisi stempel dikonversi ke koordinat PDF |
| Pesan antar-jendela (postMessage) | tantangan wajah yang tertanam melaporkan hasilnya ke halaman yang membukanya |
| TypeScript ketat | setiap build memeriksa tipe seluruh aplikasi sebelum menghasilkannya |
Di dalamnya
Aplikasi ini mengikuti Feature-Based Frontend Architecture: tiap fitur — login, dokumen, profil, stempel, sertifikat, client, dashboard, administrasi — punya view, component, dan composable miliknya sendiri, dan fitur tak pernah saling meng-import. Component tak pernah memanggil jaringan sendiri; setiap request lewat satu lapisan service dengan satu HTTP client, dan state sesi serta antarmuka tinggal di beberapa store bersama. Tampilan visualnya berasal dari paket design system terpisah di balik satu alias, jadi seluruh tampilan bisa ditukar tanpa menyentuh satu fitur pun.
Keputusan rancangan
Access token tinggal di memori dan tidak di tempat lain. Saya menyimpan sesi di memori, di dalam store Pinia, dan membiarkan credential refresh yang tak bisa dibaca script mana pun membawanya melewati reload. Saat aplikasi start, ia meminta token baru bahkan sebelum router dipasang, jadi reload mengembalikan orang ke tempatnya semula dalam keadaan login, dan refresh yang gagal cukup membuatnya mulai dalam keadaan logout.
Satu refresh, berapa pun request yang gagal bersamaan. Access token berumur pendek, jadi beberapa request sering mendapati token kedaluwarsa pada saat yang sama. HTTP client menangkap jawaban unauthorized pertama, memulai satu refresh, dan memarkir setiap request gagal lainnya di antrean; begitu token baru tiba, tiap request di antrean diulang dengan token itu. Satu versi awal mengulang request di antrean dengan header basi yang sudah mereka simpan, dan gagal lagi; kini token baru diserahkan ke masing-masing secara eksplisit. Bila refresh-nya sendiri gagal, sesi dibersihkan dan orang itu dibawa ke halaman login.
Login eksternal mengikuti spesifikasi sampai ke byte-nya. Login lewat provider eksternal memakai PKCE dengan verifier yang dibuat dari byte acak dan di-encode dalam base64 aman-URL. Versi pertama saya memakai base64 biasa, yang ditolak identity provider yang ketat sebagai code verifier tak sah. State dan nonce dibuat acak, issuer diperiksa saat kembali untuk mencegah kekeliruan provider, dan setiap nilai sementara dibersihkan begitu pertukarannya berhasil atau gagal.
Stempel mendarat persis di tempat penanda tangan menaruhnya. Viewer menggambar halaman pada kepadatan piksel layar yang sebenarnya, sehingga stempel yang ditaruh di layar tajam tak bergeser akibat skala antara piksel layar dan piksel kanvas — bug yang saya perbaiki sejak awal. Ketika penanda tangan mengonfirmasi, kotak stempel dikonversi dari koordinat layar, diukur dari kiri-atas dalam piksel, ke koordinat PDF, diukur dari kiri-bawah dalam point, bersama nomor halamannya, memakai konversi milik viewer itu sendiri. Stempel menjaga proporsinya saat diubah ukurannya, dan pratinjau stempel diletakkan di atas putih murni, karena latar kotak-kotak menyembunyikan tanda tangan yang digambar dengan pena.
Persetujuan dan serah-terima lewat redirect, bukan lewat penyematan. Sistem client dulu menyematkan halaman tanda tangan di dalam frame. Saya menggantinya dengan redirect: client mengirim user ke aplikasi dengan token sekali pakai, user memberi persetujuan atau menandatangani di dalam aplikasi yang sudah dipercayainya, lalu dikirim kembali ke client dengan state aslinya dan hasilnya. Persetujuan pindah dari halaman penuh ke dialog di layar client, tempat user juga melihat setiap sistem yang sudah tertaut kepadanya.
Design system berupa paket, bukan folder style. Aplikasi ini dibangun ulang secara bertahap menjadi module per fitur di atas paket design system yang terpisah. Paket itu dirujuk lewat alias, dan mengarahkan alias ke build tema lain mengganti tampilan seluruh aplikasi — sudah diuji dengan melakukannya persis seperti itu. Saya sempat memakainya sebagai git submodule lalu kembali ke salinan hasil kompilasi yang disimpan di repository, supaya source tema tetap privat.
Setiap label mengikuti bahasa yang aktif. Semua teks antarmuka pindah ke file terjemahan dalam sepuluh langkah yang terukur. Definisi kolom dan navigasi yang dulu dibuat sekali saat start diubah menjadi nilai computed, karena kalau tidak, labelnya membeku dalam bahasa apa pun yang aktif saat aplikasi boot. Satu script mencocokkan setiap key yang dipakai di kode dengan file terjemahan, karena sistem tipe tak bisa melakukannya. Satu suntingan massal menghasilkan binding cacat yang diam-diam menyembunyikan tombol satu dialog — tombol kirim identitas lenyap — dan baik pemeriksaan tipe maupun build tak menangkapnya, jadi pola itu kini dicari secara eksplisit. Teks yang berakhir di dalam PDF bertanda tangan, seperti kata-kata stempel, sengaja tak diterjemahkan: ia menjadi bagian dari dokumen permanen.
Bundle browser hanya menerima yang memang untuknya. Hanya variabel berawalan publik milik aplikasi yang sampai ke bundle; masing-masing diberi tipe dan dinormalkan di satu module konfigurasi.
Setiap layar tahu tempatnya. Halaman beranda publik pindah keluar dari aplikasi ini ke inti yang dirender di server, supaya mesin pencari bisa mengindeksnya, dan aplikasi kini tinggal di path-nya sendiri. Tiap route baru dimuat saat dikunjungi. Bagian administrasi ditujukan untuk layar desktop dan menyatakannya di layar yang lebih kecil, sementara layar penandatanganan dibangun untuk sentuhan, dengan navigasi bawah dan ruang untuk safe area ponsel. Kolom pencarian memakai satu input debounced bersama yang tak pernah kehilangan fokus selagi hasil dimuat.
Tak ada jawaban yang membocorkan apa yang ada. Reset password dan e-mail verifikasi selalu menampilkan pesan yang sama, entah akunnya ada atau tidak, dan tautan yang datang lewat e-mail diperiksa terhadap origin API sendiri sebelum diikuti aplikasi.
Spesifikasi
| Berbicara dengan | Satu backend — core API platform — dengan credential disertakan untuk pembaruan sesi; file langsung ke storage lewat link berumur pendek |
|---|---|
| Sesi | Access token hanya di memori; refresh senyap saat start; satu refresh berantrean untuk kegagalan bersamaan |
| Login | Password, provider eksternal dengan PKCE, dua faktor dengan kode pemulihan, perlindungan bot tak terlihat |
| Dokumen | PDF hingga 50 MB; penanda tangan dalam tahap berurutan; penempatan stempel dikonversi ke koordinat PDF |
| Antarmuka | Tema gelap dan terang; layar penandatanganan ramah sentuhan; administrasi di desktop; terjemahan dengan pencocokan key |
| Build | Vite dengan pemeriksaan tipe TypeScript ketat di setiap build; route dipecah per halaman |
Terkait
Core API di balik setiap layar: token, data, dan aturan platform.
Tantangan wajah yang tertanam di form verifikasi identitas.
Signer yang menghasilkan tanda tangan PDF jangka panjang yang ditempatkan, diunduh, dan diverifikasi aplikasi ini.
Istilah
- Single-page application
- Aplikasi web yang dimuat sekali lalu mengganti layarnya di dalam browser, hanya meminta data ke server.
- Refresh token
- Credential berumur panjang yang hanya dipakai untuk mendapatkan access token baru yang berumur pendek, di sini disimpan di tempat yang tak bisa dibaca script mana pun.
- Interceptor
- Kode yang melihat setiap request atau response dari HTTP client dan bisa mengubah, mengulang, atau menolaknya.
- PKCE
- Proof Key for Code Exchange: rahasia sekali pakai yang membuktikan bahwa aplikasi yang menuntaskan login adalah aplikasi yang memulainya.
- Design system
- Kumpulan style dan component bersama yang memberi setiap layar tampilan dan perilaku yang sama.
Technologies covered
- TypeScript
- Pinia
- Vite
- Vue
- OAuth 2.0