System design template

Authentication service architecture.

Stateless tokens make verification free and revocation hard, and that single tradeoff explains most of the components in this diagram.

Download for macOS
v0.1.33 · .dmg · Apple Silicon & Intel
Signed & notarized by Apple · opens without a Gatekeeper warning
sha256 698955a0187bc039f4c74f5d05a9f10fbb27376a45788a0a241d1326b73873c7
Download for Windows instead
$curl -fsSL https://lucidtrain.com/install.sh | sh
Authentication service architecture. 12 components across 5 tiers.
Authentication service architecture. Rendered by the same ELK layout engine the app runs: the agent emits components, tiers and edges, and the engine places them, so the boxes cannot overlap.

The components

Every row below is read from the graph that produced the diagram above, so the two cannot disagree.

ComponentTierWhy it is there
Client appClientSupporting component
API gatewayEdgeVerifies the access token on every request
Auth serviceApplicationSupporting component
OAuth / OIDCApplicationAuthorization code with PKCE
MFA serviceApplicationSupporting component
Session serviceApplicationRefresh token rotation, reuse detection
Key serviceApplicationRotating signing keys, published as JWKS
Audit logApplicationAppend only, separate from application data
PostgresDataArgon2 hashes, never reversible
RedisDataRevocation list, since JWTs cannot be recalled
External IdPsExternalSupporting component
Email providerExternalSupporting component

Design decisions worth arguing about

A diagram shows what was chosen. It does not show what it cost, and that is usually the part that matters in a review or an interview.

Stateless access tokens, and the revocation problem they create

A signed JWT lets the gateway verify a request with no network call, which is why the pattern is everywhere. The consequence is that you cannot un-issue one: a token stolen or a user disabled remains valid until it expires. Every mitigation is a way of buying back some statefulness, and the honest version is short expiry plus a revocation list checked for the cases that matter, which reintroduces exactly the lookup the design was avoiding.

Short access tokens, rotating refresh tokens

Access tokens measured in minutes keep the revocation window small; refresh tokens measured in weeks keep users logged in. Rotating the refresh token on every use adds theft detection almost for free: if an old refresh token is presented again, either it was replayed or a legitimate client raced, and either way the family should be invalidated. The cost is state per token family and occasional logouts for clients that genuinely raced.

Key rotation, published rather than distributed

Signing keys have to rotate, and pushing new keys to every verifier is a coordination problem you do not want during an incident. Publishing a JWKS endpoint and letting verifiers fetch and cache turns rotation into a deploy of one service. It makes that endpoint availability-critical for the whole system, which is a real cost and the reason it should be cached aggressively with a stale fallback.

PKCE everywhere, not only on mobile

The authorization code flow with PKCE was introduced for clients that cannot keep a secret, and it is now the right default for confidential clients too, because it closes code interception regardless of client type. It costs a little client complexity and removes a class of attack entirely, which is a trade worth making by default rather than by threat model.

How it changes with scale

Login volume is small relative to verification volume, and verification is where the design pays off: a stateless check scales with your services rather than with this one. What grows is the session store, which holds refresh state and revocations, and the audit log, which is append-only and retained for compliance rather than for utility.

Where it breaks first

Clock skew, which is invisible until it is not. Token validity is time-bounded, so a verifier whose clock drifts rejects valid tokens or accepts expired ones, and the symptom is intermittent authentication failures on a subset of hosts with nothing obviously wrong. Anyone who has debugged it once puts NTP monitoring in place afterwards.

Draw this yourself

Open the Diagram tab and describe the system. The agent emits a semantic graph rather than coordinates, so you can edit the components and the layout re-solves instead of drifting.

shell
$ Diagram an authentication service: OAuth and OIDC with PKCE, MFA, session service with refresh token rotation, a JWKS key service the gateway verifies against, Postgres for users, Redis for revocation, and external identity providers.

When the shape is right, Implement in code turns the canvas into a markdown specification, every component, every relationship and the notes, and starts a real turn in the Code tab with it.

FAQ

Questions about this design

Should you build authentication or buy it?

Buy, unless authentication is your product. The surface here is large, the failure modes are severe, and the parts that look simple, password reset and account recovery, are where most real compromises happen. This diagram is more useful for understanding what a provider is doing on your behalf than as a build plan.

Sessions or JWTs?

Server-side sessions are simpler and revoke instantly, at the cost of a lookup per request. JWTs remove the lookup and make revocation hard. For a single application, sessions are usually the better and less fashionable answer.

Where should tokens be stored in a browser?

An httpOnly, Secure, SameSite cookie. Local storage is readable by any script on the page, which turns any cross-site scripting bug into full account takeover.

How should passwords be hashed?

Argon2id, or bcrypt where Argon2 is unavailable, with parameters tuned so hashing takes a noticeable fraction of a second on your hardware. The cost is the point: it is what makes offline cracking expensive if the database is ever taken.

More templates