Feature SSO Akses Panel Admin OpenSID dari OpenKab - #1104
Open
pandigresik wants to merge 10 commits into
Open
Conversation
|
🔄 AI PR Review sedang antri di server...
|
Contributor
Author
Contributor
Author
|
Panduan deployment: docs/sso-opensid.md |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Pull Request: Feature SSO Akses Panel Admin OpenSID dari OpenKab
Description
Menerapkan Single Sign-On (SSO) yang memungkinkan administrator OpenKab (sesi aktif + 2FA selesai) masuk ke panel admin OpenSID desa hanya dengan sekali klik "Masuk ke OpenSID", tanpa login manual. OpenKab menerbitkan JWT RS256 (RSA-2048) sekali pakai berumur pendek, OpenSID memverifikasinya kembali ke OpenKab (server-to-server) dan melakukan pengecekan ganda status/izin di datanya sendiri sebelum membuat sesi. Seluruh percobaan (berhasil/gagal) dicatat append-only di kedua sistem dan dapat direview super admin melalui dashboard audit.
Changes made:
app/Services/SsoKeyManager.php— Manajemen kunci RS256: muat PEM private/public dari env/file, validasi RSA ≥2048-bit, dukungan beberapa public key untuk rotasi transisiapp/Services/SsoTokenService.php— Terbitkan/verifikasi/konsumsi token JWT RS256 (klaimjti/iat/nbf/exp/sub/desa_id/iss/aud, TTL ≤600 detik default 300, leeway 30 detik,algdipaksa RS256 anti downgrade), persisten keopenkab_sso_tokens, konsumsi atomik sekali pakaiapp/Services/SsoAuditLogger.php— Pencatatan audit immutabel (logAttempt&logVerification) keopenkab_sso_logsapp/Services/OpenSidUrlResolver.php— Resolusi URL admin OpenSID server-side dari fieldattributes.websitedesa pada API database gabungan (/api/v1/wilayah/penduduk, cache singkat ±5 menit), validasi skema HTTPS; input klien tidak pernah menjadi sumber URL redirect (FR-022). Konfigurasi manualdesa_sso_configs/SSO_OPENSID_BASE_URLdihapusapp/Http/Controllers/Sso/SsoLoginController.php— EndpointPOST /sso/generate-session: gate kelayakan berurutan (role administrator,2fa_verified, 2FA aktif, akun aktif/tidak terkunci, scope kabupaten), validasi origin, rate limit, resolusi URL dariwebsiteAPI, respons JSON{status, redirect_url, token, token_method, expires_at, retry_after}; error generik'Autentikasi gagal.'(FR-018)app/Http/Controllers/Api/SsoVerifyController.php— EndpointPOST /api/v1/sso/verify-token: verifikasi RS256, cek sekali pakai/replay/kedaluwarsa, cek ulang state user, konsumsi atomik, log, respons minimal tanpa PII; validasi memakaiapp/Http/Requests/SsoVerifyRequest.php(FormRequest)app/Http/Middleware/SsoCallbackAuth.php+SsoIpWhitelist.php— Otentikasi callback server-to-server (sekret bersama + HMAC-SHA256 atas body + timestamp ±30 detik,hash_equals), whitelist IP opsionalapp/Http/Controllers/Sso/SsoAuditController.php— Dashboard audit (DataTables + filter);DesaSsoConfigControllerdihapusOpenKabSsoLog(append-only, tolak update/delete),OpenKabSsoToken(konsumsi atomik); tabelopenkab_sso_logs,openkab_sso_tokens(tabeldesa_sso_configsdihapus/di-rollback)2026_08_11_000001_add_sso_audit_permission.php— buat permissionsso-audit-read, lampirkan ke roleadministrator, laluadmin:menu-update(logika seederSsoPermissionsSeederdi-inline ke migration, seeder dihapus)config/sso.php— Konfigurasi fitur (kunci RS256,SSO_CALLBACK_SECRET, TTL, toleransi jam, rate limit, endpoint)resources/views/sso/— View dashboard audit + partial filter (view konfigurasi desa dihapus)resources/views/desa/index.blade.php— Tombol "Masuk ke OpenSID" hanya untuk desa denganwebsiteterisi (auto-submit form POST token, CSP nonce); desa tanpawebsitemenampilkan keterangan "URL website belum diisi" (bukan tombol)routes/web.php(generate-session, audit; routesso-configdihapus),routes/apiv1.php(verify-token),bootstrap/app.php(alias middlewaresso.callback/sso.ip-whitelist),app/Enums/Modul.php(submenu "Audit Akses SSO" di bawah "Pengaturan OpenSID"),app/Providers/AppServiceProvider.php(rate limitersso-generate5/menit per user+IP)app/Console/Commands/SsoPurgeTokensCommand.php—php artisan sso:purge-tokensuntuk pembersihan token kedaluwarsaapp/Console/Commands/SsoGenerateKeysCommand.php(php artisan sso:generate-keys) +scripts/sso-keygen.sh— generate keypair RSA (default 2048, opsi--bits), tulis private key (chmod 0600) + public key, isiSSO_SIGNING_PRIVATE_KEY_FILE/SSO_SIGNING_PUBLIC_KEY_FILEdi.env; menolak bila kunci sudah terisi kecuali--forcedocs/sso-opensid.md— Panduan deployment & operasional (sso:generate-keys, env, rotasi kunci, pembersihan)composer.json(+firebase/php-jwt^7.0),.env.example(hapusSSO_OPENSID_BASE_URL),.gitignore(+/storage/sso/),README.mdtests/Feature/Sso/SsoGenerateSessionTest.php,SsoSecurityTest.php,SsoVerifyTokenTest.php,SsoAuditDashboardTest.php,SsoGenerateKeysCommandTest.php(37 test) +tests/Browser/SmokeSsoAccessTest.php(3 smoke test) + fixture keypair ditests/fixtures/sso/Reason for change:
Impact of change:
php artisan sso:generate-keys(atauscripts/sso-keygen.sh); rotasi kunci didukung tanpa memutus token yang masih berlakuRelated Issue
#1103
Steps to Reproduce
Sebelum (masalah):
Sesudah (solusi):
website-nya terisi (desa tanpawebsitemenampilkan keterangan "URL website belum diisi")websitedesa di API gabunganopenkab_sso_logs(danopensid_sso_logsdi sisi OpenSID)Testing pada fitur terkait:
2fa_verified,2fa_enabled,isLocked)Retry-Aftersetelah 5 percobaan/menit)Checklist
Technical Details
Technical Explanation
Alur verifikasi dua lapis:
SSO_CALLBACK_SECRETmengotentikasi pemanggil endpoint verifikasi (OpenSID) — dua lapisan yang berbeda, keduanya wajib.UPDATE ... SET used_at = NOW() WHERE jti = ? AND used_at IS NULL).SsoTokenService::verify()mencoba seluruh public key yang terdaftar (rotasi transisi) dan menegakkanalg=RS256padaKey.OpenKabSsoLogmenolakupdate/deletepada level model (append-only).Configuration changes
.env.examplemenambahkan:SSO_SIGNING_PRIVATE_KEY/SSO_SIGNING_PRIVATE_KEY_FILE(RSA-2048, hanya OpenKab)SSO_SIGNING_PUBLIC_KEY/SSO_SIGNING_PUBLIC_KEY_FILE(didistribusikan ke OpenSID)SSO_SIGNING_PUBLIC_KEYS_FILE(public key lama untuk rotasi transisi)SSO_CALLBACK_SECRET(≥32 byte, dibagikan ke OpenSID)SSO_TOKEN_TTL,SSO_IP_WHITELIST,SSO_RATE_LIMIT_MAX,SSO_CLOCK_SKEW_TOLERANCESSO_OPENSID_BASE_URLdihapus — URL OpenSID kini dariwebsitedesa di API gabungan)Permission
sso-audit-read+ menu "Audit Akses SSO" dibuat otomatis oleh migration2026_08_11_000001saatphp artisan migrate.Dependencies added
firebase/php-jwt: ^7.0 (v6.x dikeluarkan karena terdampak security advisory)Testing
Manual Testing
website→ tombol diganti keterangan "URL website belum diisi"X-SSO-Callback-Keyyang benar → 401php artisan sso:generate-keys→ file kunci +.envterisi; dijalankan lagi → ditolak tanpa--forceAutomated Testing
tests/Feature/Sso/SsoGenerateSessionTest.php— alur berhasil, validasi desa_id, TTL, alg RS256, desa tanpawebsite/API gagal → CONFIGURATION_ERRORtests/Feature/Sso/SsoSecurityTest.php— non-admin, tanpa 2FA, akun nonaktif/terkunci, origin invalid, desa tanpawebsite, rate limit 429, tanpa bocor PIItests/Feature/Sso/SsoVerifyTokenTest.php— valid, replay, expired, tamper, kunci lain, HS256 (anti downgrade), callback unauthorized, tanpa bocor PIItests/Feature/Sso/SsoAuditDashboardTest.php— akses super admin, filter, 403 non-permissiontests/Feature/Sso/SsoGenerateKeysCommandTest.php— keypair + isi env, perms 0600, JWT RS256 roundtrip, tolak tanpa--force,--forcemenimpa, tolak--bits<2048tests/Browser/SmokeSsoAccessTest.php— tombol tampil, keterangan untuk desa tanpa website, auto-submit (Playwright/Pest Browser)Status:
php artisan test tests/Feature/Sso→ 37 passed (136 assertions) (dengan gate kelayakan aktif);tests/Browser/SmokeSsoAccessTest.php→ 3 passed.Screenshots / Video
simplescreenrecorder-2026-08-11_09.31.14.mp4
Sebelum:
Belum ada — fitur baru (login manual ke tiap OpenSID).
Sesudah:
Breaking Changes
desa_sso_configs, routesso-config, permission/menu CRUD konfigurasi desa, dan envSSO_OPENSID_BASE_URL/config('sso.opensid_base_url')— base URL OpenSID kini di-resolve dari fieldwebsitedesa pada API database gabungan. Admin yang sebelumnya memakai konfigurasi manual harus memastikanwebsitedesa terisi di data gabungan.SsoPermissionsSeederdipindah (di-inline) ke migration2026_08_11_000001_add_sso_audit_permission— cukupphp artisan migrateuntuk permissionsso-audit-read+ menu.Migration Guide
composer install(dependencyfirebase/php-jwt^7.0).php artisan sso:generate-keys(ataubash scripts/sso-keygen.sh) → isiSSO_SIGNING_PRIVATE_KEY_FILE/SSO_SIGNING_PUBLIC_KEY_FILEdi.env; distribusikan public key ke instalasi OpenSID (out-of-band).SSO_CALLBACK_SECRET(≥32 byte) dan bagikan ke OpenSID.php artisan migrate(migration2026_08_11_000001membuat permissionsso-audit-read+ menjalankanadmin:menu-update).websitedesa terisi di API database gabungan (sumber base URL OpenSID).php artisan sso:purge-tokensuntuk pembersihan token kedaluwarsa (opsional).specs/001-opensid-sso-access/contracts/opensid-sso-contract.md(repo terpisah).References
specs/001-opensid-sso-access/contracts/openkab-sso-api.mdspecs/001-opensid-sso-access/contracts/opensid-sso-contract.mddocs/sso-opensid.mdAdditional notes: Dokumentasi spesifikasi, plan, riset, dan data model tersimpan di
specs/001-opensid-sso-access/(diabaikan dari git). Bagian frontend tombol SSO menampilkan pesan generik via SweetAlert bila permintaan gagal. Screenshot pada bagian atas bersifat placeholder — dapat ditambahkan setelah demo di environment staging.