Home › High-Level Design › Authentication vs Authorization
A

Authentication vs Authorization

Mix these two up in an interview and the interviewer quietly downgrades you. They are the first two words out of your mouth when someone says "design a login system," and they are not the same thing. I have watched strong candidates lose a point because they said "auth" and meant both, then could not split them apart when pushed. One said "we'll just use JWT auth," and when I asked "so how does a viewer get blocked from the delete endpoint?" the candidate went quiet. That gap is the whole topic. Here is the clean version so you never fumble it.

High-Level DesignBeginner10 min read

If you remember five things

Say these five and the follow-ups get easier. Everything below defends them.

  1. 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.
  2. Never trust the client for authz. Hiding a button is UI, not security. Every permission check runs on the server, on every request.
  3. 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.
  4. Hybrid wins at scale. Short-lived JWTs on the hot path, plus a small Redis revocation list checked only on sensitive actions.
  5. 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.
The part people miss

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.

sequenceDiagram participant U as User participant A as Auth Service participant S as API Service participant D as Database U->>A: Send email and password A->>D: Look up user and check hash D-->>A: User record is valid A-->>U: Return signed token U->>S: Request orders with token S->>S: Verify token signature S->>S: Check role permissions S->>D: Fetch orders for this user D-->>S: Return allowed rows S-->>U: Return orders
Two distinct phases live in one diagram. The top half (login to signed token) is authentication and runs once. The bottom half (verify signature, check permissions, filter rows) is authorization and repeats on every request.

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 orders with that token attached, usually in an Authorization: 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.

Fast path, hard to revoke
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.
One lookup, instant kill
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.
Pause before you read on: at 230,000 authz checks per second, which token style costs you a whole server tier, and which costs you revocation latency?
Reveal the answer
Stateful sessions cost you the Redis tier: every one of those 230,000 checks is a network lookup, so you run and page on a 7 to 10 node cluster. Stateless JWTs drop that tier entirely (verification is in-process CPU) but cost you revocation latency: a demoted user keeps their old access until the token expires. You are trading infra cost against how fast you can say "no."

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 DEL on 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.
~5 GB
Session memory for 10M users500 bytes per session row times 10M concurrent sessions fits in RAM on one box. The session tier is not a memory problem, it is a request-rate problem: 230K lookups per second is what forces the 7 to 10 Redis replicas.

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 AdministratorAccess to 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 Unauthorized actually means not authenticated. The naming is historical and backwards.
  • 403 Forbidden means 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 5012 without 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.
Red flag that fails you

"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.
From the interviewer's chair

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.

Follow-ups the interviewer will ask (and your answers)
"Someone steals a token. What happens?"
With a short-lived JWT the blast radius is bounded: a 15-minute token means the attacker has at most 15 minutes. For sensitive actions I also check a Redis revocation list so a stolen or compromised token can be killed before it expires. That is the stateless-versus-stateful revocation trade-off, and the hybrid gets both the fast path and the kill switch.
"A user was an admin and got demoted. When does that take effect?"
If the roles live in a JWT, the claims are frozen at issue time, so the old token still says "admin" until it expires (up to its full lifetime). To make demotion take effect immediately you either keep roles server-side in a session, or add the token to a revocation list and re-check it on sensitive actions. Short token lifetimes shrink the window either way.
"Where do you enforce permissions?"
Server side, on every request, never the client. The gateway can authenticate once and attach the verified user ID, but each service enforces its own authorization because only that service knows its rules. Hiding a UI button is a convenience, not a control.
"How do permissions scale as the product adds sharing?"
That is the RBAC-to-ReBAC conversation. RBAC is fine for admin/editor/viewer, but once permission comes from relationships (you can edit this doc because you are in a group granted edit on its folder) you want a ReBAC permission service modeled on Zanzibar: authoritative data central, checks served from cached consistent snapshots.
"Why not just use OAuth to log users in?"
OAuth is an authorization framework: it grants App X access to some of your data without sharing your password. The authentication layer ("who are you") is OpenID Connect, built on top of OAuth. So strictly you log in with OIDC, not OAuth. Naming that split is a green flag.

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.

Thanks. Check your inbox, your Authentication vs Authorization prep plan is on the way.