Masalah dan Konteks yang Berlaku
Produk Anda perlu membaca kalender pihak ketiga atas nama pengguna tanpa menangani kata sandi kalender milik pengguna tersebut. Jelaskan alur lengkap OAuth 2.0 authorization code dengan PKCE untuk public client, termasuk apa yang harus divalidasi oleh backend aplikasi dan server otorisasi.
Asumsikan klien berupa browser single-page application (SPA) atau aplikasi native. Klien tidak dapat menjaga kerahasiaan satu kredensial statis secara andal di seluruh instance yang di-deploy, sehingga berstatus sebagai public client. Server otorisasi mengautentikasi pengguna, mengumpulkan persetujuan (consent), dan menerbitkan token. Resource server mengekspos API kalender. Cakupan (scope) awal adalah akses kalender read-only; penerbitan refresh token bergantung pada kebijakan server otorisasi.
Panduan wawancara backend publik yang diterbitkan pada Maret 2026 meminta kandidat untuk menjelaskan authorization code grant, PKCE, public dan confidential client, serta batas antara OAuth dan OpenID Connect. Standar keamanan dasar saat ini adalah OAuth 2.0 Security Best Current Practice: public client wajib menggunakan PKCE, confidential client juga direkomendasikan untuk menggunakannya, server otorisasi harus mencegah downgrade PKCE, dan redirect URI yang terdaftar memerlukan pencocokan persis (exact matching).
Ini adalah pertanyaan backend karena keterampilan intinya adalah merancang dan memvalidasi protokol otorisasi lintas layanan, batasan token, dan kontrol keamanan. Pengalihan browser (browser redirect) hanyalah sarana transportasinya.
Apa yang Dievaluasi oleh Pewawancara
Pertama, apakah kandidat dapat memisahkan keempat peran? Resource owner adalah pengguna, klien adalah produk agregasi kalender, server otorisasi menangani autentikasi, persetujuan, dan penerbitan token, sedangkan resource server memiliki data kalender. Mengacaukan klien dengan pengguna akan membuat setiap pernyataan berikutnya mengenai izin dan audiens token menjadi bias.
Kedua, apakah kandidat dapat menjelaskan mengapa alur authorization code memiliki dua tahap (legs)? Browser hanya menerima authorization code sekali pakai yang berumur pendek. Klien menukarkannya di token endpoint, sehingga access token tidak berpindah melalui URL pengalihan browser. PKCE kemudian mengikat rahasia acak yang dibuat untuk permintaan otorisasi tersebut ke pertukaran kode tadi. Pihak yang mencegat kode tetapi tidak memiliki code_verifier tetap tidak dapat menukarkannya.
Ketiga, apakah kandidat dapat menjaga ketepatan tanggung jawab dari masing-masing kontrol?
| Kontrol | Pengikatan utama | Masalah utama yang ditangani |
|---|---|---|
state | Sesi klien pemula ke callback | Korelasi permintaan dan CSRF login |
| PKCE | Permintaan otorisasi ke pertukaran token | Pencegatan kode dan injeksi kode |
| Autentikasi klien | Confidential client ke server otorisasi | Identitas klien yang menukarkan kode |
| Redirect URI persis | Klien terdaftar ke callback yang diizinkan | Pengiriman kode ke endpoint yang dikontrol penyerang |
nonce OIDC | Permintaan login ke ID Token | Pemutaran ulang (replay) ID Token dan korelasi sesi login |
Terakhir, jawaban yang kuat menyatakan batasan dari PKCE. PKCE tidak menghentikan XSS same-origin untuk membaca verifier atau token. PKCE tidak menggantikan TLS, validasi pengalihan, hak istimewa terendah (least privilege), penyimpanan token yang aman, atau protokol autentikasi.
Pertanyaan Klarifikasi Sebelum Menjawab
- Apakah klien bersifat public atau confidential? SPA atau aplikasi native tidak dapat membuktikan
identitas dengan client secret statis. Aplikasi web dengan backend terkontrol dapat bersifat confidential dan harus mengautentikasi di token endpoint selain menggunakan PKCE.
- Apakah tujuannya otorisasi API atau login produk? Akses API kalender menggunakan OAuth. Jika
produk membutuhkan identitas pengguna, gunakan OIDC dan validasi ID Token alih-alih menyimpulkan identitas dari access token.
- Apakah klien menggunakan satu server otorisasi atau penyedia identitas yang dikonfigurasi per tenant?
Banyaknya penerbit (issuer) memerlukan validasi penerbit atau redirect URI yang berbeda untuk mencegah serangan mix-up.
- Apakah akses offline diperlukan? Jangan meminta refresh token jika tidak diperlukan. Jika
diperlukan, tentukan rotasi, deteksi pemutaran ulang (replay), pencabutan (revocation), dan kedaluwarsa.
- Di mana transaction state callback disimpan? Pertahankan pengikatan
state → code_verifier.
Satu verifier global akan merusak tab yang dibuka bersamaan; menaruhnya di URL atau log akan menghancurkan kerahasiaan.
- Berapa banyak akses kalender yang dibutuhkan? Mulailah dengan scope read-only yang dibutuhkan oleh
fitur. Izin menulis atau tingkat seluruh akun memerlukan justifikasi konkret.
Kerangka Jawaban 30 Detik
“Untuk setiap upaya otorisasi, saya membuat state yang tidak dapat ditebak dan code_verifier dengan entropi tinggi, lalu menurunkan S256 code_challenge. Browser menuju ke authorization endpoint dengan response_type=code, client ID, redirect URI yang terdaftar persis, scope minimal, state, dan challenge. Pengguna hanya mengautentikasi di server otorisasi. Callback mengembalikan kode sekali pakai berumur pendek dan state yang sama. Klien memvalidasi state, memuat verifier transaksi tersebut, dan mengirimkan kode, redirect URI, serta verifier ke token endpoint. Server hanya menerbitkan access token setelah memvalidasi kode, klien, redirect URI, dan pengikatan challenge. Public client tidak bisa memperlakukan secret statis sebagai kredensial; confidential client juga melakukan autentikasi. PKCE melindungi pertukaran kode, state mengorelasikan permintaan, dan ID Token OIDC adalah yang mendukung identitas login.”
Pembahasan Mendalam Langkah Demi Langkah
Langkah satu: buat transaksi otorisasi sekali pakai.
Untuk setiap tindakan “hubungkan kalender”, klien menghasilkan dua nilai acak yang independen:
stateadalah ID transaksi yang tidak dapat ditebak yang terikat pada sesi pengguna saat ini,
penerbit yang diharapkan, callback URI, dan tujuan pasca-otorisasi.
code_verifierdihasilkan dengan sumber acak yang aman secara kriptografis. RFC 7636 mendefinisikannya
sebagai 43 hingga 128 karakter unreserved dan merekomendasikan setidaknya 256 bit entropi.
Klien menurunkan challenge sebagai berikut:
code_challenge = base64url_without_padding(
SHA256(ASCII(code_verifier))
)
code_challenge_method = S256Simpan catatan state → code_verifier terpisah untuk setiap transaksi. Dua tab browser dapat memulai dua koneksi kalender secara bersamaan, sehingga model data “verifier terbaru” tidak valid. Berikan waktu kedaluwarsa yang pendek pada transaksi dan hapus setelah berhasil atau gagal permanen. Jangan meletakkan verifier di redirect URL, analitik, atau log aplikasi.
Langkah dua: susun permintaan otorisasi.
Klien mengarahkan user agent ke authorization endpoint:
GET /authorize
?response_type=code
&client_id=calendar-client
&redirect_uri=registered-callback
&scope=calendar.read
&state=random-transaction-id
&code_challenge=derived-challenge
&code_challenge_method=S256Server otorisasi memvalidasi client ID, tipe respons, scope, dan redirect URI. Redirect URI harus cocok secara persis dengan nilai yang telah didaftarkan sebelumnya, bukan sekadar domain luas, subpath arbitrer, atau parameter penerusan yang dikendalikan pengguna. Kredensial pengguna hanya diserahkan ke server otorisasi. Klien menerima hasil otorisasi, tidak pernah menerima kata sandi pengguna.
Langkah tiga: proses callback sebelum memanggil token endpoint.
Setelah persetujuan, server otorisasi mengalihkan user agent ke callback yang terdaftar dengan code dan state asli. Klien pertama-tama memvalidasi transaksi lokalnya:
- State ada, belum kedaluwarsa, dan belum digunakan.
- State terikat pada sesi browser saat ini dan server otorisasi yang diharapkan.
- Callback tiba di endpoint yang didaftarkan untuk transaksi ini.
- Respons kesalahan (error response) masih dapat menggunakan state untuk menemukan dan mengakhiri transaksi yang benar dengan aman.
Batalkan alur jika ada ketidakcocokan state daripada “mencoba permintaan token untuk melihat apakah itu berhasil.” Authorization code harus berumur pendek dan sekali pakai. Penukaran berulang harus gagal, dan server otorisasi harus mencabut token yang sudah diterbitkan dari kode tersebut jika memungkinkan.
Langkah empat: tukarkan kode dengan verifier.
Klien mengirimkan permintaan token ini:
POST /token
grant_type=authorization_code
code=returned-authorization-code
redirect_uri=registered-callback
client_id=calendar-client
code_verifier=stored-verifierServer otorisasi merekonstruksi transaksi asli: milik klien dan redirect URI mana kode tersebut, apakah sudah kedaluwarsa atau sudah digunakan, apakah permintaan otorisasi mengandung challenge, dan apakah penerapan S256 pada verifier ini sama dengan challenge yang disimpan. Jika permintaan otorisasi tidak memiliki challenge tetapi permintaan token menyajikan verifier, server tidak boleh secara diam-diam menurunkan (downgrade) transaksi ke alur non-PKCE.
Public client mengirimkan client ID untuk identifikasi, tetapi secret statis yang disertakan dalam kode publik tidak dapat mengautentikasinya. Confidential client selain itu menggunakan metode autentikasi klien yang terdaftar. PKCE melengkapi, bukan menggantikan autentikasi tersebut.
Langkah lima: jelaskan setiap kontrol melalui skenario serangan.
Misalkan klien yang sah membuat verifier V1 dan challenge C1. Penyerang mencegat kode callback tetapi tidak mengetahui V1. Verifier yang dipilih oleh penyerang tidak menghasilkan C1, sehingga pertukaran token gagal. Itulah perlindungan inti PKCE terhadap intersepsi authorization code.
Sekarang misalkan penyerang memulai transaksi otorisasi terpisah dan menyuntikkan kode tersebut ke callback korban. Transaksi korban memiliki state dan verifier yang berbeda. Korelasi state atau pengikatan PKCE akan gagal, dan klien harus membatalkannya. Server otorisasi juga harus mengingat apakah challenge ada untuk suatu kode, guna mencegah penyerang menghapus challenge dan memicu downgrade.
Jika penyerang mengubah redirect URI ke endpoint yang mereka kendalikan, pendaftaran persis dan pencocokan string persis akan menolaknya selama otorisasi. Jika klien mendukung banyak server otorisasi, klien juga harus membandingkan penerbit pada respons dengan penerbit yang disimpan dalam transaksi atau menggunakan redirect URI yang berbeda untuk setiap penerbit. Hanya mengingat URL authorization endpoint tidak cukup untuk mencegah serangan mix-up.
Langkah enam: batasi token hanya untuk tujuan sebenarnya.
Access token mewakili otorisasi dan bukan penegasan identitas pengguna (identity assertion) untuk klien. Resource server memverifikasi bahwa token memang ditujukan untuknya dan membatasinya pada resource serta tindakan yang diperlukan. Klien hanya meminta calendar.read, menggunakan access token berumur pendek, dan menjaga token agar tidak masuk ke dalam URL, log, dan penyimpanan persisten yang dapat diakses oleh skrip lain.
Tugas PKCE selesai setelah kode berubah menjadi token. Jika XSS dapat membaca memori SPA, penyimpanan transaksi, atau access token, PKCE tidak dapat memulihkan rahasia tersebut. Aplikasi browser dengan risiko lebih tinggi dapat menggunakan BFF (Backend-for-Frontend): token tetap berada di backend yang terkontrol dan browser memegang cookie sesi HttpOnly. Hal ini mengurangi paparan token terhadap JavaScript, tetapi menambah beban sesi cookie, CSRF, penskalaan backend, dan proxy API.
Jika public client menerima refresh token, server otorisasi harus mendeteksi pemutaran ulang melalui sender constraint atau rotasi refresh token. Dengan rotasi, setiap penyegaran menerbitkan refresh token baru dan membatalkan yang lama. Penggunaan kembali nilai lama mengindikasikan kemungkinan kompromi keamanan, sehingga kelompok grant tersebut dicabut dan pengguna harus mengotorisasi ulang.
Langkah tujuh: pisahkan OAuth, OIDC, dan grant lainnya.
OAuth menjawab apakah klien boleh mengakses resource atas nama pengguna. OIDC menambahkan lapisan identitas di atas OAuth sehingga klien dapat memverifikasi login melalui ID Token. Callback OIDC juga memerlukan validasi tanda tangan, penerbit, audiens, kedaluwarsa, dan nonce. Access token ditujukan untuk resource server; ID Token ditujukan untuk klien. Keduanya tidak dapat saling dipertukarkan.
Gunakan client credentials saat tidak ada pengguna yang terlibat dan suatu layanan mengakses resource di bawah kewenangannya sendiri. Perangkat dengan input terbatas dapat menggunakan alur device authorization. Implicit flow menempatkan access token dalam respons otorisasi, yang meningkatkan risiko kebocoran URL dan pemutaran ulang; praktik terbaik saat ini mendukung alur yang mengembalikan kode. Resource owner password credentials grant mengekspos kata sandi pengguna ke klien dan dilarang oleh standar keamanan dasar saat ini.
Langkah delapan: validasi kegagalan, bukan hanya skenario sukses (happy path).
| Pengujian | Hasil yang diharapkan |
|---|---|
| Ubah satu karakter verifier | Pertukaran token gagal |
| Tukarkan kode yang sama dua kali | Pertukaran kedua gagal dan memicu penanganan keamanan |
| State callback hilang, kedaluwarsa, atau milik sesi lain | Klien menolak sebelum pertukaran token |
| Otorisasi menghilangkan challenge tetapi permintaan token mengirimkan verifier | Server otorisasi menolak downgrade |
| Pengalihan hanya berbeda dalam huruf besar/kecil, path, atau trailing slash | Pencocokan persis gagal |
| Dua tab memulai alur dan callback kembali tidak berurutan | Masing-masing state mengambil verifier miliknya sendiri |
| Tukar penerbit setelah menghubungkan dua server otorisasi | Klien menolak mix-up |
| Pindai log dan analitik | Tidak ada kode, verifier, access token, atau refresh token yang muncul |
| XSS membaca penyimpanan yang dapat diakses browser | PKCE secara eksplisit dinilai tidak memadai; evaluasi penggunaan BFF |
Pemantauan produksi harus membedakan antara penolakan otorisasi, ketidakcocokan state, kegagalan PKCE, penggunaan ulang kode, ketidakcocokan pengalihan, dan pemutaran ulang refresh token. Satu penghitung "OAuth failed" global menyembunyikan sinyal serangan sekaligus cacat implementasi klien.
Contoh Jawaban Berkualitas Tinggi
“Saya akan membatasi cakupan ini sebagai public client yang mengakses API kalender pihak ketiga. Klien tidak dapat melindungi secret bersama, sehingga secret yang disematkan dalam bundle SPA bukanlah autentikasi klien.
Pada awal setiap otorisasi, saya menghasilkan state yang tidak dapat ditebak dan code verifier dengan setidaknya 256 bit entropi, menurunkan S256 challenge, dan menyimpan verifier, sesi browser, penerbit, redirect URI, serta waktu kedaluwarsa di bawah state tersebut. Permintaan otorisasi membawa response type code, client ID, redirect URI yang terdaftar persis, scope read-only, state, dan challenge. Pengguna hanya mengautentikasi dan memberikan persetujuan di server otorisasi.
Ketika callback mengembalikan kode dan state, saya pertama-tama membuktikan bahwa state tersebut milik sesi saat ini dan belum digunakan, lalu memuat verifier-nya. Permintaan token mengirimkan kode, redirect URI yang sama, client ID, dan verifier. Server otorisasi memverifikasi bahwa kodenya berumur pendek, belum digunakan, diterbitkan untuk klien dan redirect URI ini, serta nilai S256 verifier sama dengan challenge sebelum menerbitkan access token. Kode yang dicegat tidak berguna tanpa verifier. Server juga menolak permintaan yang mencoba menghapus PKCE dan menurunkan alur pertukaran.
State mengorelasikan permintaan browser, sementara PKCE mengikat pertukaran kode; saya tidak akan menggabungkannya menjadi satu parameter CSRF yang samar. Aplikasi web confidential juga mengautentikasi di token endpoint dan harus tetap menggunakan PKCE. Access token hanya untuk resource server kalender. Jika produk menggunakan alur ini untuk login, produk membutuhkan OIDC dan harus memvalidasi tanda tangan, penerbit, audiens, masa berlaku, dan nonce ID Token.
Saya akan menguji verifier yang salah, penggunaan ulang kode, state lintas sesi, ketidakcocokan redirect persis, tab yang tidak berurutan, downgrade PKCE, dan mix-up penerbit, serta memverifikasi bahwa log tidak berisi kode atau token. PKCE tidak dapat menghentikan XSS same-origin untuk mencuri verifier atau token. Untuk klien browser berisiko tinggi, saya akan mengevaluasi BFF yang menyimpan token di sisi server dan hanya memberikan cookie sesi HttpOnly kepada browser.”
Kesalahan Umum
- Memperlakukan client ID sebagai rahasia → client ID muncul dalam permintaan otorisasi dan hanya
mengidentifikasi klien → gunakan PKCE untuk public client dan autentikasi klien nyata untuk confidential client.
- Hanya mengatakan bahwa “kode lebih aman daripada token” → ini tidak menjelaskan mengapa kode yang
dicegat tidak dapat ditukarkan → tunjukkan pengikatan challenge-verifier dan perbandingan pada token-endpoint.
- Menggunakan
plainatau membiarkan PKCE diabaikan → verifier dapat terekspos atau penyerang
dapat memicu downgrade → gunakan S256 dan wajibkan server otorisasi untuk mencatat serta menegakkan PKCE.
- Menghasilkan state tanpa mengikatnya ke suatu sesi → nilai yang valid dapat dipindahkan dan alur
yang berjalan bersamaan saling menimpa → simpan pemetaan state sekali pakai, sesi, penerbit, dan verifier.
- Mengizinkan pencocokan redirect yang samar (fuzzy) → kode dapat mencapai subpath atau redirector
yang dikendalikan penyerang → daftarkan terlebih dahulu dan bandingkan redirect URI secara persis.
- Mendekode access token dan memperlakukannya sebagai identitas login → audiens dan semantiknya
mungkin milik resource server → gunakan login OIDC dan validasi ID Token.
- Mengklaim bahwa PKCE menghentikan XSS → XSS dapat membaca verifier, kode, atau token yang diterbitkan
secara langsung → kurangi paparan token pada browser, perbaiki XSS, dan evaluasi penggunaan BFF sesuai tingkat risiko.
- Mencatat kode, verifier, atau token ke dalam log → diagnostik memperluas paparan rahasia yang dapat
ditukarkan → catat ID transaksi, kelas kesalahan, dan nilai korelasi yang tidak dapat ditukarkan.
Pertanyaan Lanjutan dan Tanggapannya
Pertanyaan Lanjutan 1: Apakah aplikasi web tradisional dengan backend masih membutuhkan PKCE?
Ya. Autentikasi klien di token endpoint membuktikan bahwa confidential client memegang kredensial yang dilindungi. PKCE mengikat permintaan otorisasi ini ke pertukaran kode ini dan memberikan perlindungan lebih lanjut terhadap injeksi kode serta penyalahgunaan. Kontrol tersebut mengikat relasi yang berbeda. Sesi sisi server dapat menyimpan verifier dan state sementara browser hanya membawa pengenal sesi yang tidak dapat ditebak.
Pertanyaan Lanjutan 2: Apa yang berubah jika “hubungkan kalender” juga berarti “masuk dengan kalender”?
Gunakan OIDC alih-alih memperlakukan access token atau respons UserInfo arbitrer sebagai bukti login. Minta scope openid dan nonce. Validasi tanda tangan, penerbit, audiens, masa berlaku, dan nonce ID Token, lalu gunakan pasangan penerbit-subjek (issuer-subject) sebagai kunci identitas eksternal. State tetap mengikat transaksi browser; nonce mengikat permintaan autentikasi ke ID Token.
Pertanyaan Lanjutan 3: Risiko baru apa yang muncul ketika setiap tenant SaaS mengonfigurasi penerbit yang berbeda?
Klien dapat mengirimkan respons dari server otorisasi A ke token endpoint server otorisasi B, menyebabkan serangan mix-up. Simpan penerbit yang diharapkan di dalam transaksi dan validasi penerbit pada respons. Pertahanan lainnya adalah menggunakan redirect URI yang berbeda per penerbit dan memverifikasi bahwa respons tiba di endpoint yang benar. Authorization endpoint dan token endpoint harus berasal dari metadata penerbit tepercaya yang sama.
Pertanyaan Lanjutan 4: Bagaimana Anda melindungi refresh token untuk sinkronisasi kalender yang berjalan lama?
Berikan hanya scope yang diperlukan dan gunakan rotasi refresh token atau sender constraint. Dengan rotasi, lacak kelompok token: setiap penyegaran membatalkan nilai lama, dan penggunaan kembali refresh token lama akan mencabut seluruh kelompok yang aktif serta mewajibkan otorisasi ulang. Klien juga memerlukan penyimpanan yang aman, jalur pencabutan, masa pakai maksimum, dan kedaluwarsa akibat ketidakaktifan. PKCE melindungi pertukaran kode awal, bukan refresh token yang dicuri di kemudian hari.
Pertanyaan Lanjutan 5: Mengapa dua tab yang menghubungkan akun kalender berbeda kadang-kadang gagal?
Penyebab umumnya adalah satu state atau verifier global: tab kedua menimpa tab pertama. Callback harus mencari transaksi independen berdasarkan state. Catatan tersebut berisi verifier, akun atau penerbit yang diharapkan, redirect URI, dan waktu pembuatan. Gunakan catatan tersebut secara atomik. Tolak state yang tidak dikenal, duplikat, dan kedaluwarsa alih-alih beralih ke verifier terbaru.