Skip to main content

Auth Service — Service Overview

Status: populated Owner: Platform Engineering + Security Last updated: 2026-04-19 Companion: DOMAIN_MODEL · API_CONTRACTS · SECURITY_MODEL · ADR-0001 Kong · 13 Security

Change log

  • v1.2 (2026-04-19) — Rebaselined as a multi-provider identity service. Keycloak is the base/default IdP (also acting as OIDC/SAML broker). auth-service exposes an IdentityProvider port with pluggable providers so that tenant organisations can federate their own corporate IdP (Azure AD, Okta, Google Workspace, ADFS, generic OIDC / SAML 2.0) for SSO. Firebase is retained only as a legacy/optional provider.

1. Purpose

auth-service is the canonical identity surface for Ghasi-SMS-Gateway. It owns user/account lifecycle, API keys, sessions, MFA, RBAC, and publishes JWKS consumed by Kong at the edge.

Crucially, auth-service is not itself a raw IdP. It owns an IdP Provider Abstraction — a pluggable set of concrete providers, all implementing the same internal port. This lets us:

  1. Ship a secure default out of the box (Keycloak) without third-party lock-in.
  2. Let enterprise tenants bring their own IdP for SSO (OIDC or SAML 2.0) without any downstream service caring.
  3. Run multiple providers in parallel (useful during migrations or for hybrid tenants).

Per ADR-0001, Kong performs edge authentication using:

  • JWT plugin validating the platform JWT (issued by auth-service, signed RS256) against auth-service/.well-known/jwks.json.
  • Custom ghasi-api-key-lookup plugin resolving API keys to consumer identities via a cached endpoint here.

Downstream services trust the platform JWT + X-Tenant-Id / X-Account-Id headers Kong injects. They do not see the external IdP token, and they do not know which IdP authenticated the user.

2. Bounded Context

Identity & Access (Generic / commodity). Clear separation from tenant-service (which would own org membership if added; currently IAM is consolidated here). auth-service acts as the bounded context's anti-corruption layer against external IdPs — all external provider claims are normalised into a platform-internal identity model before being persisted.

3. Responsibilities

AreaWhat auth-service owns
Accounts / tenantsTenant-scoped account records and their tenant_identity_providers binding
UsersPer-account user records (operator, customer, admin personas)
External identity linksexternal_identities table linking platform userId(providerId, externalSubject)
Credentials (native)Password hashes (argon2id) — used only when a tenant is on the native provider; TOTP and WebAuthn (future)
SessionsPlatform access JWT issuance + rotating refresh tokens
API keysTenant-scoped key issuance, rotation, revocation, scope
JWKSPublic /.well-known/jwks.json consumed by Kong
RBAC rolesRole catalogue + assignment (enforced in services, not Kong)
IdP provider abstractionPluggable IdentityProvider port + concrete providers (Keycloak, Tenant-OIDC, Tenant-SAML, Firebase-legacy)
External IdP onboardingPer-tenant OIDC discovery or SAML metadata registration, realm/IdP mapper provisioning in Keycloak via Admin REST
SCIM provisioning (enterprise)Inbound SCIM 2.0 endpoint for tenant IdPs that push users/groups
Auditauth.events.* for every security-relevant action, including SSO login, external IdP link/unlink, SCIM changes

4. Non-Responsibilities

AreaOwner
Being the raw OIDC OP / SAML IdPKeycloak (the base provider)
Being an external tenant's own IdPThe tenant organisation
Rate limiting + JWT validation at the edgeKong
Authorization at resource level (account scope checks)Each owning service's guard
Session TLS terminationKong + Cloudflare
SMS MFA deliveryRouted through sms-orchestrator like any other SMS
Secret storageVault (auth-service reads; does not store raw)

5. IdP Provider Abstraction

5.1 Provider port

export interface IdentityProvider {
readonly id: string; // e.g. "keycloak", "tenant-oidc:acme", "tenant-saml:acme", "firebase-legacy"
readonly kind: 'oidc' | 'saml' | 'firebase';
verifyExternalToken(tokenOrAssertion: string): Promise<ExternalIdentityClaims>;
resolveExternalIdentity(claims: ExternalIdentityClaims): Promise<UserLinkResult>;
provisionUserFromClaims(claims: ExternalIdentityClaims, tenantId: string): Promise<User>;
revokeExternalSession?(externalSubject: string): Promise<void>;
}

auth-service dispatches to a provider based on the tenant's tenant_identity_providers binding and the request context (e.g. OIDC redirect from realm acme, SAML AssertionConsumerService callback).

5.2 Concrete providers

Provider IDKindRoleWhen selected
keycloakOIDCBase / default IdP; also the broker for all tenant-federated IdPsEvery tenant by default; mandatory for any tenant using external SSO (Keycloak brokers the external IdP behind this provider)
tenant-oidc:<tenantId>OIDCTenant's own OIDC IdP (Azure AD, Okta, Google Workspace, generic OIDC) brokered via KeycloakTenant admin registered an OIDC discovery URL
tenant-saml:<tenantId>SAML 2.0Tenant's own SAML IdP (ADFS, Azure AD SAML, Okta SAML, generic SAML) brokered via KeycloakTenant admin uploaded SAML metadata
firebase-legacyFirebaseRetained for existing Firebase-based customers onlyTenant flagged as legacy; slated for retirement per migration plan

5.3 How Keycloak is used

Keycloak runs as a managed deployment in the ghasi-identity namespace:

  • One realm per environment (e.g. ghasi-prod, ghasi-staging) for platform users.
  • Per-tenant IdP mappers (inside the same realm) for tenants who federate their own IdP — the tenant's IdP is configured as an "Identity Provider" in Keycloak, and users authenticate via the familiar acme.ghasi.io/realms/ghasi-prod/broker/acme/... flow. This avoids multiplying realms.
  • Admin REST API is the only programmatic surface auth-service uses against Keycloak (provisioning IdPs, mappers, clients).
  • OIDC endpoints (/realms/.../.well-known/openid-configuration, /token, /userinfo, /logout) are used by the customer/admin portal during browser login flows.

See SECURITY_MODEL §1 for the per-provider auth flows.

5.4 Tenant onboarding for external SSO

StepActorAction
1Tenant adminIn admin-dashboardSSO Settings, chooses OIDC or SAML, supplies discovery URL / metadata XML
2auth-serviceValidates metadata, creates a Keycloak IdP mapper via Admin REST, writes the binding to tenant_identity_providers
3auth-service(Optional) Exposes a SCIM 2.0 endpoint for the tenant IdP to push users/groups
4Tenant userSigns in at app.ghasi.io, is redirected through Keycloak → tenant IdP → back → platform JWT issued

6. Kong Integration (critical)

Kong pluginauth-service endpointPurpose
jwt pluginGET /.well-known/jwks.jsonJWKS pulled with ETag + cache; rotated every 30 d — validates the platform JWT, not the external IdP token
ghasi-api-key-lookup (custom)GET /v1/api-keys/lookup?hash={hash} (internal)Returns Kong consumer metadata for a hashed API key
openid-connect (Kong Enterprise, optional)Keycloak discovery URLOnly if we later let Kong itself validate OIDC tokens at the edge; not used today — validation stays inside auth-service

7. Dependencies

DepPurpose
PostgreSQL (auth schema)Users, accounts, api_keys, sessions, rbac_roles, tenant_identity_providers, external_identities, idp_session_audit
KeycloakBase IdP + OIDC/SAML broker for tenant IdPs; own PostgreSQL schema keycloak
RedisSession cache, JWKS cache, API key hash lookup cache, per-tenant IdP config cache
openid-client, @node-saml/node-saml, keycloak-admin-clientOIDC + SAML client libraries
firebase-adminLegacy provider only
VaultSigning key material (RS256 private key), Keycloak admin creds, per-tenant SAML signing keys
NATS JetStreamauth.events.* publication

8. Flow — user login via Keycloak (default)

9. Flow — tenant external SSO (OIDC, brokered)

10. Flow — tenant external SSO (SAML 2.0, brokered)

Same as §9 with SAML-specific differences: Keycloak exposes the tenant's SAML SP endpoints; the tenant IdP posts a signed <Response> to Keycloak's ACS URL; Keycloak applies attribute mappers and then issues its own OIDC code to auth-service. From auth-service's perspective, the flow converges on the same provider=tenant-saml:<tenantId> code path.

11. Status

Foundation service; must be live before Kong + any service depending on JWT validation. Keycloak must be deployed and the default realm provisioned before auth-service starts. External IdP onboarding is a post-GA enterprise capability; the provider abstraction is in place from day one so that no downstream refactor is needed when the first external-SSO tenant lands.