If you remember five things
Say these five and the follow-ups get easier. Everything below defends them.
- Two different questions. Authentication is "who are you," authorization is "what are you allowed to do." Authn runs once per session, authz runs on nearly every request.
- Never trust the client for authz. Hiding a button is UI, not security. Every permission check runs on the server, on every request.
- Token style is a revocation trade-off. Stateless JWTs are fast and network-free but hard to kill early. Stateful sessions cost one lookup but revoke instantly.
- Hybrid wins at scale. Short-lived JWTs on the hot path, plus a small Redis revocation list checked only on sensitive actions.
- Pick the right permission model. RBAC for simple orgs, ReBAC (Zanzibar style) when sharing and nesting are the product, ABAC when rules get context-heavy.
1The core idea#
Start with the one-liner I give every junior, then defend it.
- Authentication (authn) answers who are you?
- Authorization (authz) answers what are you allowed to do?
Authentication always comes first. The system confirms your identity, then decides what that identity may touch. You cannot authorize an unknown person.
The airport analogy holds up.
- Showing your passport at security is authentication: the agent confirms you are the person in the photo.
- Your boarding pass later decides which gate you enter and which seat is yours: that is authorization.
- One passport, many boarding passes. You prove identity once, then get checked against many different rules as you move.
A concrete software example.
- You log into Gmail with a password and a 6-digit code from your phone. That is authentication.
- You open a shared doc a coworker sent. Google checks whether your account is on the share list before rendering it. That is authorization.
- The first happens once. The second happens on every single request.
Authentication is usually a one-time event per session. Authorization happens constantly, on nearly every API call. If your session lasts 8 hours and you make 2,000 API calls, you authenticate once and authorize 2,000 times. That asymmetry is why the authz path has to be cheap, and it drives every token decision later.
2How it works#
Walk a real request end to end: a user logs in, gets a token, then hits a protected endpoint.
The authentication phase.
- The user sends credentials. The auth service looks up the account and compares the password against a stored hash.
- Never store raw passwords. Use bcrypt, scrypt, or Argon2 with a per-user salt, so a database leak does not hand attackers everyone's password.
- The salt is load-bearing: without it, two users with the same password get the same hash, and an attacker precomputes one rainbow table for everybody. With a per-user salt, each password has to be cracked separately.
- If the hash matches (and a second factor passes, if you have one), the service issues a token. That token is the proof of identity for the rest of the session, so the user does not retype a password on every click.
The authorization phase.
- The user calls
GET orderswith that token attached, usually in anAuthorization: Bearer <token>header. - The API service verifies the token is real and unexpired, then asks the real question: is this identity allowed to see these orders?
- The check reads the user's roles or permissions and filters the data. A regular user sees only their own orders. An admin might see everyone's.
The token itself: two common styles. Interviewers love this split.
Stateless (JWT)
- What it is
- A signed blob with user ID, roles, and expiry.
- Verify cost
- Local signature check, no database call.
- Scales
- Great across many services with a shared key.
- Breaks when
- You need to kill it early: valid until it expires, so lifetimes stay short (15 min to 1 hour) paired with a refresh token.
Stateful (session ID)
- What it is
- A random string; real session data lives in a store like Redis.
- Verify cost
- One lookup per request.
- Scales
- Needs a session tier every request touches.
- Breaks when
- Nothing for revocation: delete the row and the next request fails. Great for "log out everywhere" and banning accounts.
When I reach for each.
- Stateful sessions when instant revocation matters: banking, anything with real money.
- Stateless JWTs when 20 microservices all need to check identity without hammering one session database.
A worked example with real numbers#
"It scales better" is a hand-wave until you put numbers on it. So let me put numbers on it.
The load.
- 10 million daily active users, each averaging 2,000 API calls per day.
- That is 20 billion authorization checks per day, about 230,000 requests per second on average.
- Peak is maybe 3x that: roughly 700,000 RPS.
Reveal the answer
Option A: stateful sessions in Redis.
- Every one of those 230,000 requests per second does a Redis lookup to validate the session.
- A single Redis node handles roughly 100,000 GET per second comfortably, so at peak you need around 7 to 10 replicas just for session reads, plus headroom.
- Session rows are small: a user ID, roles, an expiry, maybe 500 bytes each. With 10M concurrent sessions that is about 5 GB of memory, which fits in RAM easily.
- The win: ban a user with
DELon their session key and their next request fails instantly. Revocation latency is basically zero.
Option B: stateless JWTs.
- Those same 230,000 requests per second verify a signature locally, in-process, with the public key. A few microseconds of CPU and zero network calls, so the entire Redis session tier disappears.
- That is a real saving: no 7 to 10 node cluster to run and page on.
- The price: set a 1-hour expiry and demote a user at minute 5, and that user keeps admin access for up to 55 more minutes.
- Cut the window with a 15-minute token, but now the client hits the refresh endpoint 4x more often (roughly 96 refreshes per user per day), which pushes load back onto the auth service.
The honest interview answer: most large apps run a hybrid.
- Stateless JWTs with a 15-minute life for the hot path, plus a small revocation list in Redis holding only the tokens you explicitly killed.
- Check the revocation list only on sensitive actions (change password, transfer money), not on every read.
- The fast path stays network-free, and you still get a kill switch where it counts.
3Trade-offs#
The real design decisions are mostly on the authorization side. "Who are you" is fairly settled, but "what can you do" has several models with sharp differences.
| Model | How it decides | Good for | The cost |
|---|---|---|---|
| RBAC (role-based) | User has roles, roles have permissions | Most apps, clean org structures | Role explosion when exceptions pile up |
| ABAC (attribute-based) | Rules over user, resource, and context attributes | Fine-grained, context-aware rules | Hard to reason about and audit |
| ReBAC (relationship-based) | Permissions follow relationships between objects | Sharing graphs like Drive or GitHub | Needs a dedicated permissions system |
| ACL (access control list) | Each resource lists who can touch it | Small, per-object control | Does not scale to millions of objects |
Most teams start with RBAC. It maps to how companies actually think.
- You have "admin," "editor," "viewer." A new hire gets a role, done.
- It breaks when the business keeps asking for exceptions: "editors can delete, except on Fridays, except in the EU region, except for the finance folder."
- Now you are inventing roles like
editor_eu_no_finance, and you have 400 roles nobody understands. That smell means you have outgrown pure RBAC.
ABAC fixes the exception problem with rules over attributes.
- Allow if
user.department == resource.department AND user.clearance >= resource.level. Very flexible. - The downside: once 50 rules interact, no human can look at a resource and say for sure who can access it.
- Auditing becomes a nightmare, and interviewers at security-heavy companies will push on exactly that.
ReBAC powers Google Drive sharing and GitHub repo access.
- Permission comes from a relationship: you can edit this doc because you are in a group that was granted edit on a folder that contains it.
- Google published a system called Zanzibar for this, and a lot of modern permission services copy its design.
- Reach for ReBAC when sharing and nesting are core to the product.
The other big trade-off is where authorization runs.
- Centralized (one permission service everyone calls) gives consistent rules and one place to audit, but it becomes a bottleneck and a single point of failure.
- Decentralized (each service enforces its own) is fast and resilient, but rules drift apart and behavior gets inconsistent.
- Common middle ground: a central service owns the policy, each service caches it and enforces locally. Zanzibar does exactly this, keeping authoritative data central but serving checks from cached, consistent snapshots so a read does not round-trip to one database.
4Where it is used#
This shows up everywhere once you look.
- OAuth and "Sign in with Google." OAuth is really an authorization framework: it lets App X access some of your Google data without seeing your password. OpenID Connect (OIDC) is the layer on top that adds proper authentication, the "who are you" part. People say "I use OAuth to log in," but strictly you log in with OIDC, built on OAuth. Knowing that split is a green flag.
- API gateways. In a microservices setup, the gateway does authentication once at the edge: it validates the token and attaches the verified user ID to the request. Each downstream service then handles its own authorization, because only the orders service knows the rules for orders.
- Cloud IAM (AWS, GCP). AWS IAM is a giant authorization engine. Your access key authenticates you; IAM policies decide whether you can start that EC2 instance. One overly broad policy, like attaching
AdministratorAccessto a service that only needed to read one bucket, is a common way companies get breached. - Kubernetes. Same two phases: the API server authenticates you via a certificate or token, then RBAC rules decide whether you can delete a pod.
5Common pitfalls and gotchas#
These are the mistakes I see in real code reviews and in interviews.
Confusing a 401 and a 403.
401 Unauthorizedactually means not authenticated. The naming is historical and backwards.403 Forbiddenmeans authenticated but not allowed, the real authorization failure.- Use these correctly and I notice.
Trusting the client for authorization.
- The classic bug: the frontend hides the "delete" button for non-admins, and the team thinks they are done.
- An attacker just calls the API directly.
- Authorization runs on the server, on every request. Hiding a button is a convenience, not a security control.
Insecure direct object references (IDOR).
- You authenticate the user, then fetch
GET order 5012without checking the order belongs to them. - User A changes the ID to 5013 and reads user B's order. This is one of the most common real-world vulnerabilities.
- Always scope the query to the authenticated user: "fetch order 5013 where owner = current user."
- Unguessable IDs (UUIDs instead of
5012,5013) are nice hardening, but defense in depth, not a substitute for the ownership check.
Putting secrets or stale data in a JWT.
- Anyone can decode a JWT; it is only signed, not encrypted. Do not put passwords or private data in it.
- The claims are frozen at issue time. Demote an admin to a user and their old token still says "admin" until it expires. That is the revocation problem again.
No token expiry, or a 30-day access token.
- A stolen long-lived token is a long-lived breach. A 30-day access token means a stolen token is valid for 30 days.
- Short access tokens plus refresh tokens exist for exactly this reason.
"Can this user do X" logic copy-pasted across 40 endpoints. Someone eventually forgets one, and that one forgotten endpoint is your breach. If you scatter authz checks instead of centralizing them into middleware or a policy layer, a senior interviewer reads it as code they would not want to maintain. Name the central check before they ask.
6How it shows up in interviews#
When I ask "how would you handle auth for this system," here is what separates answers.
Candidate (weak): I'd add a login with JWT and check the token.
Interviewer: That is just authentication. How does a viewer get blocked from the delete endpoint? What is your permission model? Where do the checks run?
Candidate (strong): Authentication happens at the gateway. Users log in, we verify the password against an Argon2 hash with a second factor, and issue a short-lived 15-minute JWT plus a refresh token. For authorization I'd start with RBAC, since the roles are simple: admin, editor, viewer. Each service enforces its own checks, and I'd guard against IDOR by scoping every data query to the authenticated user. Stolen tokens are limited to a 15-minute window, and for sensitive actions I'd check a Redis revocation list so I can kill a session instantly. If the product grows into heavy document sharing, I'd move to a ReBAC service modeled on Zanzibar.
Why the strong answer wins.
- It shows you know the two phases are different things.
- It picks specific algorithms and token lifetimes instead of hand-waving.
- It puts a number on the stolen-token blast radius (15 minutes).
- It anticipates how the design evolves (RBAC to ReBAC), so follow-ups go deeper instead of fishing for basics.
Say the words "authentication" and "authorization" in full at least once early. It signals you know they are distinct, and it sets up every trade-off conversation that follows. Candidates who only ever say "auth" make me wonder whether they can actually split the two when I push.
"Someone steals a token. What happens?"
"A user was an admin and got demoted. When does that take effect?"
"Where do you enforce permissions?"
"How do permissions scale as the product adds sharing?"
"Why not just use OAuth to log users in?"
What you can now explain in the room
- Why authentication runs once per session and authorization runs on nearly every request.
- Why passwords are hashed with a per-user salt (bcrypt, scrypt, Argon2) and never stored raw.
- The stateless-JWT versus stateful-session trade-off, with real numbers on cost and revocation.
- Why a hybrid (short JWT plus a Redis revocation list on sensitive actions) is the common production answer.
- When to pick RBAC, ABAC, ReBAC, or ACLs, and why RBAC eventually explodes.
- Why IDOR, client-trusted checks, and stale JWT claims are the bugs that actually breach systems.
Next, try the System Design Question walkthrough on designing a URL-shortener with per-user API keys, where authentication and rate-limited authorization meet head on.
Get a prep plan built around your Authentication vs Authorization interview
Tell us the role and your interview date. We will send back a dated, day by day plan. Free.