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.
See also: Scale AI Interview Guide 2026: Data Infrastructure, RLHF Pipelines, and ML Engineering
See also: Atlassian Interview Guide
See also: Shopify Interview Guide