Overview
Authentication (AuthN) answers 'who are you?'. The user or service proves its identity with something it knows (password), has (phone, security key), or is (biometrics), and the system then issues a credential, such as a session cookie or token, that later requests present instead of the password.
Good authentication design stores passwords only as slow salted hashes, supports multi-factor authentication, protects against brute force and credential stuffing, and prefers delegating to proven identity providers (OAuth 2.0 / OpenID Connect) over building everything yourself.
At check-in you show your passport once (authentication). The hotel gives you a key card (session or token). For the rest of your stay, the card opens doors without showing your passport again, and it expires when you check out.
When to use it
- Any system with user accounts or private data.
- Service-to-service calls that must prove the caller's identity.
- Choosing between sessions, JWTs, and an identity provider.
- Adding MFA and passwordless login (passkeys).
Where it shows up in interviews
Recognize it when: design sign-up, login, and session handling.
- Design an authentication service
- Design login for a banking app
Recognize it when: services and jobs authenticate to each other.
- Design service-to-service auth in microservices
- Design CI/CD access to cloud
Where it is used in real software
Okta, Auth0, Microsoft Entra ID, Amazon Cognito, and Keycloak handle login, MFA, and federation so apps do not store passwords.
Apple, Google, and Microsoft support passkeys: phishing-resistant public-key credentials that replace passwords.
Attackers replay leaked passwords from other breaches; rate limiting, MFA, and breached-password checks defend against it.
Key terms
- Credential
- Proof of identity: password, key, token, certificate.
- Password hashing
- Store bcrypt / scrypt / Argon2 hashes with a unique salt, never plain text.
- MFA
- Multi-factor authentication: combine two factor types.
- OIDC
- OpenID Connect: an identity layer on top of OAuth 2.0 that returns an ID token.
- mTLS
- Mutual TLS: services authenticate each other with certificates.
How it works, step by step
- 1User submits credentials
Over HTTPS only.
- 2Rate limit and check
Throttle attempts per account and IP; compare the password with the stored hash using a constant-time check.
- 3Second factor
TOTP code, push approval, or WebAuthn challenge.
- 4Issue a credential
Create a session (cookie) or tokens (access + refresh).
- 5Later requests present the credential
The server validates it on every request, and revokes it on logout or suspicion.
Authentication methods compared
Stronger options resist phishing and reuse
| Method | Security | User friction | Notes |
|---|---|---|---|
| Password only | Low | Low | Vulnerable to reuse and phishing |
| Password + SMS code | Medium | Medium | SIM swapping risk |
| Password + TOTP app | High | Medium | Offline codes |
| Passkey / security key | Very high | Low | Phishing resistant, no shared secret |
| SSO via identity provider | Depends on IdP | Low | Centralized policy and MFA |
NOWMethod: Password only | Security: Low | User friction: Low | Notes: Vulnerable to reuse and phishing
For most products: delegate to an identity provider, enable MFA, and offer passkeys.
Implementation
import argon2 from "argon2"; // Sign-up: store only a salted, slow hashasync function register(email: string, password: string) { const hash = await argon2.hash(password, { type: argon2.argon2id }); await db.users.insert({ email: email.toLowerCase(), passwordHash: hash });} // Login: same response for unknown user and wrong password (no user enumeration)async function login(email: string, password: string, ip: string) { if (await attempts.tooMany(email, ip)) throw new HttpError(429, "Too many attempts"); const user = await db.users.findByEmail(email.toLowerCase()); const valid = user ? await argon2.verify(user.passwordHash, password) : false; if (!valid) { await attempts.record(email, ip); throw new HttpError(401, "Invalid email or password"); } if (user.mfaEnabled) return { mfaRequired: true, challengeId: await mfa.start(user.id) }; return sessions.create(user.id); // sets an HttpOnly, Secure cookie}Complexity and performance
Deliberately slow to resist brute force.
Cache lookup or signature verify.
Trade-offs
Building auth is easy to start and hard to secure. Identity providers cost money but handle MFA, recovery, breach detection, and compliance.
Step-up authentication (asking for MFA only for sensitive actions) balances safety and usability.
Variants and related techniques
Server-side state referenced by a cookie; easy revocation.
Self-contained, verified without a database lookup; harder to revoke.
Simple long-lived secrets for server-to-server integrations; rotate and scope them.
Common mistakes
- Storing passwords with fast hashes (MD5, SHA-256).
Fix: Use Argon2id, bcrypt, or scrypt with per-user salts.
- Different errors for unknown user vs wrong password.
Fix: Return one generic message to prevent account enumeration.
- No brute-force protection.
Fix: Rate limit per account and IP, add MFA, and check breached passwords.
Interview questions
What is the difference between authentication and authorization?
Authentication verifies identity (who you are). Authorization decides what that identity may do (what you are allowed to access). Authentication comes first.
How should passwords be stored?
As slow, salted hashes using Argon2id, bcrypt, or scrypt. Never encrypted or plain text, so a database leak does not reveal passwords.
Practice problems
| Problem | Difficulty | What it trains |
|---|---|---|
| Design sign-up and login with MFA | Medium | Flows and storage. |
| Design password reset securely | Medium | Tokens, expiry, enumeration. |
| Design an authentication service for 100M users | Hard | Scale, sessions, IdP. |