Markdown Editor

Identity Provider and SSO System Design Template

OIDC/OAuth 2.0 IdP: code flow with PKCE, token lifecycle, refresh rotation, key rotation and JWKS, sessions, MFA and passkeys, recovery, multi-tenant

System DesignFullOpenID Connect Core 1.0 (errata set 2); RFC 9700; NIST SP 800-63-4; WebAuthn Level 3designidentityssoprovidermath

How to use: Use it when you design or replace the login and token service that all your applications trust. Fill the client inventory and the token lifecycle table first, then key rotation and account recovery. The main pitfall is a strong login path with a weak recovery path or long-lived, unrotated refresh tokens.

Preview

System Design: [Identity provider name]

Reference design for an identity provider (IdP) that offers single sign-on to first-party and third-party applications as an OAuth 2.0 authorization server and OpenID Connect issuer. It covers the authorization code flow with PKCE, token formats and lifetimes, refresh token rotation with reuse detection, signing key rotation and the JWKS endpoint, browser sessions and logout, multi-factor authentication with passkeys (WebAuthn), account recovery and multi-tenant issuers. It is based on OpenID Connect Core 1.0 incorporating errata set 2, RFC 6749, RFC 7636 (PKCE), RFC 9700 (OAuth 2.0 Security Best Current Practice, January 2025), NIST SP 800-63-4 (July 2025) and W3C Web Authentication Level 3. It follows the 12-section structure of the System Design Document template. Replace every [bracketed] value and every row marked Example.

Document control

FieldValue
Document ID[SDD-IDP-001]
System / feature[Customer and workforce single sign-on]
Version[0.1]
Status[Draft / In review / Approved / Implemented / Superseded]
Owner[@owner]
Author(s)[@author]
Reviewers[@reviewer-1], [@reviewer-2]
Last updatedYYYY-MM-DD
Classification[Confidential]
Target assurance[Example: NIST SP 800-63B-4 AAL2 for customers, AAL3 for administrators]
Related[Threat model], [Authorization service design], [ADR: token format]

Approvals

RoleNameDecisionDate
Tech lead[@name][Approved / Changes requested]YYYY-MM-DD
Security architect[@name][Decision]YYYY-MM-DD
Privacy / data protection[@name][Decision]YYYY-MM-DD
SRE / operations[@name][Decision]YYYY-MM-DD
Product (sign-in experience)[@name][Decision]YYYY-MM-DD

Revision history

VersionDateAuthorChange summary
0.1YYYY-MM-DD[@name]First draft for design review
0.2YYYY-MM-DD[@name]Example: Refresh token rotation with family revocation added
0.3YYYY-MM-DD[@name]Example: Passkeys as primary factor; SMS removed from recovery
1.0YYYY-MM-DD[@name]Example: Approved

Contents

1. Overview

1.1 Problem statement

[Example: Six products each run their own login, password storage and "remember me" cookies. Users hold several passwords for one company, a credential-stuffing attack last quarter succeeded against the oldest product, and enterprise customers ask for SSO with their own identity provider. Mobile apps still use the resource owner password grant, which current OAuth security guidance forbids.]

1.2 Proposed solution

[Example: One identity provider per region serves every product as an OpenID Connect issuer. Applications redirect to it with the authorization code flow and PKCE; it authenticates the user with a passkey, or a password plus a second factor, keeps a browser session, and issues a short ID token, a 10-minute JWT access token and a rotating refresh token. Keys are rotated on a schedule and published at the JWKS endpoint ahead of use. Enterprise tenants get their own issuer and can federate to their own IdP.]

1.3 Decisions requested from reviewers

IDQuestionRecommended optionSection
Q-01Example: Access token format?JWT per RFC 9068 for first-party APIs; opaque with introspection for third parties7.1, 10
Q-02Example: Refresh token protection for public clients?Rotation with reuse detection; DPoP where the client supports it7.1
Q-03Example: Primary factor?Passkeys first; password plus second factor as fallback7.3
Q-04Example: Multi-tenant issuer model?One issuer URL per enterprise tenant; shared issuer for consumers7.5
Q-05Example: Signing key custody?Keys generated and wrapped by KMS, held in memory of the token service7.2
[Q-nn][Question][Option][Section]

1.4 45-minute interview path

MinutesCoverOutput on the whiteboard
0 to 5Clarify: consumers or workforce, first-party only or third-party apps, MFA, enterprise federationCore and below-the-line lists (2.1, 2.3)
5 to 10Estimate logins/s, refreshes/s, sessions, hashing CPUNumbers with consequences (3.7)
10 to 18Authorization code flow with PKCE as a sequence diagramDiagram from 7.0
18 to 24Endpoints and token types: ID, access, refreshToken lifecycle table (7.1)
24 to 30High-level design: login UI, token service, session store, key serviceOne diagram (6)
30 to 40One deep dive: refresh rotation (7.1) or key rotation (7.2)Options, choice, reason
40 to 45Failure modes: IdP down means nobody logs in; key compromiseTwo rows of 9.1

2. Requirements

2.1 Core functional requirements

IDRequirementPriority
FR-01Example: OpenID Connect authorization code flow with PKCE (S256) for all clientsMust
FR-02Example: Client credentials grant for service-to-service callsMust
FR-03Example: Discovery document and JWKS endpointMust
FR-04Example: Refresh tokens with rotation and reuse detectionMust
FR-05Example: Passkeys (WebAuthn) as primary factor; password plus TOTP as fallbackMust
FR-06Example: Browser SSO session across first-party apps; RP-initiated and back-channel logoutMust
FR-07Example: Token revocation (RFC 7009) and introspection (RFC 7662)Must
FR-08Example: Account recovery without weakening the assurance levelMust
FR-09Example: Enterprise tenants federate to their own OIDC or SAML IdPShould
FR-10Example: Step-up authentication requested by acr_valuesShould
FR-11Example: Consent screen and stored grants for third-party clientsShould
[FR-nn][Requirement][Must / Should / Could]

2.2 Non-functional requirements

IDQualityTargetMeasured as
NFR-01Availability of token endpointExample: 99.99% monthlySuccessful token responses / valid requests
NFR-02Token endpoint latencyExample: p99 at or below 150 ms (excluding password hashing)Server timing
NFR-03Login page latencyExample: p95 at or below 800 ms end to endReal user monitoring
NFR-04AssuranceExample: AAL2 for customers; AAL3 for administratorsAuthenticator inventory review
NFR-05RevocationExample: Refresh revocation effective immediately; access token exposure at most its lifetimeTest
NFR-06Key rotationExample: No failed validations during scheduled rotationRotation drill
NFR-07Credential storageExample: Passwords hashed with a memory-hard function; no reversible secretsSecurity review
[NFR-nn][Quality][Target][Measurement]

2.3 Below the line (out of scope)

  • [Example: implicit flow and resource owner password credentials grant (not offered; RFC 9700 §2.1.2 and §2.4)]
  • [Example: device authorization grant for TVs (phase 2)]
  • [Example: identity proofing of real-world identity (handled by a proofing vendor if ever needed)]
  • [Example: fine-grained permissions (the authorization service decides; the IdP only authenticates)]
  • [Example: SCIM user provisioning from enterprise directories (phase 2)]
  • [Item deferred, with the release that will take it]

3. Capacity estimation

3.1 Assumptions

InputSymbolValueSource
Registered accountsAAExample: 50 millionUser database
Daily active usersDDExample: 5 millionProduct analytics
Interactive logins per active user per dayllExample: 2Session logs
Peak-to-average factor for loginsplp_lExample: 5Morning peak
Active hours per active user per dayuuExample: 4Session logs
Access token lifetimetat_aExample: 10 min7.1
Peak-to-average factor for refreshesprp_rExample: 3Traffic shape
Share of logins verifying a passwordqqExample: 0.6Factor mix
CPU time per password verificationccExample: 50 msBenchmark of the chosen hash parameters

3.2 Interactive logins

Lpeak=D×l86 400×pl=5×106×286 400×5≈116×5=580 logins/sL_{peak} = \frac{D \times l}{86\,400} \times p_l = \frac{5 \times 10^{6} \times 2}{86\,400} \times 5 \approx 116 \times 5 = 580 \ \text{logins/s}

Consequence: the login front end is small; capacity planning focuses on the hashing pool below.

3.3 Password hashing CPU

H=Lpeak×q×c=580×0.6×0.05 s≈17.4 coresH = L_{peak} \times q \times c = 580 \times 0.6 \times 0.05 \ \text{s} \approx 17.4 \ \text{cores}

Consequence: with 40% headroom, about 24 cores at peak. A credential stuffing attack at 10 times the normal rate would need 240 cores, so rate limiting and bot detection run before the hash, not after it, and the hashing pool is isolated so an attack cannot starve token refreshes.

3.4 Token refreshes

Rpeak=D×u×60ta86 400×pr=5×106×4×686 400×3≈1 389×3≈4 170 refreshes/sR_{peak} = \frac{D \times u \times \frac{60}{t_a}}{86\,400} \times p_r = \frac{5 \times 10^{6} \times 4 \times 6}{86\,400} \times 3 \approx 1\,389 \times 3 \approx 4\,170 \ \text{refreshes/s}

Consequence: each refresh with rotation is a conditional write (mark the old token used, insert the new one). Over 4 000 writes/s needs a store that handles conditional writes at that rate per region, so refresh tokens get their own table keyed by token hash (5.3).

3.5 State size

DataCountBytes eachTotalConsequence
Browser sessionsExample: 10 million1 KB10 GBFits in an in-memory store with replicas
Refresh token familiesExample: 20 million400 B8 GBSingle table, TTL-based expiry
Passkey credentialsExample: 30 million600 B18 GBOrdinary indexed table
Audit eventsExample: 50 million/day500 B25 GB/daySeparate append-only pipeline

3.6 Signing and key distribution

Peak signatures are about 580+4 170≈4 750580 + 4\,170 \approx 4\,750/s (one ID or access token set per login or refresh, counted once). Elliptic-curve signatures take well under a millisecond each, so a handful of cores suffice. Consequence: sign in the token service process with keys unwrapped from KMS at start-up, rather than calling a remote KMS per token, which would add latency and hit KMS rate limits. JWKS fetches by resource servers are cached for minutes and served from a CDN.

3.7 Summary

QuantityEstimateDesign consequence
Logins~580/s peakSmall login tier
Password hashing~24 cores peakIsolated hashing pool; rate limit before hashing
Refreshes~4 170/s peakDedicated refresh store with conditional writes
Sessions~10 GBIn-memory session store per region
Signatures~4 750/sIn-process signing, keys wrapped by KMS

4. API design

4.1 Endpoints

EndpointStandardPurpose
GET /.well-known/openid-configurationOpenID Connect Discovery 1.0; RFC 8414Metadata: endpoints, supported algorithms, grant types
GET /jwks.jsonRFC 7517Public signing keys
GET /authorizeRFC 6749 §3.1; OIDC Core §3.1.2Start authentication; returns code to redirect URI
POST /parRFC 9126Pushed authorization request for confidential clients
POST /tokenRFC 6749 §3.2Exchange code, refresh, client credentials
GET /userinfoOIDC Core §5.3Claims about the user
POST /revokeRFC 7009Revoke refresh or access token
POST /introspectRFC 7662Token state for resource servers using opaque tokens
GET /logoutOIDC RP-Initiated Logout 1.0End session and redirect
Back-channel logout token to clientOIDC Back-Channel Logout 1.0Notify clients that a session ended

Grant types offered: authorization_code (PKCE required, S256 only), refresh_token, client_credentials. Not offered: implicit and resource owner password credentials, per RFC 9700 §2.1.2 and §2.4. Redirect URIs are compared by exact string match (RFC 9700 §2.1), with the loopback port exception for native apps.

4.2 Request and response examples

The authorization request carries a PKCE challenge, state for CSRF protection and nonce to bind the ID token to the browser session.

GET /authorize?response_type=code&client_id=web-app
  &redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback
  &scope=openid%20profile%20email&state=af0ifjsldkj&nonce=n-0S6_WzA2Mj
  &code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
  &code_challenge_method=S256 HTTP/1.1
Host: id.example.com
POST /token HTTP/1.1
Host: id.example.com
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA
&redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback&client_id=web-app
&code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk

HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store

{"access_token": "eyJhbGciOiJFUzI1NiIsImtpZCI6IjIwMjYtMTAifQ...",
 "token_type": "Bearer", "expires_in": 600,
 "refresh_token": "rt_8xLOxBtZp8", "id_token": "eyJhbGciOiJFUzI1NiJ9..."}

Example ID token payload (claims defined in OIDC Core §2):

{"iss": "https://id.example.com", "sub": "u_24400320", "aud": "web-app",
 "exp": 1792666800, "iat": 1792666500, "auth_time": 1792666490,
 "nonce": "n-0S6_WzA2Mj", "acr": "urn:example:aal2", "amr": ["hwk", "user"]}

4.3 Errors

Token endpoint errors use the codes from RFC 6749 §5.2 (invalid_grant, invalid_client, unauthorized_client, unsupported_grant_type, invalid_scope). Return invalid_grant for a reused refresh token and revoke its family (7.1); never reveal whether a code or token existed.

5. Data model

5.1 Entities

Accounts own credentials and sessions; clients own grants; signing keys are global per issuer.

5.2 Storage choices

DataStoreWhy
Accounts, credentials, clients, consentsExample: Relational database, primary per region with replicasStrong consistency, audited changes
Sessions, authorization codesExample: In-memory replicated store with TTLShort-lived, high read rate
Refresh familiesExample: Key-value store with conditional writes and TTL4 000+ rotations/s
Signing keysKMS-wrapped key material plus metadata tableCustody and rotation schedule
Audit eventsAppend-only log to the security data platformInvestigation; never on the request path

5.3 Access patterns

IDQueryKey or indexPartition key rationale
AP-01 (Example)Find account by login identifierUnique index (tenant_id, normalized email)Tenant-scoped uniqueness
AP-02 (Example)Load passkey by credential ID during WebAuthn assertionPrimary key credential_idCredential ID arrives from the authenticator
AP-03 (Example)Redeem authorization code onceKey = hash(code), delete on readSingle use enforced atomically
AP-04 (Example)Rotate refresh tokenKey = hash(token) to family; conditional update on family_idFamily is the unit of revocation
AP-05 (Example)Revoke all sessions and families of an accountIndex account_id on SESSION and REFRESH_FAMILYNeeded on password reset and compromise
AP-06 (Example)Validate client and redirect URIPrimary key client_id, cachedEvery authorize call
AP-07 (Example)Publish keysSIGNING_KEY by tenant and stateRarely changes; served from cache

6. High-level design

ComponentResponsibilityScales by
EdgeTLS, bot scoring, per-IP and per-account login limitsEdge provider
Login and consent UIRender sign-in, step-up, consent and recovery pagesStateless replicas
Authenticator serviceVerify passkeys, passwords, TOTP; apply lockout rulesStateless; hashing pool separate
Authorization and token serviceValidate clients, issue codes and tokens, rotate refresh tokensStateless replicas
Key serviceGenerate, schedule and retire signing keysSingle leader per region
Federation brokerAct as relying party to enterprise IdPs and map claimsPer tenant configuration

7. Detailed design

7.0 Authorization code flow with PKCE

Every client, public or confidential, uses this flow for user sign-in. The IdP stores the code challenge with the code and accepts the code only once, only from the same client, and only with the matching verifier.

7.1 Deep dive: token lifecycle and refresh rotation

TokenFormatLifetime (Example)Bound toRevocation
Authorization codeOpaque, stored as hash60 s, single useClient, redirect URI, PKCE challengeConsumed on first use
ID tokenSigned JWT5 minClient (aud), nonceNot revoked; short-lived
Access tokenJWT per RFC 9068, or opaque10 minAudience (resource server), optionally DPoP keyExpiry; introspection for opaque
Refresh tokenOpaque, stored as hash30 days absolute, 7 days idleClient, session, familyFamily revocation
Option for public-client refresh tokensVerdict
A. Long-lived refresh token, no rotationRejected: RFC 9700 §2.2.2 requires sender-constraining or rotation for public clients
B. Rotation with reuse detectionChosen as baseline
C. Sender-constrained with DPoP (RFC 9449)Chosen in addition where the client library supports it

Choice and reason. Every refresh returns a new refresh token and marks the old one used, in one conditional write on the family. If a used token is presented again, either an attacker or the legitimate client holds a stolen copy; the IdP cannot tell which, so it revokes the whole family and the session's other families for that client, forcing a new login. Allow a grace window of a few seconds in which the same old token returns the same new token, to tolerate a retry after a network failure without triggering revocation.

7.2 Deep dive: signing key rotation and JWKS

OptionHowVerdict
A. One key, rotate on incident onlySimple; a compromise forces emergency rotation that breaks clientsRejected
B. Scheduled rotation with pre-publicationNew key published in JWKS before use; old key kept for verification after retirementChosen
C. Per-token remote signing in KMSStrong custody; latency and rate limits at 4 750 signatures/sRejected for tokens; used for key wrapping

Choice and reason. Keys move through states: pending (published in JWKS, not used), active (signs new tokens), retired (still published, verifies tokens until they expire), removed. Publish a pending key at least one JWKS cache lifetime plus a margin before activation, so every resource server has fetched it. Keep the retired key published for at least the longest token lifetime it signed. Resource servers select the key by kid and refetch JWKS once when they see an unknown kid, with a rate limit on refetches. For a compromised key, skip the pending period, remove the key, and accept that tokens it signed are rejected.

Step (Example schedule, 90-day rotation)Day
Generate key N+1, publish as pending0
Activate N+1; key N becomes retired2
Remove key N from JWKS3 (after the longest lifetime it signed, plus margin)

7.3 Deep dive: sessions, MFA, passkeys and step-up

  • Authenticators. Offer passkeys (WebAuthn) as the primary factor. NIST SP 800-63B-4 requires verifiers at AAL2 to offer at least one phishing-resistant option, allows syncable authenticators (synced passkeys) up to AAL2, and requires non-exportable keys at AAL3. Use device-bound keys (security keys or platform keys flagged as not backup eligible) for administrators who need AAL3.
  • Passwords as fallback. Follow SP 800-63B-4 §3.1.1: minimum 15 characters when the password is the only factor, 8 when it is one factor of MFA, a blocklist of common and breached values, no composition rules, and no forced periodic changes. Hash with a memory-hard function; tune parameters to the CPU budget in 3.3.
  • Throttling. Limit consecutive failures per account; SP 800-63B-4 §3.2.2 caps them at 100. Combine with per-IP and per-device limits at the edge so throttling does not become a lockout weapon.
  • Sessions. The browser session cookie is Secure, HttpOnly, SameSite=Lax, host-only. Apply an idle timeout and an absolute timeout; for AAL2 SP 800-63B-4 recommends reauthentication at least every 24 hours and after at most 1 hour of inactivity. A 30-day refresh token outlives that window, so for clients that claim AAL2 for their own sessions, cap the refresh family at the same absolute limit or require reauthentication with max_age.
  • Step-up. A client asks for a higher level with acr_values or max_age. The IdP compares the session's acr and auth_time with the request and reauthenticates if needed, then reports acr, amr and auth_time in the ID token so the client can verify.

7.4 Deep dive: account recovery

OptionAssuranceVerdict
A. Email link onlyEquals the security of the mailboxAllowed only for accounts without MFA
B. SMS codeVulnerable to SIM swapRejected for recovery
C. Recovery codes issued at enrolment, or a second registered passkeyEquals the original factorsChosen
D. Support-assisted recovery with identity verification and a waiting periodSlower; resists social engineeringChosen as last resort

Choice and reason. Recovery must not be easier than login. On every recovery: notify all verified channels, revoke existing sessions and refresh families (AP-05), apply a cooling-off delay before sensitive changes such as adding a new passkey, and log the event for the security team.

7.5 Multi-tenancy and federation

Consumers share one issuer. Each enterprise tenant gets its own issuer URL (https://id.example.com/t/{tenant}), its own signing keys and its own client registrations, so a token from one tenant can never validate for another and a tenant's key can be rotated alone. For tenants that federate, the broker acts as a relying party to their IdP, maps claims to local accounts by a stable subject identifier (never by email alone), and applies the tenant's MFA policy. See Multi-Tenancy Design Template (architecture/multi-tenancy-design).

How real systems do it: Keycloak

The open-source Keycloak server organises identity into realms: each realm is an isolated space with its own users, credentials, roles and client applications, which is the same per-tenant isolation as 7.5. Its Server Administration Guide (version 26.8.1) explains that each realm signs with one active key pair while older passive keys stay available to verify earlier signatures, which lets administrators rotate without downtime. The guide's suggested procedure is to add a new key with a higher priority so it becomes active, keep the previous key passive for a transition period, and remove it later; it suggests rotation every few months and warns that sessions signed with a removed key need reauthentication. This box paraphrases the public documentation; read it for the exact settings.

8. Scalability and performance

ConcernApproachLimit before redesign
Login burstsStateless UI, edge rate limitsHashing pool size
Credential stuffingBot scoring and limits before hashingEdge capacity
Refresh writesDedicated store, conditional writesExample: Store write throughput per region
Token validationLocal JWT verification at resource serversNone at the IdP
IntrospectionOnly for opaque third-party tokens; cached for secondsIntrospection rate
JWKSCDN with Cache-Control: max-age of minutesNone

Latency budget for POST /token with refresh grant (target p99 150 ms):

StepBudget
TLS and client authentication20 ms
Load family and conditional rotate40 ms
Session check (still valid, not revoked)15 ms
Sign tokens5 ms
Audit event enqueue5 ms
Headroom65 ms

9. Reliability, security and operations

9.1 Failure modes

ComponentFailureDetectionBlast radiusMitigationRTO
Token serviceExample: Region downSynthetic login probesNew logins and refreshes in regionActive-active regions; clients keep valid access tokens until expiry5 min
Session storeExample: Data lossError rate, session count dropUsers must log in againReplicas; treat loss as forced logout, never as anonymous access15 min
Refresh storeExample: UnavailableWrite errorsRefresh fails, users re-authenticateMulti-AZ; fail closed (no refresh without rotation record)15 min
Signing keyExample: Suspected compromiseSecurity alertAll tokens signed with the keyEmergency rotation runbook; remove key from JWKS1 h
JWKS / CDNExample: Stale cacheUnknown-kid errors at resource serversTokens from new key rejectedPre-publication window; refetch on unknown kid15 min
Federation brokerExample: Tenant IdP downFederation error rate per tenantThat tenant's usersShow status page; no fallback to weaker loginDepends on tenant
Hashing poolExample: Saturated by attackQueue depthPassword logins slowEdge limits; passkey logins unaffected10 min
[Component][Failure][Signal][Scope][Mitigation][RTO]

9.2 Security

ThreatControl
Authorization code interceptionPKCE S256 required; exact redirect URI match; codes single use
Mix-up and CSRF on callbackstate checked; iss returned in the authorization response (RFC 9207)
Stolen refresh tokenRotation with reuse detection; DPoP binding where supported
Phishing of credentialsPasskeys are origin-bound; phishing-resistant option offered at AAL2
Credential stuffingBreached-password blocklist, edge limits, per-account throttling
Token replay at the wrong APIaud restricted per resource server (RFC 9700 §2.3)
Insider access to keysKeys wrapped by KMS, access audited, two-person rule for key export
Account takeover via recovery7.4 controls; notification on every recovery
[Threat][Control]

Run the full analysis in Threat Model Template (security/threat-model).

9.3 Observability

SignalMetricAlert threshold
Login success ratePer method and tenantExample: Drop of 10 points vs. 7-day baseline
Failed loginsPer IP range and per accountSpike above baseline x 5
Refresh reuse detectionsCount per clientAny sustained increase
Unknown kid reportsFrom resource server librariesAny after rotation
Token endpoint latencyp99Above 150 ms for 10 min
Recovery eventsPer dayAbove baseline x 3

9.4 Cost estimate

ItemUnit costQuantityMonthly cost
Token and UI services[USD per instance-month][Count][USD]
Hashing pool[USD per core-month]Example: 24 cores peak, autoscaled[USD]
Session and refresh stores[USD per node-month][Count][USD]
KMS / HSM[USD per key and per operation][Keys, operations][USD]
SMS and email for notifications[USD per message][Volume][USD]
Total[USD]

10. Trade-offs and alternatives

DecisionOptionsChosenWhyWhat we give up
Build or adoptBuild; open-source server; managed IdP[Decision][Reason][Cost]
Access token formatJWT; opaque with introspectionJWT for first-party, opaque for third-partyLocal validation at scale; revocable where it mattersJWT cannot be revoked before expiry
Refresh protectionRotation; DPoP; mTLS (RFC 8705)Rotation plus DPoP where possibleWorks for browsers and mobileRetry handling complexity
Primary factorPassword + OTP; passkeysPasskeys firstPhishing resistance, fewer resetsDevice and recovery edge cases
Tenant modelShared issuer with tenant claim; issuer per tenantIssuer per enterprise tenantStrong isolation, per-tenant keysMore metadata and keys to manage
At 10x (50 million DAU)Refresh store becomes the hot spot: partition by family, consider longer access tokens with DPoP; dedicated regions for large tenants
At 0.1x (500 000 DAU)A managed IdP or a single open-source cluster is enough; skip custom key service and use the product's rotation

11. Delivery plan and risks

11.1 Milestones

MilestoneScopeExit criterionTarget date
M1Issuer, code flow with PKCE, passwords, TOTPConformance test suite for OpenID Connect passesYYYY-MM-DD
M2First product migrated; refresh rotationReuse detection drill passesYYYY-MM-DD
M3Passkeys and recoveryRecovery abuse test passesYYYY-MM-DD
M4All products; password grant removedNo product uses legacy loginYYYY-MM-DD
M5Enterprise tenants and federationFirst tenant liveYYYY-MM-DD

11.2 Migration and rollback

[Example: Existing password hashes are imported with their algorithm tag and re-hashed with the new function on next successful login. Each product switches login with a feature flag; rollback returns to its legacy login while keeping IdP accounts in sync for 30 days.]

11.3 Risks

IDRiskLikelihoodImpactMitigationOwner
R-01Example: IdP outage blocks all productsMediumHighActive-active regions; game days[@name]
R-02Example: Mobile apps cannot drop the password grant on timeMediumMediumNative SDK with system browser and PKCE[@name]
R-03Example: Passkey recovery generates support loadHighMediumEncourage two passkeys; recovery codes[@name]
R-04Example: Key rotation breaks third-party clients that pin keysMediumHighDocument JWKS usage; pre-publication window[@name]
[R-nn][Risk][L / M / H][L / M / H][Mitigation][@name]

12. Appendices

12.1 Traceability

RequirementDesign elementVerification
FR-014.1, 7.0OpenID Connect conformance tests
FR-04, NFR-057.1Reuse detection test
FR-05, NFR-047.3Authenticator inventory review
FR-087.4Recovery abuse test
NFR-067.2Rotation drill

12.2 Open questions

IDQuestionOwnerDue
OQ-01Example: Do we accept synced passkeys for administrators?[@name]YYYY-MM-DD
OQ-02Example: When do we adopt OAuth 2.1 once it is published as an RFC?[@name]YYYY-MM-DD
[OQ-nn][Question][@name]YYYY-MM-DD

12.3 Glossary

TermMeaning
IdPIdentity provider: authenticates users and issues identity assertions
Relying party (RP)Application that trusts the IdP's tokens
PKCEProof Key for Code Exchange: binds the code to the client instance
JWKSJSON Web Key Set: the published public keys
AALAuthenticator assurance level in NIST SP 800-63B-4
acr / amrAuthentication context class and methods reported in the ID token
Refresh familyChain of refresh tokens descending from one login
DPoPDemonstrating Proof of Possession: binds tokens to a client key

12.4 References

ReferenceUse
OpenID Connect Core 1.0 incorporating errata set 2 (15 December 2023)ID token, claims, flows
OpenID Connect Discovery 1.0; RP-Initiated Logout 1.0; Back-Channel Logout 1.0Metadata and logout
RFC 6749 OAuth 2.0; RFC 7636 PKCE; RFC 7009 Revocation; RFC 7662 IntrospectionCore protocol
RFC 9700 Best Current Practice for OAuth 2.0 Security (BCP 240, January 2025)Grant restrictions, redirect matching, refresh protection
RFC 9068 JWT access tokens; RFC 9449 DPoP; RFC 9126 PAR; RFC 9207 issuer identifier; RFC 7517 JWK; RFC 8414 metadata; RFC 8176 amr valuesToken and transport details
NIST SP 800-63-4 Digital Identity Guidelines (final, July 2025), part SP 800-63B-4Assurance levels, passwords, throttling, reauthentication
W3C Web Authentication Level 3 (W3C Recommendation, 25 August 2026)Passkeys
Keycloak Server Administration Guide 26.8.17.5 box
OAuth 2.1 Internet-Draft (draft-ietf-oauth-v2-1)Direction of travel; still a draft

Standard and licence note

Based on OpenID Connect Core 1.0 incorporating errata set 2, the IETF OAuth RFCs listed in 12.4 (including RFC 9700, January 2025), NIST SP 800-63-4 (July 2025) and W3C Web Authentication Level 3. This is a reference design with illustrative numbers; it is not a conformance claim. Specifications and NIST guidance are revised; check the current official versions, including the status of the OAuth 2.1 draft, before implementing. Original text written for this collection. Template licence: MIT.

Related templates