Alur OAuth 2.0 Kode Otorisasi, PKCE, dan Kredensial Klien
Panduan pengembang dalam memilih alur OAuth 2.0 yang tepat. Menyertakan alur otorisasi kode (aplikasi web), PKCE (aplikasi berbasis halaman dan mobile), dan klien kredensial (antara server ke server) dengan contoh kode yang berfungsi dan kesalahan yang menyerang Anda nanti.
Tiga alur mencakup 95% kasus penggunaan nyata OAuth 2.0. Spesifikasi mendefinisikan lebih banyak, tetapi sisanya didekati, kasus tepat, atau keduanya. Pilih yang tepat sejak awal, dan Anda akan menghindari perbaikan yang menyakitkan saat kebutuhan otorisasi berubah.
| Alur | Kapan digunakan | Melibatkan pengguna? | Membutuhkan client_secret? |
|---|---|---|---|
| Kode Otorisasi | Aplikasi web dengan server belakang | Ya | Ya — tetap berada di server |
| Kode Otorisasi + PKCE | Aplikasi SPA, aplikasi mobile, atau klien publik apa pun | Ya | TIDAK |
| Kredensial Klien | Antara server ke server (tidak melibatkan pengguna) | TIDAK | Ya — tetap berada di server |
Alur Kode Otorisasi
Alur standar untuk aplikasi web dengan server belakang. Token akses dan klien secret tidak pernah menyentuh browser — pertukaran token terjadi di sisi server. Itulah intinya.
Langkah 1: Arahkan pengguna
Buat URL otorisasi dan arahkan browser ke sana:
GET https://accounts.example.com/oauth/authorize
?response_type=code
&client_id=YOUR_CLIENT_ID
&redirect_uri=https://yourapp.com/callback
&scope=openid+profile+email
&state=RANDOM_CSRF_TOKEN
Itu state nilai ini adalah pertahanan CSRF untuk arahan. Buat string acak baru per alur, simpan di sesi, dan verifikasi saat pengguna kembali. Jika Anda melewatkan pengecekan ini, penyerang dapat mengarahkan pengguna melalui alur menggunakan kode otorisasi milik mereka sendiri — secara diam-diam menghubungkan akun pengguna dengan identitas penyerang.
Langkah 2: Tangan balik callback
Server otorisasi mengarahkan kembali ke Anda redirect_uri dengan kode sementara:
GET https://yourapp.com/callback?code=AUTH_CODE&state=SAME_STATE_YOU_SENT
Verifikasi state sesuai dengan yang Anda simpan. Kemudian tukar kode tersebut di sisi server.
Langkah 3: Tukar kode menjadi token (hanya di sisi server)
POST https://accounts.example.com/oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=AUTH_CODE
&redirect_uri=https://yourapp.com/callback
&client_id=YOUR_CLIENT_ID
&client_secret=YOUR_CLIENT_SECRET
Respons:
{
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "def50200a12b3c...",
"scope": "openid profile email"
}
Simpan kedua token di sisi server. Saat access_token habis (cek expires_in), gunakan refresh_token untuk mendapatkan yang baru tanpa mengirim pengguna melalui proses login lagi.
PKCE — Untuk Klien yang Tidak Bisa Menyimpan Rahasia
Aplikasi SPA atau mobile tidak memiliki tempat aman untuk client_secret. Siapa saja bisa membuka DevTools dan menemukannya di bundle JS Anda. Siapa saja bisa mendekompilasi APK Anda. PKCE (Proof Key for Code Exchange, dibaca "pixy") memecahkan ini dengan tantangan kriptografi satu kali — tidak perlu rahasia yang dibagikan.
Alur ini identik dengan Kode Otorisasi dengan dua tambahan: sebuah code_verifier (string acak yang Anda buat) dan sebuah code_challenge (hash SHA-256 dari verifikator, dienkripsi base64url). Anda kirim tantangan secara awal, lalu buktikan bahwa Anda memiliki verifikator saat pertukaran token.
Langkah 1: Buat verifikator dan tantangan
function generateCodeVerifier() {
const array = new Uint8Array(32);
crypto.getRandomValues(array);
return base64url(array);
}
async function generateCodeChallenge(verifier) {
const data = new TextEncoder().encode(verifier);
const digest = await crypto.subtle.digest('SHA-256', data);
return base64url(new Uint8Array(digest));
}
function base64url(buffer) {
return btoa(String.fromCharCode(...buffer))
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '');
}
// Usage
const codeVerifier = generateCodeVerifier();
const codeChallenge = await generateCodeChallenge(codeVerifier);
// Store codeVerifier in memory — NOT localStorage
Simpan code_verifier di memori — variabel skop modul, bukan localStorage. Anda akan kirimnya saat pertukaran token.
Langkah 2: Permintaan otorisasi — tambahkan tantangan
GET https://accounts.example.com/oauth/authorize
?response_type=code
&client_id=YOUR_CLIENT_ID
&redirect_uri=https://yourapp.com/callback
&scope=openid+profile
&state=RANDOM_CSRF_TOKEN
&code_challenge=BASE64URL_SHA256_OF_VERIFIER
&code_challenge_method=S256
Langkah 3: Pertukaran token — verifikator alih-alih client_secret
POST https://accounts.example.com/oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=AUTH_CODE
&redirect_uri=https://yourapp.com/callback
&client_id=YOUR_CLIENT_ID
&code_verifier=ORIGINAL_VERIFIER_YOU_GENERATED
Server otorisasi menghash verifikator dan memeriksa nilai tersebut terhadap tantangan yang Anda kirimkan pada langkah 2. Penyerang yang mengintersepsi kode otorisasi tidak tahu apa nilai verifikator — mereka tidak bisa menukar nilai tersebut.
Perlu diketahui: OAuth 2.1 (modernisasi berjalan dari 2.0) memaksa PKCE untuk semua alur yang melibatkan arahan. Jika Anda menulis kode baru, gunakan PKCE terlepas dari apakah penyedia Anda saat ini membutuhkannya.
Kredensial Klien — Tidak Ada Pengguna, Tidak Ada Masalah
Tugas latar belakang, layanan mikro yang memanggil layanan mikro lainnya, tugas cron yang mengakses API — tidak satupun dari ini melibatkan pengguna. Kredensial Klien adalah alur yang tepat: layanan mengautentikasi secara langsung menggunakan ID dan rahasia klien sendiri.
POST https://accounts.example.com/oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
&client_id=YOUR_CLIENT_ID
&client_secret=YOUR_CLIENT_SECRET
&scope=api:read api:write
Respons:
{
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 86400
}
Tidak ada token refresh — ketika habis, minta yang baru. Kesalahan umum: mengirim permintaan ke endpoint token pada setiap panggilan API. Simpan token, periksa kedaluwarsa sebelum setiap permintaan, hanya ambil ulang saat akan habis. Satu permintaan token per hari (atau per jam, tergantung pada expires_in) alih-alih satu per setiap permintaan:
let cachedToken = null;
let tokenExpiresAt = 0;
async function getAccessToken() {
// Refresh 30 seconds before actual expiry
if (cachedToken && Date.now() < tokenExpiresAt - 30_000) {
return cachedToken;
}
const res = await fetch('https://accounts.example.com/oauth/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'client_credentials',
client_id: process.env.CLIENT_ID,
client_secret: process.env.CLIENT_SECRET,
scope: 'api:read api:write',
}),
});
const data = await res.json();
cachedToken = data.access_token;
tokenExpiresAt = Date.now() + (data.expires_in * 1000);
return cachedToken;
}
Kesalahan yang Dibuat Pengembang
Menyimpan token di localStorage
Setiap kelemahan XSS — dalam kode Anda sendiri, ketergantungan, atau skrip manajer tag — dapat membaca semua yang ada di localStorage. Untuk SPA: simpan token akses di memori (variabel skop modul yang hilang saat refresh). Gunakan cookie httpOnly untuk token refresh ketika Anda memiliki backend yang dapat mengatur cookie tersebut. JavaScript tidak bisa membaca cookie httpOnly.
Menggunakan alur implisit
Alur implisit mengembalikan token langsung di bagian URL (#access_token=...). Token tersebut akhirnya muncul di sejarah browser, log akses server, dan header Referer. Ini didekati dalam RFC 9700. Tidak ada alasan untuk menggunakan alur implisit untuk kode baru. Gunakan PKCE.
Melewatkan validasi state
Linter dan Formatter YAML GitHub Actions state validasi pada callback, penyerang dapat membuat URL arahan yang menyelesaikan alur OAuth menggunakan kode otorisasi mereka sendiri. Hasilnya: akun pengguna Anda terhubung ke identitas penyerang di penyedia. Buatnya baru per alur, simpan di sesi, dan periksa saat callback.
Menyimpan client_secret di kode frontend
Tidak ada hal seperti rahasia yang tinggal di browser. Minifikasi tidak menyembunyikannya. Obfuscasi tidak melindunginya. Jika runtime Anda adalah browser atau aplikasi mobile, Anda memiliki klien publik — gunakan PKCE dan omong-omongkan client_secret secara keseluruhan. Itu bukan solusi; itu cara yang dimaksudkan oleh spesifikasi untuk klien publik.
Tidak menangani kedaluwarsa token secara proaktif
Setiap token akses memiliki nilai expires_in . Jika Anda menyimpan token hingga gagal dengan kode 401 dan kemudian mengulang otorisasi, pengguna mengalami kesalahan yang tidak jelas. Periksa kedaluwarsa sebelum membuat permintaan, segarkan secara proaktif (30 detik sebelum kedaluwarsa adalah buffer yang wajar), dan tangani kasus langka ketika token refresh sendiri telah habis.
Menginspeksi Token Saat Anda Bekerja
Sebagian besar penyedia OAuth mengeluarkan JWT sebagai token akses. Isi payload dienkripsi base64url dan dapat dibaca tanpa kunci pribadi — hanya tanda tangan yang membutuhkan kunci untuk memverifikasi. Saat Anda memperbaiki alur dan ingin melihat klaim, lingkup, atau kedaluwarsa dalam token, tempelkan ke dalam Dekoder JWT.
Jika Anda menguji secara manual PKCE dan perlu memverifikasi apa yang di-decode oleh base64url-encoded code_challenge , maka Konverter Base64 mengelola versi standar dan versi aman URL.
Versi Singkat
Pertanyaan satu menentukan alur yang tepat: apakah runtime Anda memiliki tempat aman untuk menyimpan rahasia?
- Server belakang → Kode Otorisasi atau Kredensial Klien (rahasia tetap di server)
- Browser atau aplikasi mobile → Kode Otorisasi + PKCE (tidak ada rahasia sama sekali)
- Tidak melibatkan pengguna → Kredensial Klien
PKCE bekerja untuk semua alur yang melibatkan arahan — tidak ada kekurangan dalam menggunakan PKCE bahkan ketika penyedia Anda belum membutuhkannya. OAuth 2.1 akan melakukannya.
Instal Ekstensi Kami
Tambahkan alat IO ke browser favorit Anda untuk akses instan dan pencarian lebih cepat
恵 Papan Skor Telah Tiba!
Papan Skor adalah cara yang menyenangkan untuk melacak permainan Anda, semua data disimpan di browser Anda. Lebih banyak fitur akan segera hadir!
Alat Wajib Coba
Lihat semua Pendatang baru
Lihat semuaMemperbarui: Kita alat terbaru ditambahkan pada 12 Jun 2026
