SvelteKit refresh token harus memberikan jawaban langsung: bagaimana endpoint menjaga keamanan, validasi, dan retry-safe bagi klien yang kehabisan akses. Artikel ini menjelaskan flow otorisasi, cara membangun kontrak payload yang valid, serta mekanisme retry dengan rate limit dan observabilitas.
Memahami Flow Otorisasi dan Kontrak Refresh Token
Klien stale membutuhkan endpoint refresh token yang menerapkan kontrak eksplisit. Awalnya klien memegang refresh token yang dihasilkan saat login dan menyimpannya di tempat aman seperti httpOnly cookie atau penyimpanan terenkripsi. Ketika access token kadaluarsa, klien memanggil endpoint /api/auth/refresh dengan payload minimal: { refreshToken } dan clientId (opsional jika multi-tenant).
Server harus memastikan refresh token masih valid di sisi storage (misalnya database atau Redis) dan terkait dengan client/usuario yang sesuai. Setelah verifikasi, server mengeluarkan access token baru dan, jika perlu, refresh token baru. Kontrak ini harus menyatakan:
- Permintaan harus tersimpan dalam sistem audit.
- Response menyertakan flag
retryEligibleketika perpanjangan gagal karena masalah sementara. - Server menolak permintaan dengan payload hilang atau tidak konsisten tanpa mengubah state.
Validasi Payload dan Idempotensi
Contoh validasi minimal: refresh token harus berada pada whitelist, belum digunakan, dan belum kadaluarsa. Jangan mengandalkan hanya JWT karena refresh token sering dibuat acak dan disimpan di backend. Pastikan juga request memuat X-Request-ID untuk menghindari eksekusi ganda dan memudahkan idempotensi.
Idempotensi penting saat klien melakukan retry otomatis. Implementasi umum:
- Gunakan
X-Request-IDuntuk menyimpan status permintaan di cache sementara. - Jika permintaan sebelumnya sukses, kembalikan token yang sama atau flag success tanpa memproses ulang.
- Jika status pending dan permintaan sebelumnya belum selesai, batasi retry di sisi klien dengan backoff.
Implementasi Endpoint Refresh Token
Berikut contoh +server.ts SvelteKit yang menangani refresh token, validasi, dan logging.
import { json } from '@sveltejs/kit';
import { z } from 'zod';
import { getRefreshRecord, rotateRefreshToken, recordRetry } from '$lib/auth';
const bodySchema = z.object({
refreshToken: z.string().min(64),
clientId: z.string().optional()
});
export const POST = async ({ request, locals }) => {
const requestId = request.headers.get('x-request-id') ?? crypto.randomUUID();
const payload = bodySchema.safeParse(await request.json());
if (!payload.success) {
return json({ error: 'Invalid payload' }, { status: 400 });
}
const { refreshToken, clientId } = payload.data;
const record = await getRefreshRecord(refreshToken);
if (!record || record.revoked || record.clientId !== clientId) {
return json({ error: 'Invalid token' }, { status: 401 });
}
if (record.requestId && record.requestId !== requestId) {
const previous = await recordRetry(record.requestId);
if (previous?.status === 'success') {
return json(previous.response);
}
}
const { accessToken, newRefreshToken } = await rotateRefreshToken(record, requestId);
return json({ accessToken, refreshToken: newRefreshToken, retryEligible: false });
};
Komponen penting:
- Validasi Schema: Gunakan
zodatau validator lainnya agar payload konsisten. - Idempotensi: Menyimpan
requestIddi record refresh token mencegah double rotation. - Rotasi Token: Menghasilkan refresh token baru dan menandai yang lama sebagai dipakai.
Middleware Retry-Safe dan Error Handling
Middleware atau handle bisa mengemulasikan logika retry-safe dengan memeriksa cache rate limit dan menambahkan informasi retry. Contoh ringkas:
import type { Handle } from '@sveltejs/kit';
import { rateLimit } from '$lib/rate-limit';
export const handle: Handle = async ({ event, resolve }) => {
if (event.url.pathname === '/api/auth/refresh') {
const key = `refresh:${event.getClientAddress()}`;
const allowed = await rateLimit(key, { max: 5, window: 60 });
if (!allowed) {
return new Response(JSON.stringify({ error: 'Rate limit exceeded' }), { status: 429 });
}
}
return resolve(event);
};
Retry-Safe Error Handling: Pastikan respons error eksplisit menerangkan apakah klien boleh retry. Misalnya, 503 dengan body { retryEligible: true } untuk gangguan sementara, sementara error validasi tetap 400.
Dokumentasi Konsumsi Klien
Dokumentasikan kontrak agar tim frontend dan klien eksternal mengikuti standar:
- Permintaan POST ke
/api/auth/refreshharus berisi headerx-request-idunik dan payload JSON. - Kalau respon berisi
retryEligible: true, klien boleh retry sekali lagi dengan backoff linear sebelum menyerah. - Simulasikan kegagalan 401/400 tidak boleh di-retry karena berarti token invalid.
- Gunakan header
Retry-Afterjika rate limit terkena, agar klien menunggu waktu yang disarankan.
Termasuk contoh cURL bantu developer melakukan debugging:
curl -X POST https://example.com/api/auth/refresh \
-H "Content-Type: application/json" \
-H "x-request-id: 123e4567-e89b-12d3-a456-426614174000" \
-d '{"refreshToken":"...","clientId":"web-app"}'
Observabilitas, Logging, dan Metrics
Untuk monitoring, catat metrik berikut:
- Refresh Attempts: total permintaan, sukses vs gagal.
- Latency: waktu rotasi token, untuk mendeteksi bottleneck database.
- Rate Limit Hits: menganalisis apakah terlalu banyak percobaan.
Logging harus menyertakan requestId, clientId, dan status akhir (terima, tolak, error). Gunakan trace ID dari distributed tracing jika tersedia agar panggilan bisa dilacak end-to-end.
Kesimpulan
Kontrak API refresh token di SvelteKit terdiri dari validasi payload, idempotensi, rate limiting, serta observabilitas untuk menjaga klien stale tetap aman. Implementasi endpoint yang konsisten, middleware retry-safe, dan dokumentasi konsumsi membuat sistem lebih bisa diandalkan dan mudah dipelihara.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!