Skip to content

Security invariants ​

These rules are enforced by the Central Auth Gateway. Platform integrations must respect them; bypassing them in the BFF creates security holes even if Central itself remains correct.

Identity and tokens ​

  • The IESF identity service (identity-api) owns credentials, e-mail verification, identity sessions, and OIDC tokens. Each environment is a separate issuer with its own users and signing keys.
  • Users are keyed by (issuer, subject). E-mail is never an identity key; the same address in DEV and PROD is two users.
  • Platforms never receive identity ID, access, or refresh tokens from Central. The gateway keeps the ID token only encrypted on the gateway session, as id_token_hint for logout.
  • PKCE verifiers and nonces never leave Central (stored hashed / encrypted; not returned on exchange).
  • Externally supplied ID tokens are rejected (Bearer tokens carrying nonce are refused); only the central code exchange path establishes identity for login.
  • Bearer access tokens are accepted only from active identity environments: iss selects the environment, the signature is checked against that environment's JWKS, and the audience must equal IDENTITY_ACCESS_TOKEN_AUDIENCE.
  • Tokens and handoff codes must never be logged.

Handoff ​

  • Codes are prefixed handoff_, stored as SHA-256 only, short-lived (AUTH_HANDOFF_TTL_SECONDS, 30–300s), platform-bound, and atomically single-use.
  • Exchange requires platform Basic auth with a BFF key (client_id + scrypt-verified secret) of an active platform; a backend key gets 403.
  • A code can only be exchanged with a BFF key whose pair has the callback the code was sent to; a code of another platform or callback gives 404. A leaked key of a development pair therefore cannot redeem production logins.
  • The pending gatewayToken is AES-256-GCM encrypted at rest and revealed only after successful exchange.

Gateway session ​

  • Opaque random token; only the hash is stored.
  • Cookie: HttpOnly, SameSite=Lax, Path=/, Secure in production; name from AUTH_SESSION_COOKIE_NAME (default iesf_auth_session).
  • TTL from AUTH_SESSION_TTL_SECONDS (default 43200).
  • Inactive / soft-deleted users cannot authenticate a session.
  • Permissions are a snapshot taken at login. Removing a role strips the permissions from active sessions immediately; a granted role takes effect at the next login.
  • POST /v1/auth/logout revokes the session and clears the cookie; it is idempotent. Its optional returnTo is checked against the redirect pairs of the platform whose login created the session and travels through the identity provider only as an AES-GCM encrypted, 1-hour state; /v1/auth/logged-out never redirects to an unvalidated URL.
  • The platform callback carries only the handoff code, no platform state; platforms protect themselves against login CSRF with their own short-lived "login pending" cookie set before redirecting to start.
  • Cookie-authenticated writes (including logout) require an allowlisted Origin: an entry in CORS_ORIGINS, the host of the deployment issuer or of an active identity environment's issuer (the identity UI, which renders central onboarding), or the origin of a redirect pair's return URL on an active platform. Database origins are cached for 60 seconds, so deactivating a platform or an environment revokes its origin within that window.
  • Because the cookie is SameSite=Lax, only origins that are same-site with the gateway can send it at all. Allowlisting a cross-site platform origin does not grant it cookie access; those platforms use the BFF handoff instead.

Platform registry ​

  • Redirect pairs (return URL root → callback), the identity environment and onboarding mode come from the server-side registry, not from untrusted query overrides. The matched callback and the environment are snapshotted on the transaction at start.
  • Only the deployment (PROD) identity environment can grant gateway administration permissions; a gateway session from one environment cannot resume a login started in another.
  • OIDC client secrets of database-managed environments are AES-256-GCM encrypted with IDENTITY_ENVIRONMENT_SECRET_KEY, bound to their row, write-only through the API and redacted from logs.
  • A database environment can never reuse the deployment issuer.
  • Platforms on a production environment only accept public HTTPS return and callback URLs.
  • onboardingMode on start is assertion-only.
  • Active platforms must have at least one return/callback URL pair, a BFF key, and an active identity environment. A login through a pair whose callback has no BFF key is refused at start.
  • Client secrets are high-entropy, returned once, stored hashed (scrypt). Each pair has its own BFF key and a platform at most one introspection-only backend key, so a key can be rotated or revoked without touching the others. A rotated secret stays valid for 60 minutes unless revoked; last_used_at shows which keys are still in use.
  • Soft deactivation preserves memberships, onboarding values, and audit history.

Authorization ​

  • User IDs are never taken from profile/account request bodies for identity.
  • User permissions come only from local user_roles (platform_admin) and only for PROD users. Machine-token scopes count only when the issuer is PROD; machine tokens are refused on user endpoints.
  • platform_admin can be granted only to PROD users, and the last remaining one cannot be removed. PLATFORM_ADMIN_BOOTSTRAP_EMAILS applies only to PROD logins with a verified e-mail.
  • Platform ownership for admin/schema routes is derived from verified token claims/scopes (or platforms:admin), not from trusting a body/path platform key alone.
  • Passwords never reach the gateway; they are managed in the identity UI (accountManagementUrl).
  • Account deletion suspends the identity user through the identity service API first (which also revokes identity sessions and tokens), then soft-deletes locally and revokes gateway sessions; local profile and audit data remain.
  • Gateway → identity-api service calls use short-lived (60 s) HS256 JWTs signed with IDENTITY_SERVICE_KEY and scoped to one environment and action.

Onboarding integrity ​

  • Completion is always recalculated server-side from active required definitions and current values.
  • Resume ignores client completion assertions.
  • Historical onboarding values remain when definitions are archived.
  • Schema definitions cannot carry arbitrary executable / HTML payloads; uiConfig is declarative.

Operational ​

  • Application instances are stateless; auth state lives in PostgreSQL.
  • Do not connect to or migrate the identity service's database (its auth_<env> schemas) from this service; use the internal service API.
  • Generate API clients from /openapi.json; treat Swagger examples as illustrative and verify against runtime for security-sensitive fields.