Perbandingan Model Autentikasi: SPA, BFF, & Server-Side
Dokumen ini menjelaskan perbedaan mendasar, model ancaman (threat models), dan pertimbangan arsitektur antara tiga pendekatan implementasi autentikasi OIDC dalam ekosistem aplikasi Duta Meta Graha.
1. Tiga Model Arsitektur Autentikasi
Dalam ekosistem DUMEG, aplikasi yang mengintegrasikan DUMEG Identity (Keycloak) terbagi menjadi tiga kategori arsitektur utama:
flowchart TD
subgraph ModelA["1. Single Page Application (SPA / Public Client)"]
BrowserA["Browser Pengguna\n(React / Vue SPA)"]
KeycloakA["DUMEG Identity\n(login.dumeg.com)"]
BrowserA <-->|"OIDC Auth Code + PKCE S256\n(In-Memory / SessionStorage)"| KeycloakA
end
subgraph ModelB["2. Backend-for-Frontend (BFF / SSR Confidential Client)"]
BrowserB["Browser Pengguna"]
BFF["Server BFF / SSR\n(Next.js / Laravel)"]
KeycloakB["DUMEG Identity\n(login.dumeg.com)"]
BrowserB <-->|"Encrypted Session Cookie\n(HttpOnly, Secure, SameSite)"| BFF
BFF <-->|"OIDC PKCE + Client Secret\n(Back-Channel Server-to-Server)"| KeycloakB
end
subgraph ModelC["3. Backend API / Resource Server"]
ClientC["Klien HTTP / Web / Mobile"]
NestJS["Backend API\n(NestJS / Node / Go)"]
KeycloakC["DUMEG Identity\n(login.dumeg.com)"]
ClientC <-->|"Bearer Token / Session Cookie"| NestJS
NestJS <-->|"JWKS Key Set Signature Validation / Introspection"| KeycloakC
end2. Matriks Perbandingan Komprehensif
| Kriteria Evaluasi | Model 1: SPA Murni (Public Client) | Model 2: BFF / SSR (Confidential Client) | Model 3: Backend API (Server-Side) |
|---|---|---|---|
| Contoh Framework | React.js (Vite), Vue 3 SPA | Next.js (App Router), Laravel + Blade | NestJS, Express, Spring Boot |
| OIDC Client Type | Public Client | Confidential Client | Confidential Client / Resource Server |
Penyimpanan client_secret | DILARANG (Tidak memiliki server) | Aman di Server (.env server) | Aman di Server (.env server) |
| Alur OIDC Rekomendasi | Authorization Code + PKCE S256 | Authorization Code + PKCE S256 | Auth Code Back-Channel / JWT Bearer |
| Tempat Penyimpanan Token | In-Memory (JavaScript) / sessionStorage | Session Server / HttpOnly Encrypted Cookie | Server Memory / Redis / JWT Bearer |
| Resistansi Terhadap XSS | Rentan (Skrip JS di browser dapat mengintip memori) | Sangat Tinggi (Cookie HttpOnly tak terbaca JavaScript) | Sangat Tinggi (Token tidak menyentuh browser pengguna) |
| Resistansi Terhadap CSRF | Tinggi (Menggunakan Bearer Authorization Header) | Memerlukan Proteksi CSRF (SameSite Cookie + CSRF Token) | Memerlukan Proteksi CSRF jika menggunakan Cookies |
| Mekanisme Refresh Token | Perputaran token di memori atau silent iframe | Dikelola aman di backend tanpa eksposur browser | Dikelola aman oleh service backend |
| Kompleksitas Implementasi | Rendah - Menengah | Menengah | Menengah - Tinggi |
3. Mengapa Menghindari localStorage sebagai Default pada SPA?
Sebagian besar tutorial di internet menyarankan penyimpanan token di window.localStorage demi kemudahan akses. Namun, pada standar keamanan DUMEG Identity:
Bahaya Fatal `localStorage` untuk Token Sensitif
localStorage dapat dibaca oleh seluruh skrip JavaScript yang dieksekusi di origin yang sama. Jika aplikasi web Anda mengalami celah Cross-Site Scripting (XSS) — baik melalui input pengguna yang tidak ter-sanitasi, library npm pihak ketiga yang terkompromi, atau penyusupan CDN — penyerang dapat mengekstraksi seluruh access_token dan id_token dalam hitungan milidetik.
Rekomendasi Penyimpanan Token untuk SPA (React / Vue Standalone):
- Prioritas 1 (Teraman): In-Memory Storage Simpan token murni dalam variabel memori JavaScript (misal melalui state library OIDC atau store Pinia/Redux). Token akan hilang saat tab browser ditutup atau di-refresh, namun dapat dipulihkan secara transparan melalui silent renew (memanfaatkan session cookie SSO Keycloak yang masih aktif di browser).
- Prioritas 2 (Toleransi Tab Navigation):
sessionStoragesessionStoragemengisolasi data hanya pada satu tab aktif. Data otomatis terhapus saat tab ditutup dan tidak dapat diakses dari tab/jendela browser lain. - Alternatif Terbaik: Bila aplikasi memiliki komponen server, migrasikan ke pola Backend-for-Frontend (BFF).
4. Keunggulan Pola Backend-for-Frontend (BFF)
Pola BFF (seperti yang diimplementasikan pada Next.js App Router atau Laravel) adalah standar emas industri untuk aplikasi web modern yang membutuhkan tingkat keamanan tinggi:
[Browser Pengguna] <====== HttpOnly, Secure, SameSite Cookie ======> [Server BFF Next.js/Laravel]
│
OIDC PKCE + Client Secret
│
▼
[DUMEG Identity (Keycloak)]Manfaat Utama BFF:
- Pemisahan Token Kredensial: Browser pengguna tidak pernah menerima
access_token,refresh_token, maupunclient_secret. - Proteksi Eksfiltrasi Token via XSS: Cookie sesi bertanda
HttpOnlytidak dapat dibaca atau dicuri oleh skrip JavaScript di browser. - Audit Terpusat: Penanganan izin, validasi masa berlaku token, dan pembaruan (refresh) dilakukan secara deterministik di server.
Perjalanan `id_token_hint` pada RP-Initiated Logout & Proteksi Kebocoran
Meskipun arsitektur BFF menjaga access_token dan refresh_token tetap berada di server backend (tidak diekspos ke lingkungan JavaScript browser), perlu dicatat bahwa standar RP-Initiated Logout (OIDC 1.0) mengharuskan browser diarahkan ke endpoint logout Keycloak dengan parameter id_token_hint pada query string URL.
Karena parameter ini melewati URL di browser pengguna, klaim "zero token leakage" mutlak tidak tepat tanpa menerapkan mitigasi berikut:
- Proteksi Referrer Header: Pasang konfigurasi
Referrer-Policy: strict-origin-when-cross-originatauno-referreragar URL logout yang memuatid_token_hinttidak bocor ke domain pihak ketiga via header HTTPReferer. - Sanitasi Access Log: Pastikan reverse proxy (seperti Caddy atau Nginx) dan web server dikonfigurasi untuk tidak mencatat query string sensitif pada access log (menghindari ID Token tersimpan dalam arsip log teks biasa).
Klarifikasi Keamanan XSS pada Arsitektur BFF
Penggunaan pola BFF dan cookie HttpOnly mengeliminasi risiko pencurian token langsung (token exfiltration) karena skrip berbahaya tidak dapat membaca nilai cookie melalui DOM atau Storage API. Namun, pola BFF tidak sepenuhnya menghilangkan ancaman XSS (Cross-Site Scripting) secara menyeluruh.
Apabila penyerang berhasil menyuntikkan skrip jahat ke dalam aplikasi (misalnya akibat celah input tidak ter-sanitasi), skrip tersebut tetap dapat mengirimkan request HTTP ke API BFF atas nama pengguna yang sedang aktif (session riding). Oleh sebab itu, strategi pertahanan berlapis (defense-in-depth) tetap wajib diimplementasikan: sanitasi input dan escaping output, penerapan Content Security Policy (CSP) yang ketat, dan proteksi CSRF.
5. Panduan Pemilihan Model untuk Tim Pengembang
Gunakan diagram keputusan berikut untuk menentukan model integrasi yang tepat untuk aplikasi Anda:
flowchart TD
Q1{"Apakah aplikasi memiliki\nserver backend sendiri?"}
Q1 -- "Ya, full-stack SSR\n(Next.js, Nuxt, Laravel)" --> OptBFF["Gunakan Model 2: BFF / SSR\n(Confidential Client)\n👉 Panduan Next.js / Laravel"]
Q1 -- "Ya, backend API terpisah\n(NestJS, Spring, Go)" --> OptAPI["Gunakan Model 3: Backend API\n(Resource Server)\n👉 Panduan NestJS"]
Q1 -- "Tidak, static SPA murni\n(React Vite, Vue CLI)" --> OptSPA["Gunakan Model 1: Public SPA\n(In-Memory / SessionStorage PKCE)\n👉 Panduan React.js / Vue"]6. Langkah Selanjutnya
Pelajari panduan implementasi teknis untuk masing-masing framework: