Memecahkan kebutuhan refresh token otomatis antar zona

Ketika layanan pengguna berada di zona berbeda, token refresh perlu otomatis dan konsisten agar sesi tetap hidup tanpa menghasilkan token ganda atau melanggar kebijakan retry. Artikel ini langsung membahas skenario integrasi tempat layanan backend memanggil auth server pihak ketiga untuk refresh token, mengelola retry dengan perjanjian kontrak API yang jelas, serta menjaga konsistensi response di seluruh zona.

Fokus utamanya: menulis kontrak yang memberitahu klien kapan boleh retry, menghindari double refresh, serta menggabungkan cache/locking, TTL, scope, dan observability—semua tanpa memecah keamanan autentikasi.

Skenario integrasi dan tantangan lintas zona

Bayangkan API gateway di tiap zona menghubungi penyedia otentikasi untuk refresh token pengguna. Jika gateway di zona A gagal karena timeout, gateway di zona B bisa mencoba lagi. Tanpa kontrak yang kuat, dua permintaan refresh bisa menghasilkan dua token aktif, atau klien tidak tahu kapan retry aman.

Masalah paling umum:

  • Retry tak terbatas: Tanpa status khusus, klien bisa retry terus menerus padahal server sedang proses capture token.
  • Token ganda: Multi zona memicu refresh bersamaan dan menghasilkan dua token valid.
  • Ketidakpastian cache: Zona lain baca cache lama dan menganggap token sudah diganti padahal belum.

Kontrak API: header, status, dan body

Kontrak harus eksplisit. Misalnya:

POST /auth/token/refresh

Headers wajib:

  • X-Request-ID: trace unik per permintaan agar logging dan deduplikasi mudah.
  • X-Cache-Status: miss/hit memberi konteks apakah token sudah di-cache.
  • X-Retry-After: jika permintaan ditolak temporer, header ini beri tahu kapan boleh retry.

Status kandidat:

  • 200: berhasil, body berisi access_token, expires_in, refresh_token baru.
  • 202 Accepted: sedang proses refresh primer, client harus menunggu sesuai X-Retry-After.
  • 409 Conflict: refresh sudah berjalan; client harus menunggu TTL lama cache.
  • 429 Too Many Requests: rate limit pihak ketiga aktif, retry setelah X-Retry-After.

Contoh body respons sukses:

{
  "access_token": "eyJhbGci...",
  "expires_in": 300,
  "scope": "api:read api:write",
  "token_type": "Bearer"
}

Kontrak juga harus menyebut field scope untuk memastikan klien tidak menganggap token bisa untuk hal lain.

Strategi retry dan idempotensi

Kunci retry aman adalah idempotensi: hanya satu refresh boleh dieksekusi per X-Request-ID dan user_id. Gunakan token deduplikasi berbasis cache distribusi (Redis atau memcached) dengan TTL sedikit lebih lama dari durasi refresh (misal 10 detik). Jika permintaan masuk dan cache sudah ada kunci, API mengembalikan 202 dengan X-Retry-After.

Dto dikelola sebagai berikut:

  1. Cek cache refresh-lock:{user_id}.
  2. Jika tidak ada, set nilai unique request ID dengan TTL 10 detik dan lanjut refresh.
  3. Jika ada dan request ID berbeda, respons 409 atau 202 dengan TTL lock sebagai indikasi.

Dengan pattern ini, retry klien yang menangani timeout akan mendapatkan informasi kapan boleh mencoba lagi, sementara request lain tidak memicu refresh kedua.

Cache, locking, dan TTL lintas zona

Cache harus konsisten antar zona. Pertimbangkan cache global (Redis cluster multi-region) atau menggunakan layer cache yang memperbarui token secara push/pull ke zona lain.

Hal yang perlu diterapkan:

  • Cache sesi token: setelah refresh berhasil, simpan token baru dan metadata (scope, expiry) di cache terdistribusi, dengan TTL set sesuai expires_in.
  • Locking distribusi: TTL lock harus cukup besar untuk menutup jarak waktu antar zona, namun tidak terlalu lama agar tidak memblokir refresh berikutnya.
  • Cache invalidation: Jika terjadi rollback token, gunakan mekanisme invalidation eksplisit dan broadcast update ke zona lain.

Cache TTL harus di-synchronize dengan expires_in agar zona lain membaca token paling baru. Jika cache TTL lebih panjang, ada risiko zona lain menganggap token masih valid.

Observability dan troubleshooting

Observability penting untuk mendeteksi kontrak broken. Minimal:

  • Tracing: Gunakan X-Request-ID untuk menelusuri permintaan refresh di seluruh zona.
  • Metrics: Hitung jumlah 202/409/429 untuk melihat apakah retry terlalu agresif atau rate limit dipicu.
  • Log detail: Sertakan info cache lock, ID request, dan zona asal.

Debugging tip: jika terlalu banyak 202, artinya lock terlalu pendek atau klien tidak menunggu X-Retry-After. Jika terlalu banyak 429, evaluasi throttle rate limit dan pattern retry eksponensial.

Strategi rate limit pihak ketiga

Pihak ketiga umumnya punya quota. Terapkan strategi:

  • Exponential backoff: Gabungkan X-Retry-After dengan peningkatan interval sampai batas maksimum.
  • Token bucket lokal: Di sisi layanan Anda, batasi permintaan refresh per user agar tidak meneruskan flood ke provider.
  • Fallback: Jika provider mengembalikan 429, kirimkan response 429 ke klien dengan info retry dan gunakan cache lock agar zona lain tidak dup refresh.

Jangan langsung meneruskan 429 tanpa memperbarui cache lock; hal itu bisa melepas refresh serentak.

Checklist validasi implementasi

Untuk memastikan tim developer dan ops menerapkan pola dengan benar, ikuti checklist ini:

  1. Header konsisten: Pastikan X-Request-ID, X-Cache-Status, dan X-Retry-After tersedia.
  2. Status handling: Cek client memahami 202, 409, dan 429 sebagai indikasi retry atau blocking.
  3. Lock TTL: Sesuaikan TTL lock dengan jarak multi zona dan durasi timeout.
  4. Scope & expiry: Lemparkan scope & expiry di body; klien harus memvalidasi scope baru.
  5. Observability: Setup tracing, metrics, dan alert untuk nilai status non-200 serta cache hit rate.
  6. Security: Pastikan refresh token tidak bocor di log, hanya ID/metadata yang tercatat.

Penutup

Kontrak API token refresh yang kokoh membantu menghindari retry berlebihan, token ganda, dan inkonsistensi lintas zona. Dengan definisi header/status/body, cache locking terdistribusi, dan observability, tim dapat memperluas sistem tanpa mengorbankan keamanan atau kestabilan. Gunakan checklist validasi untuk memastikan semua pihak tetap sinkron saat implementasi pola ini.