# Refresh Token ## Tujuan Memperbarui JWT yang akan habis masa berlakunya tanpa harus login ulang dengan username/password LDAP. Digunakan oleh FE PWA untuk menjaga sesi tetap aktif. ## Business Rule - Token lama harus **masih valid** (belum expired) untuk bisa di-refresh - Token baru diterbitkan dengan TTL penuh (`expires_in` dari awal lagi) - Token lama tidak di-invalidate (stateless JWT — tidak ada blacklist) - Groups dan is_admin diambil dari payload token lama, bukan re-query ke LDAP - Rate limit: 30 request / menit per IP ## Kapan Harus Refresh? Strategi yang direkomendasikan untuk PWA: ``` Token diterima saat login → expires_in: 3600 (1 jam) ↓ Simpan: access_token, expires_at = Date.now() + (expires_in * 1000) ↓ Sebelum setiap request API: if (Date.now() > expires_at - 5 * 60 * 1000) // 5 menit sebelum expire → POST /auth/refresh → Simpan token baru ↓ Jika refresh gagal (401) → redirect ke halaman login ``` ## URL `POST https://console.gisportal.id/api/v1/auth/refresh` ## HTTP Method `POST` ## Authentication Token lama (yang akan di-refresh) dikirim di header: ``` Authorization: Bearer ``` ## Request Body Tidak diperlukan (kosong). ## Response Success **HTTP 200** ```json { "data": { "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJqb2huLmRvZSIsImlzX2FkbWluIjp0cnVlLCJleHAiOjE3MTk1MDM2MDB9.new_signature", "token_type": "Bearer", "expires_in": 3600, "auth_type": "ldap_jwt" } } ``` ## Response Error **HTTP 401 — Token tidak ada:** ```json { "error": { "code": "missing_token", "message": "No token provided." } } ``` **HTTP 401 — Token expired atau invalid:** ```json { "error": { "code": "invalid_token", "message": "Token invalid or expired." } } ``` ## Status Code | Code | Kondisi | |------|---------| | 200 | Token berhasil di-refresh | | 401 | Token tidak ada / expired / invalid | | 429 | Rate limit (30x/menit) | ## Contoh Request ```bash curl -s -X POST https://console.gisportal.id/api/v1/auth/refresh \ -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..." ``` ## Contoh Implementasi PWA (JavaScript) ```javascript const TOKEN_KEY = 'geonet_token'; const EXPIRES_KEY = 'geonet_expires_at'; async function getValidToken() { const expiresAt = parseInt(localStorage.getItem(EXPIRES_KEY) || '0'); const token = localStorage.getItem(TOKEN_KEY); // Refresh jika kurang dari 5 menit lagi expire if (Date.now() > expiresAt - 5 * 60 * 1000) { const res = await fetch('https://console.gisportal.id/api/v1/auth/refresh', { method: 'POST', headers: { 'Authorization': `Bearer ${token}` } }); if (!res.ok) { // Token expired total → login ulang window.location.href = '/login'; return null; } const data = await res.json(); localStorage.setItem(TOKEN_KEY, data.data.access_token); localStorage.setItem(EXPIRES_KEY, Date.now() + data.data.expires_in * 1000); return data.data.access_token; } return token; } // Gunakan sebelum setiap API call const token = await getValidToken(); const res = await fetch('https://console.gisportal.id/api/v1/databases', { headers: { 'Authorization': `Bearer ${token}` } }); ``` ## Sequence Flow ```mermaid sequenceDiagram PWA->>API: POST /auth/refresh (Bearer token_lama) API->>API: Validasi JWT signature & exp alt Token masih valid API->>API: Issue JWT baru (TTL penuh) API->>AuditLog: log api.v1.auth.refresh API-->>PWA: 200 {access_token baru, expires_in: 3600} else Token expired/invalid API-->>PWA: 401 invalid_token PWA->>PWA: Redirect ke /login end ``` ## Database yang Diakses - Tidak ada (JWT stateless) - `databaselist_audit` — write audit log ## Audit Log Event: `api.v1.auth.refresh` ## Rate Limit 30 request / menit per IP. ## Idempotent Tidak — setiap call menghasilkan token baru dengan exp baru. ## Security - Token lama tidak di-blacklist (stateless JWT) - Jika token bocor, revoke dengan cara: admin bisa rotate `APP_KEY` (invalidate semua JWT) - Selalu gunakan HTTPS ## Changelog | Tanggal | Perubahan | |---------|-----------| | 2026-06-23 | Endpoint dibuat untuk mendukung FE PWA my.gisportal.id |