# Create Service Token ## Tujuan Membuat Bearer token baru untuk microservice. Token plain text ditampilkan **sekali** — harus disimpan saat itu juga. ## Business Rule - Token dihasilkan secara acak (cryptographically secure random) - 16 karakter pertama disimpan sebagai `token_prefix` untuk identifikasi UI - SHA256 hash dari token lengkap disimpan di database (plain text tidak pernah disimpan) - Token otomatis disinkronkan ke `geonetagent_dev.agent_tokens` jika `GEONETAGENT_DB_USERNAME` di-set - Setelah endpoint ini mengembalikan response, token plain text tidak bisa diambil lagi - Jika token hilang → gunakan endpoint Regenerate ## URL `POST https://console.gisportal.id/api/v1/service-tokens` ## HTTP Method `POST` ## Authentication ``` Authorization: Bearer ``` ## Request Header ``` Content-Type: application/json Authorization: Bearer ``` ## Request Body ```json { "name": "GeoNetAgent-Production", "service": "GeoNetAgent", "expires_in_days": null } ``` ## Validation | Field | Aturan | |-------|--------| | `name` | required, string, max 100 karakter | | `service` | required, string, max 50 karakter | | `expires_in_days` | nullable, integer, min 1 | ## Response Success **HTTP 201** ```json { "data": { "id": "065eb796-ecb5-4d95-b4cd-9ca6244cf13f", "name": "GeoNetAgent-Production", "service": "GeoNetAgent", "token_prefix": "Col5FUFl34dVXgyB", "token": "Col5FUFl34dVXgyBgTSA23KsYc5QhAh8s224OkR5GjQ", "is_active": true, "expires_at": null, "created_by": "admin", "created_at": "2026-06-23T08:54:04+07:00" }, "message": "Service token created successfully. Save the token securely as it will not be shown again." } ``` > **PENTING:** Field `token` hanya ada di response ini. Setelah request ini selesai, token tidak bisa diambil lagi. ## Response Error **HTTP 422 — Validasi gagal:** ```json { "message": "The given data was invalid.", "errors": { "name": ["The name field is required."], "service": ["The service field is required."] } } ``` **HTTP 401 — Tidak terautentikasi:** ```json { "error": { "code": "UNAUTHENTICATED", "message": "Unauthenticated." } } ``` ## Status Code | Code | Kondisi | |------|---------| | 201 | Token berhasil dibuat | | 401 | Tidak terautentikasi | | 422 | Validasi request gagal | ## Contoh Request ```bash curl -s -X POST https://console.gisportal.id/api/v1/service-tokens \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "name": "GeoNetAgent-Production", "service": "GeoNetAgent", "expires_in_days": null }' ``` ## Sequence Flow ```mermaid sequenceDiagram Client->>API: POST /service-tokens {name, service} API->>API: Generate random token API->>API: Hash token (SHA256) API->>DB(databaselist_app): INSERT service_tokens API->>DB(geonetagent_dev): INSERT agent_tokens (sync) API->>AuditLog: log service_token_created API-->>Client: 201 {token (plain, sekali tampil), prefix, id} ``` ## Database yang Diakses - `databaselist_app.service_tokens` — INSERT - `geonetagent_dev.agent_tokens` — INSERT (sync, opsional) - `databaselist_audit` — INSERT audit log ## Audit Log Event: `service_token_created` ```json { "action": "service_token_created", "entity_type": "ServiceToken", "entity_id": "065eb796-ecb5-4d95-b4cd-9ca6244cf13f", "details": { "name": "GeoNetAgent-Production", "service": "GeoNetAgent", "token_prefix": "Col5FUFl34dVXgyB", "expires_at": null, "agent_token_synced": true } } ``` ## Security - Token hanya ditampilkan sekali — simpan segera - Gunakan HTTPS — token tidak boleh dikirim via plain HTTP - Simpan token di `.env` atau secret manager, bukan di source code ## Idempotent Tidak — setiap request menghasilkan token baru yang berbeda. ## Retry Policy - Jika `201` sudah diterima tapi token tidak sempat disimpan → jangan retry, gunakan Regenerate - Jika `500`: retry max 1x. Cek apakah token sudah terbuat di database sebelum retry ## Changelog | Tanggal | Perubahan | |---------|-----------| | 2026-06-23 | Endpoint dibuat, sync ke geonetagent_dev diimplementasi |