tenant-registry's API is INTERNAL_SERVICE_ONLY — once its AUTH_ENABLED flips, every route but /healthz and /readyz needs a Keycloak token whose audience contains tenant-registry. Today the portal calls it with no Authorization header at all, so this is the prerequisite for enabling enforcement there.
The portal calls tenant-registry as a service principal (machine-to-machine), which is distinct from the visitor's SSO session that authenticates a human against dev-portal.
New src/lib/service-token.ts: fetches and caches a client_credentials token for the realm's new portal-svc client, refreshes 30s before the 5-minute expiry, and de-dupes concurrent fetches so parallel renders share one request. A failed request throws rather than silently making an unauthenticated call, and does not poison the cache.
Wired into the single req() choke point in tenant-registry.ts, so all 10 call sites are covered at once.
Inert until configured: with PORTAL_SVC_CLIENT_ID / PORTAL_SVC_CLIENT_SECRET unset the helper returns null and no header is sent — exactly today's behaviour. That is what makes it safe to deploy before tenant-registry enforces.
Realm side is already done: portal-svc client created with the tenant-registry-audience scope; a live token was verified to carry aud: [tenant-registry, account] with a 300s TTL. Env staged in sharang/orca-infra#56 (secret already set via orca secrets).
9 new tests; full suite 79 green; typecheck, lint (--max-warnings 0) and production build all clean.
tenant-registry's API is INTERNAL_SERVICE_ONLY — once its AUTH_ENABLED flips, every route but /healthz and /readyz needs a Keycloak token whose audience contains `tenant-registry`. Today the portal calls it with no Authorization header at all, so this is the prerequisite for enabling enforcement there.
The portal calls tenant-registry as a **service principal** (machine-to-machine), which is distinct from the visitor's SSO session that authenticates a human against `dev-portal`.
- New `src/lib/service-token.ts`: fetches and caches a client_credentials token for the realm's new `portal-svc` client, refreshes 30s before the 5-minute expiry, and de-dupes concurrent fetches so parallel renders share one request. A failed request throws rather than silently making an unauthenticated call, and does not poison the cache.
- Wired into the single `req()` choke point in `tenant-registry.ts`, so all 10 call sites are covered at once.
- **Inert until configured**: with PORTAL_SVC_CLIENT_ID / PORTAL_SVC_CLIENT_SECRET unset the helper returns null and no header is sent — exactly today's behaviour. That is what makes it safe to deploy before tenant-registry enforces.
Realm side is already done: `portal-svc` client created with the tenant-registry-audience scope; a live token was verified to carry `aud: [tenant-registry, account]` with a 300s TTL. Env staged in sharang/orca-infra#56 (secret already set via orca secrets).
9 new tests; full suite 79 green; typecheck, lint (--max-warnings 0) and production build all clean.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
tenant-registry's API is INTERNAL_SERVICE_ONLY: once its AUTH_ENABLED
flips, every route but /healthz and /readyz requires a Keycloak token
whose audience contains `tenant-registry`. The portal calls it as a
service principal — machine-to-machine, distinct from the visitor's SSO
session, which authenticates a human against dev-portal.
New src/lib/service-token.ts fetches and caches a client_credentials
token for the realm's portal-svc client, refreshing 30s before the
5-minute expiry and de-duping concurrent fetches so parallel renders
share one request. A failed request throws rather than silently falling
back to an unauthenticated call, and does not poison the cache.
Wired into the single req() choke point in tenant-registry.ts, so every
call is covered. Inert until configured: with PORTAL_SVC_CLIENT_ID /
PORTAL_SVC_CLIENT_SECRET unset the helper returns null, no header is
sent, and behaviour is exactly as today — which is what keeps this
deployable before tenant-registry starts enforcing.
9 new tests; full suite 79 green; typecheck, lint and build clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
sharang
merged commit f2f9ab74c8 into main2026-08-25 09:15:51 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
tenant-registry's API is INTERNAL_SERVICE_ONLY — once its AUTH_ENABLED flips, every route but /healthz and /readyz needs a Keycloak token whose audience contains
tenant-registry. Today the portal calls it with no Authorization header at all, so this is the prerequisite for enabling enforcement there.The portal calls tenant-registry as a service principal (machine-to-machine), which is distinct from the visitor's SSO session that authenticates a human against
dev-portal.src/lib/service-token.ts: fetches and caches a client_credentials token for the realm's newportal-svcclient, refreshes 30s before the 5-minute expiry, and de-dupes concurrent fetches so parallel renders share one request. A failed request throws rather than silently making an unauthenticated call, and does not poison the cache.req()choke point intenant-registry.ts, so all 10 call sites are covered at once.Realm side is already done:
portal-svcclient created with the tenant-registry-audience scope; a live token was verified to carryaud: [tenant-registry, account]with a 300s TTL. Env staged in sharang/orca-infra#56 (secret already set via orca secrets).9 new tests; full suite 79 green; typecheck, lint (--max-warnings 0) and production build all clean.
🤖 Generated with Claude Code