Auth Service — Application Logic
Status: populated Owner: Platform Engineering + Security Last updated: 2026-04-19
Change log
- v1.2 (2026-04-19) — Rebaselined to dispatch logins through the
IdentityProviderport (Keycloak default + tenant OIDC/SAML brokered + Firebase legacy + native fallback). Added SSO start/callback, tenant IdP onboarding, and SCIM use cases.
1. Use Cases
| Use case | Endpoint(s) | Notes |
|---|---|---|
RegisterUserUseCase | POST /v1/users (admin) | Creates user in an existing account; binds to tenant's default provider |
LoginUseCase | POST /v1/auth/login | Native / legacy only — delegates to nativeProvider or firebase-legacy |
StartSsoUseCase | GET /v1/auth/sso/start | Resolves tenant's primary_provider_id; redirects to Keycloak broker URL |
CompleteSsoUseCase | GET /v1/auth/sso/callback | Exchanges OIDC code at Keycloak; runs IdentityProvider.verifyExternalToken + resolveExternalIdentity; issues platform JWT |
HandleSamlAcsUseCase | POST /v1/auth/saml/{providerId}/acs | Verifies SAML <Response> (via Keycloak broker → our ACS); issues platform JWT |
HandleSamlSlsUseCase | POST /v1/auth/saml/{providerId}/sls | Single-logout propagation |
RefreshTokenUseCase | POST /v1/auth/refresh | Rotates refresh token |
LogoutUseCase | POST /v1/auth/logout | Revokes refresh; for SSO sessions, triggers Keycloak back-channel logout |
IssueApiKeyUseCase | POST /v1/api-keys | Generates raw key, stores hash, returns raw ONCE |
RevokeApiKeyUseCase | DELETE /v1/api-keys/{id} | Soft-delete + publish event |
LookupApiKeyUseCase | GET /v1/api-keys/lookup?hash= | Internal only; Kong custom plugin caller |
GetJwksUseCase | GET /.well-known/jwks.json | Cached 5m; includes all active + next keys |
AssignRoleUseCase | POST /v1/users/{id}/roles | Admin-only |
RotateJwksUseCase | cron + admin manual | Promotes next → active; publishes event |
EnableTotpUseCase | POST /v1/users/me/mfa/totp | Returns provisioning URI (native / Keycloak-managed only) |
VerifyTotpUseCase | POST /v1/auth/mfa/totp/verify | During native login second factor |
ConfigureTenantIdpUseCase | POST /v1/tenants/{tenantId}/identity-providers | Validates OIDC discovery / SAML metadata; calls Keycloak Admin REST to provision broker IdP + mappers; writes tenant_identity_providers; emits auth.idp.configured.v1 |
DisableTenantIdpUseCase | PATCH .../identity-providers/{id} | Emergency disable; emits auth.idp.disabled.v1 |
RemoveTenantIdpUseCase | DELETE .../identity-providers/{id} | Removes from Keycloak + our tables; rejected for default keycloak |
TestTenantIdpUseCase | POST .../identity-providers/{id}/test | Runs a synthetic auth probe |
ScimProvisionUseCase | /scim/v2/Users, /scim/v2/Groups | Accepts SCIM 2.0 CRUD; mirrors into Keycloak via Admin REST |
2. Ports
| Port | Adapter |
|---|---|
UserRepository, AccountRepository, ApiKeyRepository, SessionRepository, TenantIdpRepository, ExternalIdentityRepository | Prisma |
PasswordHasher | argon2 module (native fallback only) |
TokenSigner | jose with Vault-fetched private key |
JwksProvider | In-memory cache with TTL |
IdentityProvider (base port) | Dispatched by IdentityProviderRegistry to a concrete adapter: KeycloakProvider, TenantOidcProvider, TenantSamlProvider, FirebaseLegacyProvider, NativeProvider |
KeycloakAdminClient | keycloak-admin-client — provisioning broker IdPs, mappers, realms |
OidcClient | openid-client — OIDC discovery + token exchange |
SamlClient | @node-saml/node-saml — metadata + <Response> verification |
ScimServer | SCIM 2.0 HTTP handler |
EventPublisher | NATS JetStream |
3. Token Lifecycle
| Token | TTL | Claims (key ones) |
|---|---|---|
| Access JWT | 15 min | sub (userId), account_id, tenant_id, scopes[], roles[], kid |
| Refresh token | 30 d, rotating | Opaque string hashed in DB |
| API key | No expiry unless set | Raw-hash stored; scopes embedded via lookup |
4. Key Rotation
- Admin (or cron) runs
RotateJwksUseCase. - Current
nextkey promoted toactive; oldactivemoved toretiring(still in JWKS for 10 min window). - Tokens signed with new
active.kid. - After 10 min,
retiringkey removed from JWKS. - Event
auth.jwks.rotated.v1emitted; Kong's JWKS cache TTL ensures pickup within 5 min.
5. API Key Lookup (Kong integration)
Flow when a client calls Kong with X-Api-Key: ghasi_live_abc123...:
Kong (custom plugin) → GET /v1/api-keys/lookup?hash=sha256(key) → 200 { accountId, tenantId, scopes, status }
→ Kong caches for 30s
→ Kong injects X-Consumer-Id, X-Account-Id, X-Tenant-Id on upstream
If status != active → Kong returns 401. If lookup endpoint returns 404 → 401. On 5xx from this service → Kong fail-closed.