Identity Provider: Low-Level Design
An identity provider (IdP) is the central authority for authentication in a multi-application ecosystem. It implements the OpenID Connect (OIDC) authorization code flow, issues signed JWT access and ID tokens, manages refresh token rotation, and supports session federation so users authenticate once and access many applications. Getting the security details right is critical and frequently tested in system design interviews.
Requirements
Functional
- Implement OIDC Authorization Code flow with PKCE for public clients
- Issue signed JWT ID tokens and opaque access tokens on successful authentication
- Implement refresh token rotation: each use of a refresh token invalidates it and issues a new one
- Support multiple authentication methods: password, TOTP MFA, passkey (WebAuthn)
- Federate sessions across registered relying party (RP) applications via single sign-on
- Publish OIDC discovery document and JWKS endpoint for RP self-configuration
Non-Functional
- Token issuance latency under 100 ms p99
- JWKS key rotation with zero downtime
- Support 1 million active sessions
Data Model
- users: user_id (UUID), email (TEXT UNIQUE), password_hash (bcrypt), mfa_secret (BYTEA encrypted), webauthn_credentials (JSONB), status (ENUM: active, locked, suspended), created_at
- clients: client_id (UUID), client_secret_hash (TEXT), name, redirect_uris (ARRAY), allowed_scopes (ARRAY), token_endpoint_auth_method (ENUM: secret_basic, none), created_at
- authorization_codes: code (TEXT PRIMARY KEY), client_id, user_id, redirect_uri, scope (TEXT), code_challenge (TEXT), code_challenge_method (TEXT), expires_at (TIMESTAMP), used (BOOL)
- refresh_tokens: token_hash (TEXT PRIMARY KEY), family_id (UUID), user_id, client_id, scope, expires_at, revoked (BOOL), created_at
- sessions: session_id (UUID), user_id, created_at, last_active_at, user_agent, ip_address, active (BOOL)
- signing_keys: kid (TEXT PRIMARY KEY), algorithm (TEXT), private_key (BYTEA encrypted), public_key (BYTEA), active (BOOL), created_at
Core Algorithms
PKCE Verification
During the authorization request, the public client sends a code_challenge (BASE64URL(SHA256(code_verifier))) and code_challenge_method=S256. The IdP stores the code_challenge alongside the authorization code. At the token endpoint, the client presents the code_verifier; the IdP recomputes SHA256, BASE64URL-encodes it, and compares with the stored challenge using a constant-time comparison. This prevents authorization code interception attacks without requiring a client secret.
JWT Token Issuance
ID tokens are RS256-signed JWTs containing: iss (issuer URL), sub (user_id), aud (client_id), exp (now + 3600 s), iat (now), nonce (from authorization request), and standard OIDC claims (email, name, picture). The signing key is selected by the currently active kid. Access tokens are short-lived (15 minutes) and may be opaque or self-contained JWTs depending on whether resource servers can perform local validation.
Refresh Token Rotation and Family Invalidation
Each refresh token belongs to a family_id (UUID assigned at initial issuance). On refresh: mark the presented token as revoked, issue a new token in the same family. If a revoked token is presented again, it indicates token theft: revoke all tokens in the family immediately, forcing re-authentication. This is the refresh token family detection algorithm described in RFC 6819 and OAuth 2.0 Security BCP.
Scalability and Architecture
The IdP runs as a stateless service cluster behind a load balancer. Session lookup is the hot path: sessions are cached in Redis keyed by session cookie value with a TTL matching the session lifetime. Authorization codes are stored in Redis (not Postgres) with a 60-second TTL since they are single-use and short-lived.
- JWKS key rotation: generate a new signing key, publish it to the JWKS endpoint alongside the old key (both active), wait one token lifetime (1 hour) for old tokens to expire, then deactivate the old key. No downtime because RPs cache JWKS and accept any key matching a kid in their cached set.
- Rate limiting at the token endpoint: 5 failed password attempts per minute per IP via a sliding window counter in Redis, with exponential lockout
- WebAuthn ceremonies (registration and assertion) are stateless except for a challenge stored in Redis with a 5-minute TTL
- Session federation: when a user authenticates at the IdP, a session cookie is set on the IdP domain. When an RP initiates an OIDC flow and the IdP detects a valid session, it skips the login prompt and issues tokens directly (silent SSO)
API Design
OIDC Standard Endpoints
GET /.well-known/openid-configuration— discovery document listing all endpoint URLs and supported featuresGET /.well-known/jwks.json— public keys for JWT verification, cached by RPs with a 1-hour TTLGET /authorize?response_type=code&client_id=X&redirect_uri=X&scope=X&state=X&code_challenge=X&code_challenge_method=S256— starts the authorization code flowPOST /token— exchanges authorization code or refresh token for tokensPOST /token/revoke— revokes an access or refresh tokenGET /userinfo— returns claims for the authenticated user (Bearer token required)
Session Management (IdP UI)
GET /account/sessions— list active sessions for the current userDELETE /account/sessions/{session_id}— revoke a specific session (remote logout)
Interview Tips
Common interview probes: why PKCE over client secrets for SPAs (client secrets cannot be kept confidential in browser code); how to handle logout across federated RPs (front-channel logout: IdP sends a logout request to each RP logout URL; back-channel logout: IdP POSTs a signed logout token to RP back-end); and how JWKS caching interacts with key rotation (RPs must re-fetch JWKS on encountering an unknown kid, with a maximum refetch rate to prevent DoS). Discuss token storage recommendations: ID tokens in memory, refresh tokens in HttpOnly Secure cookies for web clients.
{
“@context”: “https://schema.org”,
“@type”: “FAQPage”,
“mainEntity”: [
{
“@type”: “Question”,
“name”: “How does the OIDC authorization code flow with PKCE work?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “The client generates a random code_verifier, hashes it to a code_challenge (S256), and sends the challenge with the authorization request. The IdP stores the challenge, issues an auth code, and redirects back. The client exchanges the code plus the original code_verifier for tokens. The IdP verifies that SHA256(code_verifier) matches the stored challenge, preventing auth code interception attacks without requiring a client secret.”
}
},
{
“@type”: “Question”,
“name”: “How does an identity provider issue JWT tokens?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “After authenticating the user, the IdP builds a JWT with standard claims (sub, iss, aud, exp, iat) plus application-specific claims (roles, tenant). It signs the token with an RSA or EC private key and publishes the corresponding public key at a JWKS endpoint. Resource servers validate incoming tokens by fetching the JWKS, verifying the signature, and checking exp and aud without contacting the IdP on every request.”
}
},
{
“@type”: “Question”,
“name”: “What is refresh token rotation and why is it important?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “Refresh token rotation issues a new refresh token on every use and immediately invalidates the previous one. If a stolen refresh token is used, the legitimate client's next refresh attempt will fail (old token already invalidated), alerting the system to a possible compromise. The IdP can then revoke the entire token family. This limits the window of exposure compared to long-lived static refresh tokens.”
}
},
{
“@type”: “Question”,
“name”: “How does session federation work across multiple applications in an identity provider?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “Session federation relies on a shared IdP session cookie scoped to the IdP domain. When a user authenticates to App A, the IdP sets this cookie. When the user navigates to App B, App B redirects to the IdP, which detects the existing session cookie and issues tokens for App B without prompting for credentials again. Each app's access token is scoped independently, but they all share the same upstream session lifetime.”
}
}
]
}
See also: Scale AI Interview Guide 2026: Data Infrastructure, RLHF Pipelines, and ML Engineering
See also: Atlassian Interview Guide