Tidak suka iklan? Pergi Bebas Iklan Hari ini

Alur OAuth 2.0 Kode Otorisasi, PKCE, dan Kredensial Klien

Diperbarui pada

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.

Alur OAuth 2.0: Kode Otorisasi, PKCE, dan Kredensial Klien 1
IKLAN · HAPUS?

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.

AlurKapan digunakanMelibatkan pengguna?Membutuhkan client_secret?
Kode OtorisasiAplikasi web dengan server belakangYaYa — tetap berada di server
Kode Otorisasi + PKCEAplikasi SPA, aplikasi mobile, atau klien publik apa punYaTIDAK
Kredensial KlienAntara server ke server (tidak melibatkan pengguna)TIDAKYa — 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.

Ingin bebas iklan? Bebas Iklan Hari Ini

Instal Ekstensi Kami

Tambahkan alat IO ke browser favorit Anda untuk akses instan dan pencarian lebih cepat

Ke Ekstensi Chrome Ke Ekstensi Tepi Ke Ekstensi Firefox Ke Ekstensi Opera

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!

IKLAN · HAPUS?
IKLAN · HAPUS?
IKLAN · HAPUS?

Pojok Berita dengan Sorotan Teknologi

Terlibat

Bantu kami untuk terus menyediakan alat gratis yang berharga

Belikan aku kopi
IKLAN · HAPUS?