Skip to content

Sauth Client — Changelog

v0.3.3 (2026-05-26)

TL;DR

Perubahan:

  • 🔵 Per-issuer algorithm override — SAUTH_GWA_ALGO, SAUTH_GWC_ALGO; fallback ke SAUTH_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:

php
// 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:

dotenv
SAUTH_ALGO=RS256          # global fallback
SAUTH_GWA_ALGO=ES256      # hanya GWA
# GWC tidak perlu diset — sudah fallback ke RS256

Tidak ada perubahan perilaku jika SAUTH_GWA_ALGO dan SAUTH_GWC_ALGO tidak diisi — guard tetap menggunakan SAUTH_ALGO untuk semua issuer seperti sebelumnya.


Upgrade

bash
composer update bpmlib/sauth-client

Tidak 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 — RuntimeException dengan pesan actionable sebelum JWT::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 formatSAUTH_ALGOSebelumSesudah
PEM (-----BEGIN ...)EdDSAError kriptografi internalRuntimeException dengan pesan jelas
Base64 Ed25519 (tanpa header PEM)RS256 / ES256 / dllError kriptografi internalRuntimeException dengan pesan jelas
PEMRS256 / ES256 / dllTidak berubahTidak berubah
Base64 Ed25519EdDSATidak berubahTidak 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

bash
composer update bpmlib/sauth-client

Tidak 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 ke storage/ (Low)
  • ✨ Auto-add .gitignore entry 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).

bash
# 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 ada

Sesuaikan SAUTH_FAKE_CLAIMS_FILE di .env dengan nilai --path yang dipakai:

dotenv
SAUTH_FAKE_CLAIMS_FILE=sauth/dev-claims.json

Auto-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.json

Jika .gitignore tidak ditemukan atau entry sudah ada, command mencetak pesan info dan lanjut tanpa error.


Upgrade

bash
composer update bpmlib/sauth-client

Tidak 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 mengembalikan true untuk user token 1p saat fp='1p' (Breaking) — sebelumnya selalu false untuk 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_map untuk token 3p dengan iss berbasis URL (High)
  • 🟢 Claim fp di MicroserviceUser — 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_BYPASS deprecated — merge ke SAUTH_BYPASS via 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().

php
// 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.

NilaiPerilaku
false / none / ""Bypass nonaktif — production path normal
sigToken didecode tanpa verifikasi signature/exp/aud; permission & scope check tetap jalan
fetToken didecode tanpa verifikasi; permission check dilewati; scope check tetap jalan
scopeToken didecode tanpa verifikasi; scope check dilewati; permission check tetap jalan
permToken didecode tanpa verifikasi; permission + scope check keduanya dilewati
true / totalSemua check dilewati — termasuk shape check M2M dan kehadiran claim scope
dotenv
# 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=total

Route kombinasi (sauth.gate + sauth.scope):

dotenv
# Untuk 3p user endpoint — kedua middleware perlu sinkron
SAUTH_BYPASS=perm   # gate: skip permission; scope: skip scope check → 3p user shape penuh

Nilai 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.

php
// 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':

  1. Tidak cocok dengan kode pendek 'gwa' di step 1 & 2
  2. Step 3: iterasi trusted_issuer_url_map'gwa''https://gwa.example.com' cocok
  3. Public key diambil dari trusted_issuer_files.gwa atau trusted_issuers.gwa (logic yang sama)

Token iss kode pendek tetap berfungsi via step 1 & 2 — backward compatible dengan sauth-server lama.

dotenv
# .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.

php
$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!==null

Tidak 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:

bash
php artisan bpm:sauth:fake publish
# → menulis storage/sauth-fake-claims.json (skip jika sudah ada)

Format file:

json
{
    "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:

MiddlewareKunci profil
sauth.gate, sauth.dual1p_user
sauth.m2m1p_m2m
sauth.scope3p_m2m

Aktifkan via config:

dotenv
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.

dotenv
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

bash
composer update bpmlib/sauth-client

Sinkronisasikan 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:

bash
# 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.com

v0.2.1 (2026-05-24)

TL;DR

Perubahan:

  • 🐛 Middleware alias tidak terdaftar — afterResolving diganti dengan direct make(Router::class) di SauthClientServiceProvider (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

bash
composer update bpmlib/sauth-client

Tidak ada perubahan config atau env var.


v0.2.0 (2026-05-22)

TL;DR

Perubahan:

  • 🔧 isFirstParty() — discriminator diubah dari permissions !== null ke scope === null (High) — align dengan kontrak token ekosistem: 1p M2M tidak lagi membawa fet
  • ✨ JTI Blacklist — revokasi per-resource-server untuk API key JWT (High)
  • 🔧 Bypass sauth.m2mSAUTH_BYPASS=true kini aktif di route M2M (Medium)
  • ✨ Bypass sauth.scope — flag terpisah SAUTH_SCOPE_BYPASS + config bypass_fake_scope (Medium)
  • 🐛 Guard user() tidak menghormati setUser() (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.

php
// 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:

bash
php artisan bpm:sauth:jti publish-migration
php artisan migrate

Artisan command:

bash
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 diblokir

Injectable service JtiBlacklist — untuk HTTP endpoint internal (dipanggil oleh listener GWA):

php
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.m2mSAUTH_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.

dotenv
SAUTH_BYPASS=true
SAUTH_FAKE_M2M_SID=dev-worker      # injected jika tidak ada Authorization header
KondisiHasil
Bypass aktif + tidak ada token + SAUTH_FAKE_M2M_SID diisi200 — fake 1p M2M user
Bypass aktif + tidak ada token + SAUTH_FAKE_M2M_SID null401
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.

dotenv
SAUTH_SCOPE_BYPASS=true
SAUTH_FAKE_SCOPE_SID=dev-partner-client
SAUTH_FAKE_SCOPE_SCOPES=srf.read srf.write    # space-separated

Tiga kasus bypass:

KondisiHasil
Tidak ada token, tidak ada guard userFake 3p M2M dari bypass_fake_scope config
Tidak ada token, gate bypass sudah set guard userMerge scope ke user yang ada → 3p user shape (snm + fet + scope)
Token ada tapi tidak punya scope claim403

Route kombinasi (3p user endpoint):

dotenv
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

bash
composer update bpmlib/sauth-client

Tidak ada perubahan wajib kecuali:

  • Jika pakai isFirstParty() dan masih deploy sauth-server lama (yang masih kirim fet: [] di 1p M2M) — koordinasikan update keduanya bersamaan
  • Hapus bypass_fake_m2m.fet dari .env lokal 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 route sauth.scope yang perlu bypass lokal

v0.1.3 (2026-05-21)

TL;DR

Perubahan:

  • 🔥 Rename kode issuer — '01-gwa''gwa', '02-gwc''gwc' di ServiceCodes dan config (Breaking)
  • ✨ Middleware sauth.m2m baru (High) — route M2M-only; menerima 1p dan 3p token, menolak user token; scope params AND logic
  • 🔧 File-based public key loading (Medium) — trusted_issuer_files config; prioritas atas inline key; rotasi key tanpa ubah .env
  • 🔧 MicroserviceUser helpers: isFirstParty(), isThirdParty(), hasScope() (Medium)
  • 🔧 Config bypass_fake_m2m + env SAUTH_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:

php
// 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):

php
// 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:

  1. Update config/sauth-client.php di setiap consuming service — ganti key '01-gwa'/'02-gwc' ke 'gwa'/'gwc'
  2. Koordinasikan dengan update sauth-server — gateway harus menerbitkan iss: '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.

php
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 tokenTanpa paramsDengan scope params
User token (snm ada)403403
1p M2M (fet ada)LolosLolos — scope params diabaikan
3p M2M (scope ada)LolosLolos 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.

dotenv
GWA_PUBLIC_KEY_FILE=keys/gwa_public.pem
GWC_PUBLIC_KEY_FILE=keys/gwc_public.pem

Simpan 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:

php
'trusted_issuer_files' => [
    'gwa' => env('GWA_PUBLIC_KEY_FILE'),
    'gwc' => env('GWC_PUBLIC_KEY_FILE'),
],

MicroserviceUser — Typed Helpers untuk Subtipe M2M

php
$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 M2M

Config bypass_fake_m2m + Env SAUTH_FAKE_M2M_SID

Fake 1p M2M user untuk dev lokal pada route sauth.m2m saat tidak ada Authorization header.

php
'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.

bash
php artisan bpm:sauth:lookup --type=m2m
php artisan bpm:sauth:lookup --type=m2m --json

Upgrade

bash
composer update bpmlib/sauth-client

Breaking 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.

bash
# Cek config yang perlu diupdate
grep -n "01-gwa\|02-gwc" config/sauth-client.php

v0.1.2 (2026-05-20)

TL;DR

Perubahan:

  • 🔥 ServiceCodes — hapus konstanta SRF_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:

php
// 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:

php
// 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.

bash
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 jq

Output 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.show

Route dengan sauth.gate:psn,cpy muncul di baris psn dan cpy. Group middleware diambil via gatherMiddleware() — tidak ada route yang terlewat.


Upgrade

bash
composer update bpmlib/sauth-client

Breaking changes: Cek referensi ServiceCodes::SRF_ISSUER, ServiceCodes::SRF, ServiceCodes::PNR, ServiceCodes::PEL di codebase. Definisikan secara lokal jika ada yang menggunakannya.

bash
# 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_AUDIENCE diganti SAUTH_AUDIENCE (Breaking) — rename untuk konsistensi prefix ekosistem
  • ✨ Middleware sauth.scope baru (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 claim scope dari 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_AUDIENCESAUTH_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.

dotenv
# Before (v0.1.0) — tidak lagi dibaca
MSAUTH_AUDIENCE=srf

# After (v0.1.1) — wajib diperbarui
SAUTH_AUDIENCE=srf

Fix: 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), bukan fet
  • OR logic: sauth.scope:srf.read,srf.write lolos jika salah satu scope ada
  • Token 3p M2M tidak punya fetsauth.gate selalu menolaknya; sauth.scope menanganinya
  • Property $scope tersedia di MicroserviceUser untuk akses langsung di controller

🟢 Medium Impact

Peningkatan:

  • Config algorithms + env SAUTH_ALGO — algoritma JWT tidak lagi hardcoded RS256, bisa dikonfigurasi; default tetap RS256
  • Config bypass + env SAUTH_BYPASS — saat true dan app()->isLocal(), sauth.gate melewati verifikasi signature/expiry/audience tapi tetap decode token nyata ke MicroserviceUser; Authorization header tetap wajib kecuali bypass_fake_user.sid diisi
  • Config bypass_fake_user (SAUTH_FAKE_SID, SAUTH_FAKE_SNM, SAUTH_FAKE_FET) — fake user diinjeksikan saat bypass aktif dan tidak ada Authorization header; set sid=null untuk tetap meminta token nyata (meski tidak diverifikasi)
  • MicroserviceUser::$scope — property baru ?string yang menyimpan nilai claim scope; null untuk 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 mengembalikan null. Diperbaiki ke sauth-client.audience

Perubahan:

  • Guard membaca algoritma dari config('sauth-client.algorithms')[0] alih-alih string hardcoded

Upgrade

bash
composer update bpmlib/sauth-client

Breaking changes: 1 item — perbarui MSAUTH_AUDIENCESAUTH_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 untuk config/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 — stateless Authenticatable dengan isM2M(), userId(), clientId()
  • ConfigDrivenIssuerResolver — resolusi public key per issuer dari config

Upgrade

bash
composer require bpmlib/sauth-client

Setup awal: Publish config, daftarkan guard sauth di config/auth.php, isi env vars.