Sauth Client — Changelog
v0.3.3 (2026-05-26)
TL;DR
Perubahan:
- 🔵 Per-issuer algorithm override —
SAUTH_GWA_ALGO,SAUTH_GWC_ALGO; fallback keSAUTH_ALGO(Low)
Impact: 🔵 Low: 1
Backward Compatible: ✅ Ya
🔵 Low Impact
Per-Issuer Algorithm Override — issuer_algorithms Config
Config baru issuer_algorithms memungkinkan setiap issuer menggunakan algoritma tanda tangan yang berbeda. Berguna saat GWA dan GWC sedang dalam proses migrasi algoritma (misalnya GWA pindah ke ES256 sementara GWC masih RS256) — resource server bisa menerima keduanya tanpa harus mengorbankan salah satunya.
Config baru:
// config/sauth-client.php
'issuer_algorithms' => [
'gwa' => env('SAUTH_GWA_ALGO'), // null = fallback ke SAUTH_ALGO
'gwc' => env('SAUTH_GWC_ALGO'),
],Resolusi algoritma di guard (setelah issuer diketahui):
SAUTH_GWA_ALGO (atau SAUTH_GWC_ALGO) → SAUTH_ALGO → 'RS256'Per-issuer override dievaluasi setelah issuer diketahui dari JWT. Token 3p dengan iss berbasis URL di-map terlebih dahulu ke kode pendek via trusted_issuer_url_map sebelum lookup issuer_algorithms. Fallback SAUTH_ALGO tetap berfungsi bila override tidak dikonfigurasi.
Contoh — GWA migrasi ke ES256, GWC tetap RS256:
SAUTH_ALGO=RS256 # global fallback
SAUTH_GWA_ALGO=ES256 # hanya GWA
# GWC tidak perlu diset — sudah fallback ke RS256Tidak ada perubahan perilaku jika SAUTH_GWA_ALGO dan SAUTH_GWC_ALGO tidak diisi — guard tetap menggunakan SAUTH_ALGO untuk semua issuer seperti sebelumnya.
Upgrade
composer update bpmlib/sauth-clientTidak ada perubahan config atau env var yang diwajibkan. Hanya perlu set SAUTH_GWA_ALGO / SAUTH_GWC_ALGO jika gateway yang bersangkutan menggunakan algoritma berbeda dari SAUTH_ALGO.
v0.3.2 (2026-05-25)
TL;DR
Perubahan:
- 🔵 Deteksi mismatch key/algoritma di guard —
RuntimeExceptiondengan pesan actionable sebelumJWT::decode()(Low)
Impact: 🔵 Low: 1
Backward Compatible: ✅ Ya
🔵 Low Impact
Deteksi Mismatch Key/Algoritma di Guard
MicroserviceTokenGuard kini mendeteksi ketidakcocokan format public key dengan algoritma yang dikonfigurasi sebelum menyerahkan ke firebase/php-jwt. Sebelumnya, mismatch menghasilkan error kriptografi internal yang tidak jelas asalnya.
Kondisi yang dideteksi:
| Key format | SAUTH_ALGO | Sebelum | Sesudah |
|---|---|---|---|
PEM (-----BEGIN ...) | EdDSA | Error kriptografi internal | RuntimeException dengan pesan jelas |
| Base64 Ed25519 (tanpa header PEM) | RS256 / ES256 / dll | Error kriptografi internal | RuntimeException dengan pesan jelas |
| PEM | RS256 / ES256 / dll | Tidak berubah | Tidak berubah |
| Base64 Ed25519 | EdDSA | Tidak berubah | Tidak berubah |
Pesan error yang dihasilkan:
// PEM + EdDSA
Public key is PEM but SAUTH_ALGO=EdDSA expects base64-encoded Ed25519.
Run: php artisan bpm:sauth:keygen EdDSA on the issuing gateway,
then copy the generated public key to this service.
// Base64 + RS256
Public key appears to be base64 Ed25519 but SAUTH_ALGO=RS256 expects PEM.
Run: php artisan bpm:sauth:keygen RS256 on the issuing gateway,
then copy the generated public key to this service.Exception bertipe RuntimeException — tidak ditangkap oleh middleware (yang hanya menangkap UnknownIssuerException, TokenExpiredException, InvalidTokenException). Mismatch menghasilkan HTTP 500, bukan 401, karena ini adalah kesalahan konfigurasi operator, bukan token yang tidak valid.
Tidak ada perubahan perilaku saat key dan algoritma cocok. Deploy ke resource server kapan saja — tidak perlu koordinasi dengan gateway.
Upgrade
composer update bpmlib/sauth-clientTidak ada perubahan config atau env var. Jika SAUTH_ALGO dan format key sudah cocok, tidak ada dampak sama sekali.
v0.3.1 (2026-05-25)
TL;DR
Perubahan:
- ✨
bpm:sauth:fake publish --path— path kustom relatif kestorage/(Low) - ✨ Auto-add
.gitignoreentry setelah publish (Low)
Impact: 🔵 Low: 2
Backward Compatible: ✅ Ya
🔵 Low Impact
bpm:sauth:fake publish --path — Path Kustom
Flag --path memungkinkan penempatan file di subdirektori atau dengan nama berbeda, tetap relatif terhadap storage_path(). Default tidak berubah (sauth-fake-claims.json).
# Default (tidak berubah)
php artisan bpm:sauth:fake publish
# → storage/sauth-fake-claims.json
# Path kustom
php artisan bpm:sauth:fake publish --path=sauth/dev-claims.json
# → storage/sauth/dev-claims.json
# Subdirektori dibuat otomatis jika belum adaSesuaikan SAUTH_FAKE_CLAIMS_FILE di .env dengan nilai --path yang dipakai:
SAUTH_FAKE_CLAIMS_FILE=sauth/dev-claims.jsonAuto-add .gitignore Entry
Setelah file berhasil ditulis, command menambahkan entry storage/<path> ke .gitignore di root project jika belum ada. Ini mencegah file fake claims yang berisi data dev ikut ter-commit secara tidak sengaja.
# ditambahkan otomatis ke .gitignore
storage/sauth-fake-claims.jsonJika .gitignore tidak ditemukan atau entry sudah ada, command mencetak pesan info dan lanjut tanpa error.
Upgrade
composer update bpmlib/sauth-clientTidak ada perubahan config atau env var. Perintah lama bpm:sauth:fake publish (tanpa flag) berfungsi identik seperti sebelumnya.
v0.3.0 (2026-05-25)
TL;DR
Perubahan:
- 🔥
isFirstParty()— kini mengembalikantrueuntuk user token 1p saatfp='1p'(Breaking) — sebelumnya selalufalseuntuk user token - 🟡 Bypass mode enum
SAUTH_BYPASS— nilai boolean diganti multi-value:sig,fet,scope,perm,total(High) - 🟡 URL map issuer resolver —
trusted_issuer_url_mapuntuk token 3p denganissberbasis URL (High) - 🟢 Claim
fpdiMicroserviceUser— eksplisit first/third party marker dari token (Medium) - 🟢 JSON claim injection —
SAUTH_FAKE_CLAIMS_FILE+bpm:sauth:fake publish(Medium) - 🟢
SAUTH_JTI_BYPASS— skip JTI blacklist check lokal tanpa disable bypass sepenuhnya (Medium) - 🔵
SAUTH_SCOPE_BYPASSdeprecated — merge keSAUTH_BYPASSvia OR logic; masih berfungsi (Low)
Impact: 🔴 Breaking: 1 | 🟡 High: 2 | 🟢 Medium: 3 | 🔵 Low: 1
Backward Compatible: ❌ Tidak (lihat catatan isFirstParty())
🔴 Breaking Changes
isFirstParty() — Kini Membaca Claim fp dari Token
Saat token mengandung claim fp: '1p' (dikirim oleh sauth-server v0.3+), isFirstParty() mengembalikan true berdasarkan klaim eksplisit tersebut — termasuk user token 1p. Sebelumnya, isFirstParty() selalu false untuk user token karena bergantung pada isM2M().
// Sebelum (v0.2.x) — hanya true untuk M2M
$user->isFirstParty(); // false untuk user token, bahkan jika 1p
// Sesudah (v0.3.0) — membaca klaim fp jika ada
// fp='1p' → true (user token ATAU M2M)
// fp='3p' → false
// fp absent → fallback ke logic lama (isM2M() && scope === null)
$user->isFirstParty();isThirdParty() mengikuti pola yang sama: fp='3p' → true; fp absent → fallback isM2M() && scope !== null.
Catatan backward compat: Hanya berdampak jika code bergantung pada isFirstParty() untuk membedakan tipe token dan sauth-server sudah di-upgrade ke v0.3 (yang mulai menyisipkan klaim fp). Deploy sauth-server v0.3 dan sauth-client v0.3 bersamaan.
🟡 High Impact
Bypass Mode Enum SAUTH_BYPASS
SAUTH_BYPASS kini menerima nilai string selain true/false untuk kontrol granular — berguna saat ingin melewati hanya sebagian validasi di dev lokal.
| Nilai | Perilaku |
|---|---|
false / none / "" | Bypass nonaktif — production path normal |
sig | Token didecode tanpa verifikasi signature/exp/aud; permission & scope check tetap jalan |
fet | Token didecode tanpa verifikasi; permission check dilewati; scope check tetap jalan |
scope | Token didecode tanpa verifikasi; scope check dilewati; permission check tetap jalan |
perm | Token didecode tanpa verifikasi; permission + scope check keduanya dilewati |
true / total | Semua check dilewati — termasuk shape check M2M dan kehadiran claim scope |
# Decode real token tapi skip signature — paling aman untuk testing integrasi
SAUTH_BYPASS=sig
# Tidak perlu permission di token — tapi tetap butuh token
SAUTH_BYPASS=fet
# Skip semua — mode development penuh tanpa gateway
SAUTH_BYPASS=totalRoute kombinasi (sauth.gate + sauth.scope):
# Untuk 3p user endpoint — kedua middleware perlu sinkron
SAUTH_BYPASS=perm # gate: skip permission; scope: skip scope check → 3p user shape penuhNilai true (legacy) masih berfungsi sebagai alias total — backward compatible.
URL Map Issuer Resolver — trusted_issuer_url_map
sauth-server v0.3 mulai menerbitkan iss berbasis APP_URL untuk token 3p (bukan kode pendek seperti 'gwa'). ConfigDrivenIssuerResolver kini mendukung mapping URL → kode issuer sebagai step 3 setelah lookup file dan inline PEM.
// config/sauth-client.php
'trusted_issuer_url_map' => [
'gwa' => env('GWA_APP_URL'), // mis. 'https://gwa.example.com'
'gwc' => env('GWC_APP_URL'),
],Saat token datang dengan iss: 'https://gwa.example.com':
- Tidak cocok dengan kode pendek
'gwa'di step 1 & 2 - Step 3: iterasi
trusted_issuer_url_map—'gwa'→'https://gwa.example.com'cocok - Public key diambil dari
trusted_issuer_files.gwaatautrusted_issuers.gwa(logic yang sama)
Token iss kode pendek tetap berfungsi via step 1 & 2 — backward compatible dengan sauth-server lama.
# .env
GWA_APP_URL=https://gwa.example.com
GWC_APP_URL=https://gwc.example.com🟢 Medium Impact
Claim fp di MicroserviceUser
Property baru $fp: ?string tersedia di MicroserviceUser, diisi dari klaim fp token.
$user->fp // '1p' | '3p' | null (token lama / pra-v0.3 sauth-server)
// Typed helpers otomatis membaca fp jika ada
$user->isFirstParty() // true jika fp='1p'; fallback ke isM2M() && scope===null
$user->isThirdParty() // true jika fp='3p'; fallback ke isM2M() && scope!==nullTidak ada perubahan constructor — fp adalah parameter ke-6 dengan default null.
JSON Claim Injection — SAUTH_FAKE_CLAIMS_FILE
Cara baru untuk mengkonfigurasi fake identity di dev lokal — satu file JSON dengan profil per middleware, lebih mudah dikelola daripada banyak env var flat.
Publish template:
php artisan bpm:sauth:fake publish
# → menulis storage/sauth-fake-claims.json (skip jika sudah ada)Format file:
{
"1p_user": {
"sid": "dev-user-1",
"snm": "Dev User",
"fet": ["report.view"],
"fp": "1p"
},
"1p_m2m": {
"sid": "dev-worker",
"fp": "1p"
},
"3p_m2m": {
"sid": "dev-partner-client",
"scope": ["srf.read", "srf.write"],
"fp": "3p"
},
"3p_user": {
"sid": "dev-ext-user",
"snm": "Ext User",
"fet": ["user.read"],
"scope": ["srf.read"],
"fp": "3p"
}
}Mapping profil per middleware:
| Middleware | Kunci profil |
|---|---|
sauth.gate, sauth.dual | 1p_user |
sauth.m2m | 1p_m2m |
sauth.scope | 3p_m2m |
Aktifkan via config:
SAUTH_FAKE_CLAIMS_FILE=sauth-fake-claims.json # relatif ke storage/Jika file dikonfigurasi dan kunci profil ada, JSON dipakai. Jika tidak, fallback ke env var flat lama (SAUTH_FAKE_SID, dll) — backward compatible.
SAUTH_JTI_BYPASS — Skip JTI Blacklist Check di Dev Lokal
Flag baru untuk melewati JTI blacklist DB check saat bypass aktif dan database lokal tidak memiliki tabel blacklist.
SAUTH_JTI_BYPASS=true # skip DB check; hanya aktif saat app()->isLocal()Berguna saat service mengembangkan fitur API key JWT tapi lokal belum running migration blacklist. Tidak mempengaruhi prod — flag diabaikan di luar isLocal().
🔵 Low Impact
SAUTH_SCOPE_BYPASS Deprecated
SAUTH_SCOPE_BYPASS masih berfungsi tapi kini di-stack di atas SAUTH_BYPASS via OR logic. Nilai efektif adalah union keduanya:
SAUTH_BYPASS=false + SAUTH_SCOPE_BYPASS=true → effective: 'scope'
SAUTH_BYPASS=sig + SAUTH_SCOPE_BYPASS=true → effective: 'scope'
SAUTH_BYPASS=fet + SAUTH_SCOPE_BYPASS=true → effective: 'perm'
SAUTH_BYPASS=perm + SAUTH_SCOPE_BYPASS=true → effective: 'perm' (tidak berubah)Migrasi ke SAUTH_BYPASS=scope atau SAUTH_BYPASS=perm direkomendasikan. Warning dicetak ke stderr saat php artisan dijalankan jika scope_bypass masih aktif di environment lokal.
Upgrade
composer update bpmlib/sauth-clientSinkronisasikan dengan sauth-server v0.3 untuk klaim fp dan iss berbasis URL.
Breaking change: Cek penggunaan isFirstParty() — jika code bergantung pada nilainya untuk token user (bukan M2M), perilaku berubah saat fp='1p' hadir di token.
Opsional:
# Publish template fake claims untuk dev setup baru
php artisan bpm:sauth:fake publish
# Update .env lokal — ganti SAUTH_BYPASS=true ke nilai spesifik
SAUTH_BYPASS=sig # paling aman; masih membutuhkan token valid (tanpa verifikasi sig)
SAUTH_BYPASS=total # setara SAUTH_BYPASS=true lama
# Jika service menggunakan API key JWT dan lokal belum ada tabel blacklist
SAUTH_JTI_BYPASS=true
# Jika URL gateway dikonfigurasi (untuk token 3p sauth-server v0.3)
GWA_APP_URL=https://gwa.example.comv0.2.1 (2026-05-24)
TL;DR
Perubahan:
- 🐛 Middleware alias tidak terdaftar —
afterResolvingdiganti dengan directmake(Router::class)diSauthClientServiceProvider(Low)
Impact: 🔵 Low: 1
Backward Compatible: ✅ Ya
🔵 Low Impact
Bug Fix: Middleware Alias Tidak Terdaftar
afterResolving(Router::class, ...) tidak memicu callback dengan benar di beberapa setup consuming app, menyebabkan sauth.gate, sauth.wm, sauth.dual, sauth.scope, dan sauth.m2m tidak dikenali sebagai middleware alias.
Fix: registrasi alias kini dilakukan via $this->app->make(Router::class) langsung di boot() — aman karena Router selalu sudah terikat di container saat boot() dipanggil.
Upgrade
composer update bpmlib/sauth-clientTidak ada perubahan config atau env var.
v0.2.0 (2026-05-22)
TL;DR
Perubahan:
- 🔧
isFirstParty()— discriminator diubah daripermissions !== nullkescope === null(High) — align dengan kontrak token ekosistem: 1p M2M tidak lagi membawafet - ✨ JTI Blacklist — revokasi per-resource-server untuk API key JWT (High)
- 🔧 Bypass
sauth.m2m—SAUTH_BYPASS=truekini aktif di route M2M (Medium) - ✨ Bypass
sauth.scope— flag terpisahSAUTH_SCOPE_BYPASS+ configbypass_fake_scope(Medium) - 🐛 Guard
user()tidak menghormatisetUser()(Low) — bypass middleware tidak berfungsi tanpa fix ini
Impact: 🟡 High: 2 | 🟢 Medium: 2 | 🔵 Low: 1
Backward Compatible: ✅ Ya (dengan catatan di bawah)
🟡 High Impact
isFirstParty() — Discriminator Diubah ke scope === null
Logic lama menggunakan kehadiran fet (permissions !== null) sebagai penanda 1p M2M. Sejak kontrak token ekosistem diperbarui, 1p M2M tidak lagi membawa fet sama sekali — sehingga logic lama selalu mengembalikan false untuk token 1p M2M yang valid.
// Sebelum (v0.1.3)
public function isFirstParty(): bool
{
return $this->isM2M() && $this->permissions !== null; // salah — 1p M2M tidak punya fet
}
// Sesudah (v0.2.0)
public function isFirstParty(): bool
{
return $this->isM2M() && $this->scope === null; // benar — 1p tidak punya scope
}isThirdParty() tidak berubah (isM2M() && scope !== null — sudah benar sejak awal).
Catatan backward compat: Jika ada code yang bergantung pada isFirstParty() untuk token yang masih membawa fet: [] (token lama sebelum sauth-server diupdate), nilai return berubah. Deploy sauth-server (yang menghentikan pengiriman fet di 1p M2M) bersamaan dengan update library ini.
JTI Blacklist — Per-Resource-Server Revocation untuk API Key JWT
API key JWT adalah long-lived token tanpa exp. Revokasi dilakukan dengan memblokir jti UUID-nya di tabel lokal per resource server. Guard mengecek blacklist (dengan cache 5 menit) hanya jika token punya claim jti — token OAuth biasa tidak terpengaruh sama sekali.
Publish migration:
php artisan bpm:sauth:jti publish-migration
php artisan migrateArtisan command:
php artisan bpm:sauth:jti block {jti} [--reason=] # blokir; request berikutnya ditolak (401)
php artisan bpm:sauth:jti unblock {jti} # hapus blokir; cache flush otomatis
php artisan bpm:sauth:jti list # tampilkan semua JTI yang diblokirInjectable service JtiBlacklist — untuk HTTP endpoint internal (dipanggil oleh listener GWA):
class JtiBlacklistController extends Controller
{
public function block(Request $request, JtiBlacklist $blacklist): JsonResponse
{
$request->validate(['jti' => 'required|uuid', 'reason' => 'nullable|string']);
$blacklist->block($request->jti, $request->reason);
return response()->json(['blocked' => true]);
}
}Guard check berjalan otomatis — tidak ada perubahan route/middleware yang dibutuhkan. Cache TTL 5 menit: setelah unblock, token masih bisa lolos sebentar (documented behavior).
Optional — hanya perlu setup jika service menerima API key JWT. Service yang hanya menerima OAuth token tidak perlu publish migration.
🟢 Medium Impact
Bypass sauth.m2m — SAUTH_BYPASS=true Kini Aktif di M2M Routes
Sebelumnya SAUTH_BYPASS=true hanya aktif di sauth.gate. Kini juga aktif di sauth.m2m dengan pola yang sama.
SAUTH_BYPASS=true
SAUTH_FAKE_M2M_SID=dev-worker # injected jika tidak ada Authorization header| Kondisi | Hasil |
|---|---|
Bypass aktif + tidak ada token + SAUTH_FAKE_M2M_SID diisi | 200 — fake 1p M2M user |
Bypass aktif + tidak ada token + SAUTH_FAKE_M2M_SID null | 401 |
Bypass aktif + token ada (shape M2M, tidak ada snm) | 200 — user dari claims token |
Bypass aktif + token ada (shape user, ada snm) | 403 |
Catatan: Config bypass_fake_m2m.fet dihapus — tidak relevan lagi karena 1p M2M tidak membawa fet.
Bypass sauth.scope — Flag Terpisah SAUTH_SCOPE_BYPASS
Flag terpisah dari SAUTH_BYPASS agar setiap middleware bisa dikontrol secara independen. Penting untuk route kombinasi (sauth.gate + sauth.scope): kedua flag harus aktif bersama untuk menghasilkan 3p user shape yang benar.
SAUTH_SCOPE_BYPASS=true
SAUTH_FAKE_SCOPE_SID=dev-partner-client
SAUTH_FAKE_SCOPE_SCOPES=srf.read srf.write # space-separatedTiga kasus bypass:
| Kondisi | Hasil |
|---|---|
| Tidak ada token, tidak ada guard user | Fake 3p M2M dari bypass_fake_scope config |
| Tidak ada token, gate bypass sudah set guard user | Merge scope ke user yang ada → 3p user shape (snm + fet + scope) |
Token ada tapi tidak punya scope claim | 403 |
Route kombinasi (3p user endpoint):
SAUTH_BYPASS=true # sauth.gate — inject user shape (snm + fet)
SAUTH_SCOPE_BYPASS=true # sauth.scope — merge scope ke user yang sudah ada🔵 Low Impact
Bug Fix: Guard user() Tidak Menghormati setUser()
MicroserviceTokenGuard::user() mengecek cache internal ($resolvedUser) tapi melewati $this->user yang di-set oleh GuardHelpers::setUser(). Akibatnya, bypass middleware yang memanggil $guard->setUser($fakeUser) tidak berdampak — controller tetap mendapat null dari auth()->user().
Fix: user() kini mengecek $this->user instanceof MicroserviceUser terlebih dahulu sebelum mencoba decode token dari request.
Upgrade
composer update bpmlib/sauth-clientTidak ada perubahan wajib kecuali:
- Jika pakai
isFirstParty()dan masih deploy sauth-server lama (yang masih kirimfet: []di 1p M2M) — koordinasikan update keduanya bersamaan - Hapus
bypass_fake_m2m.fetdari.envlokal jika pernah diset (tidak akan error, hanya diabaikan)
Opsional:
bpm:sauth:jti publish-migration+php artisan migrate— jika service menerima API key JWT- Set
SAUTH_SCOPE_BYPASS=true+SAUTH_FAKE_SCOPE_SID— jika ada routesauth.scopeyang perlu bypass lokal
v0.1.3 (2026-05-21)
TL;DR
Perubahan:
- 🔥 Rename kode issuer —
'01-gwa'→'gwa','02-gwc'→'gwc'diServiceCodesdan config (Breaking) - ✨ Middleware
sauth.m2mbaru (High) — route M2M-only; menerima 1p dan 3p token, menolak user token; scope params AND logic - 🔧 File-based public key loading (Medium) —
trusted_issuer_filesconfig; prioritas atas inline key; rotasi key tanpa ubah.env - 🔧
MicroserviceUserhelpers:isFirstParty(),isThirdParty(),hasScope()(Medium) - 🔧 Config
bypass_fake_m2m+ envSAUTH_FAKE_M2M_SID(Medium) - 🔧
bpm:sauth:lookup --type=m2m(Medium)
Impact: 🔴 Breaking: 1 | 🟡 High: 1 | 🟢 Medium: 4
Backward Compatible: ❌ Tidak (kode issuer berubah)
🔴 Breaking Changes
Rename Kode Issuer: '01-gwa' → 'gwa', '02-gwc' → 'gwc'
Kode issuer di ServiceCodes dan config key diubah untuk menghapus prefix numerik.
ServiceCodes:
// Sebelum (v0.1.2)
ServiceCodes::GWA // '01-gwa'
ServiceCodes::GWC // '02-gwc'
// Sesudah (v0.1.3)
ServiceCodes::GWA // 'gwa'
ServiceCodes::GWC // 'gwc'Config sauth-client.php (published):
// Sebelum
'trusted_issuers' => ['01-gwa' => ..., '02-gwc' => ...],
'trusted_issuer_files' => ['01-gwa' => ..., '02-gwc' => ...],
// Sesudah
'trusted_issuers' => ['gwa' => ..., 'gwc' => ...],
'trusted_issuer_files' => ['gwa' => ..., 'gwc' => ...],Fix:
- Update
config/sauth-client.phpdi setiap consuming service — ganti key'01-gwa'/'02-gwc'ke'gwa'/'gwc' - Koordinasikan dengan update
sauth-server— gateway harus menerbitkaniss: 'gwa'/'gwc'; deploy keduanya bersamaan
🟡 High Impact
Middleware sauth.m2m — M2M-Only Route Support
Middleware baru ValidateM2MToken (alias sauth.m2m) untuk route yang hanya boleh diakses token M2M — baik 1p internal worker maupun 3p external partner — dan menolak user token secara eksplisit.
Route::middleware('sauth.m2m') // token M2M apapun
Route::middleware('sauth.m2m:srf.read') // 1p selalu lolos; 3p wajib punya srf.read
Route::middleware('sauth.m2m:srf.read,srf.write') // 1p lolos; 3p wajib punya KEDUA scope (AND)| Tipe token | Tanpa params | Dengan scope params |
|---|---|---|
User token (snm ada) | 403 | 403 |
1p M2M (fet ada) | Lolos | Lolos — scope params diabaikan |
3p M2M (scope ada) | Lolos | Lolos hanya jika semua scope ada (AND) |
🟢 Medium Impact
File-Based Public Key Loading
ConfigDrivenIssuerResolver kini mendukung pembacaan public key dari file dengan prioritas lebih tinggi dari inline PEM.
GWA_PUBLIC_KEY_FILE=keys/gwa_public.pem
GWC_PUBLIC_KEY_FILE=keys/gwc_public.pemSimpan file PEM di storage/keys/. Jika *_FILE tidak diisi, fallback ke GWA_PUBLIC_KEY inline. Cocok untuk rotasi key berbasis Docker secret atau K8s secret.
Config:
'trusted_issuer_files' => [
'gwa' => env('GWA_PUBLIC_KEY_FILE'),
'gwc' => env('GWC_PUBLIC_KEY_FILE'),
],MicroserviceUser — Typed Helpers untuk Subtipe M2M
$user->isFirstParty(): bool // true jika 1p M2M — fet ada (bahkan [])
$user->isThirdParty(): bool // true jika 3p M2M — scope ada
$user->hasScope(string ...$scopes): bool // AND logic; false untuk 1p M2MConfig bypass_fake_m2m + Env SAUTH_FAKE_M2M_SID
Fake 1p M2M user untuk dev lokal pada route sauth.m2m saat tidak ada Authorization header.
'bypass_fake_m2m' => [
'sid' => env('SAUTH_FAKE_M2M_SID', null),
'fet' => [],
],bpm:sauth:lookup --type=m2m
Command bpm:sauth:lookup kini mendukung --type=m2m untuk reverse-index route yang menggunakan sauth.m2m.
php artisan bpm:sauth:lookup --type=m2m
php artisan bpm:sauth:lookup --type=m2m --jsonUpgrade
composer update bpmlib/sauth-clientBreaking change — wajib: Update config/sauth-client.php di setiap service — ganti key '01-gwa'/'02-gwc' ke 'gwa'/'gwc' di trusted_issuers dan trusted_issuer_files. Koordinasikan dengan deployment sauth-server yang menerbitkan issuer code baru.
# Cek config yang perlu diupdate
grep -n "01-gwa\|02-gwc" config/sauth-client.phpv0.1.2 (2026-05-20)
TL;DR
Perubahan:
- 🔥
ServiceCodes— hapus konstantaSRF_ISSUER,SRF,PNR,PEL(Breaking) — nilai app-level tidak seharusnya ada di library ekosistem - ✨ Artisan command
bpm:sauth:lookup(High) — reverse-index route berdasarkan sauth middleware key
Impact: 🔴 Breaking: 1 | 🟡 High: 1
Backward Compatible: ❌ Tidak (konstanta publik dihapus)
🔴 Breaking Changes
Hapus Konstanta ServiceCodes::SRF_ISSUER, SRF, PNR, PEL
Empat konstanta ini dihapus dari ServiceCodes:
// Tidak ada lagi di v0.1.2
ServiceCodes::SRF_ISSUER // '06-srf'
ServiceCodes::SRF // 'srf'
ServiceCodes::PNR // 'pnr'
ServiceCodes::PEL // 'pel'Alasan: Nilai-nilai ini adalah app-level knowledge, bukan kontrak ekosistem. Setiap service sudah tahu audience code-nya sendiri dari SAUTH_AUDIENCE di .env. SRF_ISSUER hanya relevan untuk service yang menerima exam token SRF — domain-specific, bukan ekosistem-wide. Memasukkan kode service baru ke library berarti semua consumer harus update dependency hanya untuk string yang sudah mereka ketahui sendiri.
Fix: Jika codebase kamu referensi konstanta ini, definisikan sendiri secara lokal:
// Di consuming app — bukan di library
private const SRF_ISSUER = '06-srf';Konstanta yang tetap ada: GWA, GWC, WEBMASTER, ROLE_PERSONAL, ROLE_COMPANY, ROLE_MITRA, CUSTOMER_ROLES.
🟡 High Impact
Artisan Command bpm:sauth:lookup
Command baru untuk reverse-index semua route berdasarkan middleware sauth yang dipakai. Setara dengan artisan route:list tapi di-scope ke permission/scope sauth.
php artisan bpm:sauth:lookup # semua permission (fet)
php artisan bpm:sauth:lookup --type=scope # OAuth scope (3p M2M)
php artisan bpm:sauth:lookup --type=wm # route webmaster
php artisan bpm:sauth:lookup --json # output JSON, pipeable ke jqOutput default (--type=fet):
Permission Routes Route Names
------------ ------ ----------------------------------------
(none) 1 api.health
psn 3 api.profile.show, api.profile.update, ...
report.view 2 api.reports.index, api.reports.showRoute dengan sauth.gate:psn,cpy muncul di baris psn dan cpy. Group middleware diambil via gatherMiddleware() — tidak ada route yang terlewat.
Upgrade
composer update bpmlib/sauth-clientBreaking changes: Cek referensi ServiceCodes::SRF_ISSUER, ServiceCodes::SRF, ServiceCodes::PNR, ServiceCodes::PEL di codebase. Definisikan secara lokal jika ada yang menggunakannya.
# Cek di consuming app
grep -r "ServiceCodes::SRF\|ServiceCodes::PNR\|ServiceCodes::PEL\|ServiceCodes::SRF_ISSUER" app/ routes/ config/v0.1.1 (2026-05-19)
TL;DR
Perubahan:
- 🔥 Env var
MSAUTH_AUDIENCEdigantiSAUTH_AUDIENCE(Breaking) — rename untuk konsistensi prefix ekosistem - ✨ Middleware
sauth.scopebaru (High) — validasi scope untuk third-party M2M token - 🔧 Config
algorithms+bypass(Medium) — algoritma JWT konfigurable + dev bypass lokal (decode tanpa verifikasi) - 🔧 Config
bypass_fake_user(Medium) — fake user untuk dev lokal tanpa gateway sama sekali - 🔧 Property
MicroserviceUser::$scope(Medium) — eksposes claimscopedari token 3p M2M - 🐛 Bug fix: audience validation guard (Low) — salah config key di v0.1.0 menyebabkan semua request ditolak
Impact: 🔴 Breaking: 1 | 🟡 High: 1 | 🟢 Medium: 3 | 🔵 Low: 1
Backward Compatible: ❌ Tidak (env var rename)
🔴 Breaking Changes
Rename Env Var: MSAUTH_AUDIENCE → SAUTH_AUDIENCE
Env var untuk audience code diganti dari MSAUTH_AUDIENCE menjadi SAUTH_AUDIENCE agar konsisten dengan prefix SAUTH_ yang dipakai env var lainnya di ekosistem ini.
# Before (v0.1.0) — tidak lagi dibaca
MSAUTH_AUDIENCE=srf
# After (v0.1.1) — wajib diperbarui
SAUTH_AUDIENCE=srfFix: Ganti nama env var di semua file .env dan deployment config (Forge, Railway, Docker, dll). Config key PHP (config('sauth-client.audience')) tidak berubah.
🟡 High Impact
Middleware sauth.scope — Third-Party M2M Token Support
Middleware baru CheckServiceScope (alias sauth.scope) memungkinkan service menerima token dari external OAuth client yang menggunakan standard OAuth scope claim — berbeda dari token internal yang menggunakan claim fet. Ini memisahkan route internal (user + 1p M2M worker) dari route partner eksternal secara eksplisit, tanpa risiko token tercampur.
Poin utama:
- Membaca claim
scope(space-separated), bukanfet - OR logic:
sauth.scope:srf.read,srf.writelolos jika salah satu scope ada - Token 3p M2M tidak punya
fet→sauth.gateselalu menolaknya;sauth.scopemenanganinya - Property
$scopetersedia diMicroserviceUseruntuk akses langsung di controller
🟢 Medium Impact
Peningkatan:
- Config
algorithms+ envSAUTH_ALGO— algoritma JWT tidak lagi hardcodedRS256, bisa dikonfigurasi; default tetapRS256 - Config
bypass+ envSAUTH_BYPASS— saattruedanapp()->isLocal(),sauth.gatemelewati verifikasi signature/expiry/audience tapi tetap decode token nyata keMicroserviceUser; Authorization header tetap wajib kecualibypass_fake_user.siddiisi - Config
bypass_fake_user(SAUTH_FAKE_SID,SAUTH_FAKE_SNM,SAUTH_FAKE_FET) — fake user diinjeksikan saat bypass aktif dan tidak ada Authorization header; setsid=nulluntuk tetap meminta token nyata (meski tidak diverifikasi) MicroserviceUser::$scope— property baru?stringyang menyimpan nilai claimscope;nulluntuk user token dan 1p M2M token
🔵 Low Impact
Bug Fix:
- Guard menggunakan config key yang salah (
msauth-client.audience) di v0.1.0 — menyebabkan audience validation selalu gagal karena key tidak ditemukan dan mengembalikannull. Diperbaiki kesauth-client.audience
Perubahan:
- Guard membaca algoritma dari
config('sauth-client.algorithms')[0]alih-alih string hardcoded
Upgrade
composer update bpmlib/sauth-clientBreaking changes: 1 item — perbarui MSAUTH_AUDIENCE → SAUTH_AUDIENCE di semua .env dan deployment config
Opsional: Gunakan sauth.scope untuk route external partner token; aktifkan SAUTH_BYPASS=true + set SAUTH_FAKE_SID di .env.local untuk dev loop tanpa gateway sama sekali
v0.1.0 (2026-05-19)
TL;DR
Perubahan:
- ✨ Rilis awal (High) — JWT verification stack untuk Laravel microservice
Impact: 🟡 High: 1
Backward Compatible: ✅ Ya
🟡 High Impact
Rilis Awal — JWT Verification Stack
Library ini menyediakan semua yang dibutuhkan service untuk memverifikasi JWT dari gateway (GWA atau GWC) — tanpa Passport, tanpa database. Guard sauth (MicroserviceTokenGuard) memvalidasi token per request dan mengisi auth()->user() dengan objek stateless MicroserviceUser.
Poin utama:
- Guard
sauth— driver untukconfig/auth.php, validasi JWT berbasis public key per issuer - Middleware
sauth.gate— autentikasi + permission check opsional dengan OR logic dan wildcard - Middleware
sauth.wm— route khusus webmaster (fet === 'wm') - Middleware
sauth.dual— session-or-JWT untuk gateway monolith (GWA, GWC) MicroserviceUser— statelessAuthenticatabledenganisM2M(),userId(),clientId()ConfigDrivenIssuerResolver— resolusi public key per issuer dari config
Upgrade
composer require bpmlib/sauth-clientSetup awal: Publish config, daftarkan guard sauth di config/auth.php, isi env vars.