System design interview guide
Auth System Design
A product needs email and password authentication for web and mobile users. People must be able to sign up, log in, stay signed in, reset a forgotten password, optionally use MFA, and sign out from a stolen device. At the same time, bots are trying credential stuffing with leaked passwords, so the design must protect accounts and the login infrastructure.

Problem statement
Design an authentication system for a product with email and password signup. The system should create accounts, verify credentials, issue sessions or token pairs, validate every later API request, support logout and password reset, and optionally require MFA. The main design work is choosing where secrets live, how they expire, how they are revoked, and how the system behaves when attackers try many reused passwords.
Introduction
Authentication is the front door of almost every product. A user types an email and password, expects to get in quickly, and expects old devices to stop working when they sign out or change the password. An attacker sees the same login form as a cheap way to test leaked passwords from other breaches.
That is why this design is about secrets and their lifetimes. A password proves identity during login. A session id or access token proves identity on later requests. A reset token proves identity for one password change. These are all bearer secrets in different forms, and each needs a different storage, expiry, and revoke story.
Weak designs jump straight to "use JWT" or "use OAuth" without explaining how passwords are stored, how login is rate-limited, how reset links expire, or how a stolen laptop gets signed out. Strong designs split the system into a slow path and a fast path. The slow path handles signup, login, MFA, and reset. The fast path validates a session or token on every API request.
The interviewer is listening for operational judgment as much as security vocabulary. Credential stuffing can return HTTP 200 for every attempt and still melt password-hash CPU. A Redis outage can turn into every user being logged out. A long-lived JWT can make instant revoke impossible. Call these tradeoffs out before they are used against you.
How to approach
Walk one user from signup to login, then walk one normal authenticated API request. After that, cover password reset, revoke, MFA, and abuse. This order keeps the design grounded: prove identity once, then prove the session cheaply many times.
Clarifying questions (say these in the room)
You: "Are we designing email and password authentication, social login, or both?"
Interviewer: "Design email and password. You can mention OAuth as an extension."
That answer means your core design must include password hashing, login, sessions, reset, and revoke. OAuth may delegate identity, but it does not remove the need for your app's session model.
You: "Which clients do we support: browser, mobile app, third-party API clients, or all of them?"
Interviewer: "Browser and mobile app are enough."
That answer affects token storage. Browser sessions usually prefer HttpOnly cookies. Mobile apps often use bearer access tokens plus refresh tokens stored in the platform keychain.
You: "Do users need instant sign out everywhere when a password changes or a device is lost?"
Interviewer: "Yes, we should support revoking active sessions."
That pushes you toward opaque sessions or a hybrid token design. If you use JWT access tokens, you need short TTLs and refresh-token invalidation so "revoke" is not just a promise on paper.
You: "Is MFA required for everyone, or optional based on user setting and risk?"
Interviewer: "Optional, but the design should show where MFA fits."
That means login should not issue a full session immediately after password verification when MFA is required. It should issue a temporary challenge state, then create the session after the second factor succeeds.
You: "What attack volume should we expect on login?"
Interviewer: "Assume credential stuffing spikes much higher than normal login volume."
This answer makes rate limiting part of the core design. Hashing every attempt is expensive, so the system must protect hash CPU before it becomes an availability incident.
Minute pacing
| Minute | What to cover | Why it matters |
|---|---|---|
| 0-5 | Clarify clients, session type, revoke needs, MFA, and attack assumptions | These choices decide cookie vs bearer, opaque vs JWT, and fail behavior |
| 5-10 | Capacity sketch: login QPS, session-validation QPS, hash cost, active sessions | Session checks dominate normal traffic; login dominates attack cost |
| 10-18 | Draw signup and login, including rate limit, hash verify, MFA, session issue | This is where most security mistakes happen |
| 18-25 | Draw the fast request path through gateway and session store or JWT verify | Every API call depends on this path staying cheap |
| 25-34 | Deep dive into refresh, revoke, password reset, and account recovery | These flows decide what happens after compromise |
| 34-45 | Abuse, observability, failure, security, cost, and interview tips | This shows you can operate auth under real attacks |
In the room (a fuller opening you can actually say): "I will design email and password auth for web and mobile. Signup stores a salted slow hash, not the password. Login is rate-limited, verifies the hash, optionally completes MFA, and then issues a session. Every later request validates a session id in Redis or a short JWT at the gateway without re-checking the password. Password reset uses a one-time random token stored as a hash, and password change revokes old sessions through deletion or a session-version bump. I will call out where JWT helps and where it makes revoke harder."
Out of scope (and why you park these)
Full identity provider business logic. Enterprise SSO, SAML, SCIM provisioning, and tenant admin policy can be added later, but they would turn this into an identity-platform interview. Keep the base system focused on account credentials and sessions.
Authorization for every product resource. Authentication proves who the user is. Authorization decides what the user may access inside each product service. Mention the boundary, then keep the design on login and session validation.
Device biometrics. Face ID or fingerprint unlocks a local keychain item. It does not replace server-side session validation, revoke, or password reset.
Fraud ML platform. Risk scoring is useful, but the core system can use simple signals such as IP reputation, device id, impossible travel, failed attempts, and new location. Leave model training out unless asked.
Customer support workflow. Support tooling matters, but support must not see passwords, raw reset tokens, TOTP secrets, or recovery codes. Mention audited admin actions and park the workflow.
Capacity estimation
Authentication has two very different traffic shapes. Login is relatively rare and expensive because it runs a slow password hash. Session validation is constant and cheap because every authenticated API call needs it.
| Axis | Interview-scale number | What it means for design |
|---|---|---|
| Users | Millions to tens of millions | Store credentials and session metadata with clear user ids and versioning |
| Login QPS | Normal traffic may be modest; attacks can spike far higher | Rate limits and risk checks protect hash CPU |
| Password hash cost | Tens to hundreds of milliseconds depending on parameters | Never run the hash on every API request |
| Session checks | Orders of magnitude higher than login | Use O(1) Redis lookup, local signature verify, or a hybrid |
| Active sessions | Multiple devices per user | Store device metadata, expiry, refresh family, and revoke state |
| Reset requests | Low normally, high during phishing or attacks | Generic response, rate limits, and token TTL prevent abuse |
The main implication is that session validation must stay boring. If every page load calls the credential database or runs Argon2, the design will not survive. The second implication is that login endpoints need their own protection because attackers can spend your CPU with valid-looking requests.
High-level architecture
The system is easier to reason about when you draw the slow credential path and the fast session path separately.
Signup / login / reset:
[ Client ] --> [ Edge / WAF ] --> [ Auth service ] --> [ User credentials DB ]
| |
| +--> [ MFA / Risk service ]
| |
| +--> [ Session store / Refresh store ]
|
+--> [ Rate limiter ]
Every authenticated request:
[ Client ] --> [ Gateway / Middleware ] --> validate cookie or bearer token
|
+--> opaque session lookup in Redis
| or
+--> JWT signature verify + optional version check
|
v
upstream service receives user_id
The auth service owns credential changes and session issue. The gateway or middleware owns repeated validation. Other services should receive a trusted user_id and security context from the gateway rather than asking for the password again.
For browsers, a common default is an opaque session_id in an HttpOnly Secure SameSite cookie. Redis or a session database maps that id to user_id, expiry, device information, and session version. For mobile, a common design is a short-lived access JWT plus a rotating opaque refresh token. The access token keeps normal calls fast, while the refresh token gives the server a revoke point.
Who owns what:
- Edge and WAF apply coarse IP, device, and geography controls before expensive hash work.
- Rate limiter tracks attempts by IP, device, and normalized email or account id.
- Auth service owns signup, login, MFA handoff, reset, logout, refresh, and credential changes.
- Credentials DB stores user account state and password hash metadata, not plaintext passwords.
- Session store stores active opaque sessions or refresh-token families.
- Gateway validates sessions or access tokens on every request and attaches user context.
- Audit log records security events with no secrets in the payload.
In the room: Say that login proves the password once, then every later request proves a session. That one sentence keeps the architecture from becoming "hash on every call."
Core design approaches
Opaque server-side sessions
An opaque session is a random unguessable id stored in a cookie or bearer header. The server stores the meaning of that id in Redis or a session table: user id, expiry, device, created time, last seen time, and version. Revoke is straightforward because deleting the row or bumping the user version makes the session invalid.
This is a strong default for web apps that care about instant logout, admin revoke, and stolen-device response. The cost is that every request needs a lookup unless you add a short local cache with a clear staleness policy.
JWT access tokens
A JWT is a signed statement that may include sub, exp, issuer, audience, and session version. The gateway can verify the signature without Redis, which helps at high request volume and across regions. The hard part is revoke. A JWT that expires in eight hours stays valid for eight hours unless you add a blocklist, introspection, or version check.
JWTs work best when access tokens are short-lived and refresh is stateful. The refresh token becomes the revoke handle.
Hybrid access JWT plus rotating refresh token
The common production compromise is a short-lived access JWT and a longer-lived opaque refresh token stored in an HttpOnly cookie for browsers or secure keychain for mobile. Normal API calls verify the JWT locally. When it expires, the refresh endpoint checks server-side state, rotates the refresh token, and issues a new access token.
Refresh rotation means each refresh token is one-time use. If an old token is used again, the system can treat it as theft and revoke the whole token family.
MFA and risk step-up
MFA should sit between password verification and session issue. If the account has TOTP or WebAuthn enabled, or risk signals are high, the auth service returns a temporary challenge id. The user completes the second factor, then the service issues the real session.
Recovery codes should be stored as hashes. TOTP secrets should be encrypted. Support should never read the raw secret.
Data model
Keep credentials, sessions, reset tokens, and audit events separate. They have different lifetimes and access rules.
| Entity or store | Important fields | Why it exists |
|---|---|---|
users | user_id, email_normalized, email_verified_at, password_hash, password_alg, password_params, password_changed_at, session_version, status | Source of truth for account and credential state |
sessions | session_id_hash, user_id, device_id, created_at, expires_at, last_seen_at, session_version, revoked_at | Opaque session lookup and revoke |
refresh_tokens | refresh_token_hash, family_id, user_id, device_id, expires_at, rotated_from, revoked_at | Stateful refresh and theft detection for JWT hybrid designs |
reset_tokens | token_hash, user_id, created_at, expires_at, used_at, request_ip | One-time password reset proof |
mfa_factors | factor_id, user_id, type, encrypted secret or public key, enabled_at | TOTP, WebAuthn, and recovery setup |
rate_limit_counters | keys such as login:ip, login:email_hash, counters, expiry | Protects login and reset endpoints from abuse |
auth_audit_events | event_id, user_id, event type, IP, device, outcome, timestamp | Investigation trail with no passwords, raw tokens, or TOTP secrets |
Store hashes of session ids, refresh tokens, and reset tokens if they live in a database. If the database leaks, raw bearer tokens should not be immediately usable.
Detailed design
Follow the user through signup, login, session validation, refresh, logout, and reset. Each flow has a different security purpose, so do not compress them into "auth service returns token."
Signup
The user submits email and password over TLS. The auth service normalizes the email, checks password policy, optionally checks whether the password appears in a known breach list, and creates a user record. The password is turned into a one-way hash using Argon2id or bcrypt with a per-user salt. The system stores the algorithm version and parameters beside the hash so it can upgrade old hashes later.
The service may issue a session immediately or require email verification first. That is a product choice. If verification is required, the verification link should use the same token hygiene as reset links: random token, hash at rest, short TTL, and one-time use.
Login
Login begins before the password hash runs. The edge and auth service rate-limit by IP, device, and normalized email. That protects the expensive hash worker pool. The service fetches the user by email. If the email does not exist, it should still do comparable dummy work so response timing does not reveal which emails are registered.
If the user exists, the service verifies the supplied password against the stored hash. The comparison should not leak timing differences. If the algorithm version is old and the password is correct, the service can rehash with stronger parameters in the background or during the login transaction.
If MFA is required, login does not create a full session yet. It creates a temporary challenge id with a short TTL. After the user completes TOTP or WebAuthn, the service issues the session. This avoids the common mistake where a password-only login briefly creates a usable token before MFA is checked.
When login succeeds, the service rotates any pre-login session id to avoid session fixation. For a browser, it sets an HttpOnly Secure SameSite cookie containing an opaque id. For a hybrid mobile flow, it returns a short access token and sets or returns a refresh token according to client rules. The audit log records success, IP, device, and risk result, but no password or token.
Every authenticated request
The client sends a cookie or bearer access token to the gateway. With opaque sessions, the gateway looks up the session id in Redis or a session service, checks expiry and revoked state, compares session version with the user's current version, and attaches user_id to the request context. With JWTs, the gateway verifies signature, issuer, audience, expiry, and optionally session version or token id for sensitive routes.
The upstream service should not receive the password or raw session secret. It receives identity context such as user_id, tenant id, auth strength, and scopes if needed. That keeps authentication centralized and makes audit easier.
Refresh and revoke
If using short access JWTs, the refresh endpoint is the stateful checkpoint. The client sends the refresh token. The server hashes it, finds the token family, verifies it has not been used or revoked, rotates it to a new refresh token, and issues a new access JWT. If an already-used refresh token appears again, that suggests theft, so the server can revoke the whole family and force login.
Logout deletes the current opaque session or revokes the current refresh token family. "Sign out everywhere" can delete all sessions for a user, but at large scale it is often easier to bump session_version on the user record. Every old session fails the next validation check because its stored version is behind.
Password reset and account recovery
The reset request returns the same user-facing message whether the email exists or not. If the email exists, the system creates a random token, stores only its hash with a short TTL, and emails an HTTPS link. The raw token should not be logged, and the URL should not include the user id.
On reset confirm, the service hashes the presented token and verifies it once. It burns the token, stores the new password hash, bumps session version or revokes all sessions, and sends a notification email. Retrying the same confirm request should not create multiple valid states; after success, the token is used and cannot be used again.
In the room (recap): "Signup stores a salted slow hash. Login is rate-limited, verifies the hash, runs MFA if needed, and then issues a session. Every later request validates an opaque session or short JWT without touching the password. Refresh is stateful so tokens can rotate and be revoked. Reset uses a one-time random token stored as a hash and invalidates old sessions after the password changes."
Architectural dig: revoke without breaking the hot path
The hardest auth tradeoff is that validation happens constantly, but revoke must still be real. If you optimize only for speed, stolen tokens live too long. If you optimize only for instant revoke, every request can depend on one central store.

The tempting but incomplete design
"Use JWT because it is stateless" sounds scalable, but it hides the revoke problem. If the token lasts a day and the user loses a laptop, the server cannot easily kill that token unless every request checks a blocklist or version store. The design is only stateless until the first compromise.
"Use Redis sessions for everything" has the opposite trap. Revoke is simple, but every API call depends on Redis. A Redis outage can become a full product outage unless you decide whether to fail closed, fail open for low-risk reads, or use short-lived local caches.
A practical web default
For browser apps with strong revoke needs, use opaque sessions in HttpOnly cookies. Redis maps the session to user id, expiry, and version. Logout deletes one row. Password change bumps a user-level version. Session validation stays fast because it is one key lookup, and revoke is understandable.
You can add a tiny in-process cache for session lookups only if the product accepts a few seconds of revoke delay. For sensitive apps, read the session store directly because stolen-session kill time matters more than saving one round trip.
A practical mobile or edge default
Use short-lived access JWTs, often five to fifteen minutes, and rotating refresh tokens. The gateway verifies access tokens locally. The refresh endpoint checks server state and can revoke a family. If a refresh token is replayed after rotation, treat it as theft and revoke.
This gives you cheap validation most of the time and a server-side checkpoint often enough to recover from compromise. It does not give instant access-token revoke unless you add blocklists or introspection on sensitive routes, so say that tradeoff out loud.
The interview land
Land on this sentence: "JWT moves work from lookup to revoke; opaque sessions move work from revoke to lookup." Then choose based on product needs. That is more convincing than declaring one token type always correct.
Key challenges
Password storage is irreversible by design. You should be able to verify a password without ever being able to recover the original password. That is why slow hashing beats encryption for stored credentials.
Credential stuffing is both security and availability. Bots can send millions of plausible login attempts. Even failed attempts consume hash CPU unless you rate-limit and step up before the expensive work.
Session revoke must be real. Logout, password change, stolen device, and admin response all need a clear path. A long-lived JWT without stateful refresh makes this hard.
Account recovery is an attack surface. Reset links and recovery codes can take over an account without knowing the old password. Treat them as bearer secrets with hashes, TTLs, one-time use, and audit.
Errors can leak account existence. Login and reset should not reveal whether an email is registered. Return generic messages to users and keep detail in server-side logs.
Scaling the system
Scale signup and login separately from session validation. Login needs a controlled worker pool for password hashing because Argon2id and bcrypt are intentionally expensive. If the worker pool is saturated, the system should shed attack traffic before real users time out.
Shard sessions by session id hash rather than user id for normal lookup distribution. For "sign out everywhere," avoid deleting a large number of rows synchronously. A user-level session version lets all existing sessions fail on their next validation check.
JWT verification scales at the gateway because signature checks are local, but key management becomes important. Use key ids, rotate signing keys carefully, keep private keys in KMS or a secret manager, and make old keys expire after access tokens age out.
For global deployments, keep login and refresh in a region that owns the user or use replicated state with clear consistency rules. Session validation can be closer to the edge if the product accepts the revoke delay. Security-sensitive actions can force a fresh server-side check.
Separate auth infrastructure from general product traffic. Login, reset, and refresh should have their own rate-limit policies, dashboards, and capacity planning because attacks target those endpoints directly.
Failure handling
| Failure | What users or teams see | What to build |
|---|---|---|
| Session store down | Users get 401s or pages fail to load | Decide fail closed for sensitive apps; optional short JWT fallback only with known revoke risk |
| Credentials DB down | New login, signup, and reset fail | Existing sessions may continue; return controlled 503 for credential writes |
| Hash worker pool saturated | Real users time out during stuffing | Edge limits, identity limits, queue depth alerts, and risk step-up before hashing |
| Refresh token replay | Possible token theft | Revoke token family, force login, alert user, audit event |
| JWT signing key leak | Forged access tokens possible | Rotate keys, revoke refresh families, shorten active token windows, inspect audit logs |
| Email provider down | Reset and verification emails delayed | Retry with backoff, show honest status, do not create permanent tokens |
The safety principle is to fail closed for identity decisions when the risk is high. A catalog page might tolerate a short degraded read. A banking transfer should not.
API design
The API should keep credential exchange, session validation, refresh, revoke, and reset separate enough that each can be protected and observed.
| Endpoint | Role |
|---|---|
POST /v1/auth/register | Create account and password hash; optionally send verification email |
POST /v1/auth/login | Verify credentials, create MFA challenge or issue session |
POST /v1/auth/mfa/verify | Complete a pending MFA challenge before session issue |
POST /v1/auth/logout | Revoke current session or refresh family |
POST /v1/auth/logout-all | Revoke every session by deletion or session-version bump |
POST /v1/auth/token/refresh | Rotate refresh token and issue a short access token |
POST /v1/auth/password/reset-request | Send generic reset response and email token if account exists |
POST /v1/auth/password/reset-confirm | Verify one-time token and set new password |
Login request:
| Field or header | Role |
|---|---|
email | Normalized for lookup and rate-limit key |
password | Never logged; verified only inside auth service |
device_id | Optional stable client signal for risk and session display |
Idempotency-Key | Useful for refresh or reset confirm retries, not for password guessing |
Authenticated request:
| Mechanism | Role |
|---|---|
Cookie: sid=... | Browser opaque session; HttpOnly, Secure, SameSite |
Authorization: Bearer <access_jwt> | Mobile or API client access token |
| Gateway context | Upstream receives user_id, auth strength, and scopes |
POST /login
-> rate limit -> lookup user -> verify hash -> MFA if needed
-> issue session cookie or access + refresh token
GET /account
-> gateway validates session or JWT
-> upstream receives user_id
POST /logout-all
-> bump session_version
-> old sessions fail next validation
Errors: Use 401 with a generic message for invalid credentials, 403 or a challenge response for MFA required, 429 with Retry-After for too many attempts, and 503 when auth dependencies are unavailable. Avoid "email not found" responses.
Observability
Auth dashboards should show both user experience and attack pressure. Basic availability is not enough because many auth attacks look like successful HTTP responses.
Track login success rate, failed login rate, p95 and p99 login latency, hash worker queue depth, rate-limit decisions, MFA challenge rate, password reset request-to-confirm ratio, refresh failures, session validation latency, and revoke propagation time.
Break metrics down by endpoint and reason. /login under stuffing behaves differently from /token/refresh during a mobile bug. Track top IPs, device ids, email hashes, and regions after privacy-preserving hashing so security can investigate without exposing raw user data broadly.
Audit logs should record security-relevant events: signup, login success, login failure, MFA failure, password reset request, reset confirm, password change, refresh replay, logout, logout-all, and admin revoke. They should never record passwords, raw session ids, raw refresh tokens, reset tokens, or TOTP secrets.
Alert on patterns that users feel: sudden login p99 increase, hash queue saturation, 429 spike, reset email spike, refresh replay events, session store error rate, and unusual new-device logins.
Security and abuse
Store secrets so database leaks do not immediately become account takeover. Passwords use salted slow hashes. Reset tokens, session ids, and refresh tokens should be stored as hashes when persisted. Signing keys and peppers belong in KMS or a secret manager, not in application code.
Protect against brute force and credential stuffing with layers. Use IP and device limits at the edge, identity-based limits on normalized email hash, progressive backoff, risk scoring, CAPTCHA or MFA step-up, and breached-password checks. Avoid naive account lockout that lets attackers lock real users out by repeatedly failing their password.
Use generic user-facing errors. The login form and reset form should not reveal whether an email exists. Server-side logs can hold structured reason codes for support and security teams.
Use safe cookie settings for browser sessions: HttpOnly, Secure, SameSite, and no session id in URLs. Protect CSRF where cookies are automatically sent. Protect XSS because HttpOnly reduces token theft but does not stop an attacker from acting as the user inside the browser.
For MFA, encrypt TOTP secrets, hash recovery codes, and audit factor changes. A session should be issued only after the required factor succeeds.
Cost awareness
Password hashing intentionally costs CPU and memory. That is a security feature, but it means bots can turn your own defense into an outage if you do not rate-limit before hashing. Capacity plan hash workers and monitor queue depth.
Session validation cost depends on your model. Opaque sessions cost a Redis lookup per request. JWT access tokens cost local signature verification and more key rotation discipline. A hybrid can reduce Redis reads while keeping revoke at refresh time.
Audit logs and security events grow quickly during attacks. Store enough detail for investigation, but keep payloads small and avoid secrets. Retention should match security and compliance requirements, not generic application log retention.
MFA and email providers add external cost and dependency. Reset storms and MFA floods can become expensive, so rate-limit unauthenticated flows and monitor provider failures.
Production scenes
Scene 1: Credential stuffing looks like a healthy API
The moment starts with account takeover reports, not a red availability page. Bots are trying leaked passwords from another breach. Every attempt returns a normal login response, so generic HTTP success rate looks fine while the password-hash queue grows and real users wait.
The trap is watching only 5xx. Auth attacks often produce valid 200 or 401 responses. The expensive part is the hash check and risk evaluation behind those responses.
What to build is layered admission. The edge applies IP and device controls. The auth service applies identity-based limits and backoff. Risky attempts get MFA or CAPTCHA step-up before consuming unlimited hash CPU. Dashboards track hash queue depth, login p99, and stuffing indicators separately from normal API health.
Scene 2: Stolen laptop requires revoke now
A user reports a stolen laptop and presses "sign out everywhere." If the system uses long-lived JWTs with no stateful checkpoint, the stolen device may keep working until token expiry. Support cannot honestly promise revoke.
The trap is selling stateless tokens as free scale. Stateless validation removes a lookup, but it also removes the easy kill switch unless you add short TTLs, refresh-token state, session version, or blocklists.
What to build depends on the product. Opaque sessions can delete rows or bump session version. Hybrid JWT designs revoke refresh families and use short access-token TTLs. Sensitive actions can force a fresh session-version check even when normal reads use local JWT verify.
Scene 3: Password reset becomes account takeover
Users report reset emails they did not request, and some accounts change passwords without a successful old-password login. The reset endpoint is unauthenticated, cheap to call, and easy to abuse if it reveals account existence or stores reusable tokens.
The trap is treating reset as a support feature instead of an auth feature. A reset link is a bearer secret. Whoever holds it can set the password.
What to build is a one-time token flow. Store only the token hash, keep TTL short, burn on use, rate-limit request and confirm, notify the user after change, and revoke old sessions. Return the same message whether the email exists.
Bottlenecks and tradeoffs
Opaque sessions vs JWT
The tension is revoke versus lookup. Opaque sessions make revoke simple but add a store read on every request. JWTs make validation cheap at the gateway but make immediate revoke harder.
Teams usually choose based on product risk. Admin panels, banking, and security-sensitive apps often prefer server-side sessions or mandatory introspection. Mobile APIs often use short access JWTs plus rotating refresh tokens.
Hash strength vs login latency
The tension is that stronger hash parameters slow attackers and real users. Too weak, and a database leak is easier to crack. Too strong, and stuffing can exhaust CPU.
Tune the hash to a measured budget, protect it with rate limits, and keep algorithm metadata so you can raise the cost later.
Generic errors vs user clarity
The tension is that "email not found" helps honest users but helps attackers enumerate accounts. Generic errors protect users but can frustrate support.
Use generic public messages and detailed private audit events. Recovery and reset flows should follow the same rule.
Instant revoke vs global availability
The tension is that strict revoke often means checking current server state. Global low-latency validation often means local token verification.
Use short token TTLs, version checks on sensitive routes, and clear regional policies. Do not promise both perfect instant global revoke and zero server lookup without explaining the mechanism.
Interview tips
Do not lead with JWT as a magic answer
You might say: "JWT is stateless, so it scales."
Interview Push: "A user loses a laptop. How do you kill that token in five minutes?"
Land here: Explain the revoke story before defending JWT. Use opaque sessions when instant revoke matters. Use short access JWTs plus rotating refresh tokens when edge validation matters. If the product requires immediate kill, add blocklist, introspection, or session-version checks on sensitive routes.
Explain password hashing in plain terms
You might say: "We encrypt passwords."
Interview Push: "Who has the key, and can they decrypt every user's password?"
Land here: Store a slow one-way hash with salt. The server verifies by hashing the entered password and comparing the result. It cannot recover the original password. Argon2id or bcrypt parameters are tuned to make guessing expensive while keeping real login acceptable.
Put rate limits before hash CPU melts
You might say: "The password hash is slow, so brute force is hard."
Interview Push: "Bots send fifty thousand attempts per minute. Who pays for those slow hashes?"
Land here: Slow hashes protect leaked databases; they do not replace login admission control. Rate-limit by IP, device, and email hash, apply risk step-up, and monitor hash queue depth. The goal is to spend expensive hash work on likely real users, not unlimited bot traffic.
Make reset tokens disappear
You might say: "We email a password reset link."
Interview Push: "If that email is forwarded or the link is logged, can it be reused?"
Land here: Use a random token, store only its hash, keep a short TTL, burn on first successful use, and revoke old sessions after password change. The reset request response should not reveal whether the email exists.
Close with audit logs that do not leak secrets
You might say: "We log auth events for debugging."
Interview Push: "Do those logs contain passwords, reset tokens, or session ids?"
Land here: Audit event type, user id, IP, device, outcome, and risk reason are useful. Raw secrets are not. Logs should help investigate account takeover without becoming a second credential database.
What should stick
- Two speeds. Login is slow and protected. Session validation is fast and runs on every request.
- Three bearer secrets. Passwords, sessions, and reset tokens each need different storage, expiry, and revoke rules.
- JWT is a tradeoff. It saves lookup work but makes revoke harder unless you add short TTLs and stateful refresh.
- Stuffing burns your CPU. Rate limits and risk step-up protect the hash path before attackers make it an outage.
- Recovery is auth. Password reset and recovery codes can take over accounts, so treat them like security-critical flows.
Tell it in the room: "Signup stores a salted Argon2id hash. Login is rate-limited, verifies the hash, completes MFA if required, and issues an opaque session or short access JWT plus rotating refresh. Every API call validates the session at the gateway and attaches user id. Password reset uses a one-time token stored as a hash and revokes old sessions. Credential stuffing is handled with edge limits, identity limits, risk step-up, and hash queue alerts."
Key Takeaways
- Never store plaintext passwords, and do not use reversible encryption for password verification.
- Choose opaque sessions, JWTs, or a hybrid by explaining validation cost and revoke behavior.
- Keep access tokens short-lived when JWTs are used, and make refresh tokens stateful and rotating.
- Protect login and reset with rate limits, generic errors, MFA step-up, and audit logs.
- Observe auth by endpoint and security event, not only generic API success rate.
Related Topics
- Rate Limiter System Design - Login and reset endpoint protection.
- Distributed Cache System Design - Session store sharding, hot keys, and failover.
- Notification Service System Design - Reset emails, security alerts, and async delivery.
- Logging System Design - Audit logs and safe security event storage.
- Design WhatsApp - Privacy and account security in messaging systems.
Quick revision notes
Use this as a last look before a mock interview, not as a substitute for the full guide.
Clarify clients and revoke. Browser cookies, mobile bearer tokens, MFA, and "sign out everywhere" change the design. Ask these before picking JWT or sessions.
Separate login from validation. Login verifies the password once with a slow hash. Every later request validates a session id or access token cheaply.
Pick a revoke mechanism. Opaque sessions delete rows or bump version. JWT designs need short access TTLs, rotating refresh tokens, and sometimes blocklists or version checks.
Protect the unauthenticated edge. Login and reset need IP, device, and identity limits, generic errors, risk step-up, and hash queue alerts.
Treat reset as a bearer-secret flow. Random token, hash at rest, one-time use, short TTL, no user id in URL, and session revoke after password change.
Practice this path: Auth System Design.
What interviewers expect
- Separate the slow credential path from the fast session-validation path. Login verifies a password hash; every later API call validates a session or access token without re-checking the password.
- Store passwords as salted Argon2id or bcrypt hashes, never plaintext and not reversible encryption. Mention algorithm versioning so hashes can be upgraded.
- Compare opaque sessions and JWTs honestly. Opaque sessions make revoke simple; JWT access tokens verify cheaply but need short TTL, rotating refresh tokens, blocklists, or session-version checks to revoke.
- Protect unauthenticated endpoints with rate limits, risk scoring, generic errors, MFA step-up, and audit logs. Credential stuffing is an availability and security problem, not only a password problem.
- Make password reset a one-time bearer-token flow: random token, hash at rest, short TTL, burn on use, notify the user, and invalidate old sessions.
- State failure behavior. If the session store fails, a bank may fail closed; a low-risk app may allow short JWT access until refresh expires.
Interview workflow (template)
- Clarify requirements. Confirm functional scope, users, consistency needs, and which non-functional goals matter most (latency, availability, cost).
- Rough capacity. Estimate QPS, storage, and bandwidth so your data model and partitioning story are grounded.
- APIs and core flows. Define a minimal API and walk 1–2 critical read/write paths end to end.
- Data model and storage. Choose stores for each access pattern; call out hot keys, indexes, and retention.
- Scale and failure. Add caching, sharding, replication, queues, or fan-out as needed; say what breaks in failure modes.
- Tradeoffs. Name alternatives you rejected and why (e.g. strong vs eventual consistency, sync vs async).
Frequently asked follow-ups
- How do you store passwords?
- JWT or server-side session?
- How do refresh tokens and revoke work?
- How do you stop brute force and credential stuffing?
- How do password reset and MFA fit the login flow?
Deep-dive questions and strong answer outlines
How do you store passwords?
Store a per-user salt and a slow one-way hash using Argon2id or bcrypt. Keep the algorithm and parameter version beside the hash so you can upgrade on the next successful login. Never store plaintext passwords, never log passwords, and do not "encrypt" passwords you need to verify.
JWT or opaque session?
Opaque sessions store a random session id in an HttpOnly cookie and keep server-side state in Redis or a session database. Revoke is deleting or invalidating that row. JWT access tokens are signed and can be verified without a lookup, which helps gateways, but revoke needs short TTLs, rotating refresh tokens, a blocklist, or a session-version check.
How does login work under credential stuffing?
Rate limit at the edge and by normalized identity, run generic error responses, use dummy hash work for unknown emails, add risk step-up such as MFA or CAPTCHA, and monitor hash queue depth. Do not let bots consume all Argon2 CPU while dashboards show only HTTP 200 responses.
How does password reset work?
The reset request returns the same message whether the email exists. If it does, store a hash of a random token with a short TTL and email the raw token in an HTTPS link. On confirm, verify once, burn the token, store the new password hash, bump session version or revoke all sessions, and notify the user.
Where does MFA fit?
MFA comes after password verification and before session issue. If the user has TOTP or WebAuthn enabled, or if risk is high, return an MFA challenge id rather than a full session. Recovery codes should be stored as hashes, and support should not be able to read them.
AI feedback on your design
After a practice session, InterviewCrafted summarizes strengths, gaps, and interviewer-style expectations—similar to a written debrief. See a static example report, then practice this problem to get feedback on your own answer.
FAQs
Q: Is OAuth a replacement for this design?
A: No. An external provider can prove identity, but your application still needs to issue its own session, validate requests, support logout, and own audit logs.
Q: Should JWTs go in localStorage?
A: For browsers, prefer HttpOnly Secure SameSite cookies so JavaScript cannot read the token after an XSS bug. Mobile clients can use bearer tokens stored in the platform keychain.
Q: Should login say "email not found"?
A: Usually no. Use a generic "invalid credentials" response so attackers cannot build a list of registered emails. Keep detailed reasons in server logs for support and security investigation.
Q: What does sign out everywhere mean?
A: Either delete all sessions for the user or bump a session version on the user record so every old session fails the next validation check. JWT-based systems also need refresh-token invalidation and short access-token TTLs.
Practice interactively
Open the practice session to use the canvas and stages, then review AI feedback.