What it is
Authentication establishes who is calling; authorization decides what they may do. For an application, authentication means proving a user’s identity (a password, a federated login) and then carrying that proof on later requests as a session or a token. Authorization means checking each request against rules about the caller and the resource. System design interviews ask where each happens, how a token is verified without a database call on every request, and what happens when one is stolen.
Important
Key takeaway: Authenticate once at the edge, authorize close to the data. Use short-lived access tokens (and revocable refresh tokens) so you can verify without a lookup but limit the damage of theft, derive identity from the verified token and never from a request parameter, and enforce authorization in the service that owns the resource.
When you care
Every API design has a login and a permission question, and the follow-ups are sessions versus tokens, how logout and revocation work, and how services trust each other.
Note
Interview signal:
Stating that a stateless token cannot be revoked before it expires, and that you therefore keep it short-lived with a revocable
refresh token, shows you know the cost of “stateless”. Saying “identity comes from the token, never from a user_id parameter” prevents
a whole class of authorization bugs.
Sessions versus tokens
| Server-side session | Signed token (JWT) | |
|---|---|---|
| State | The server stores the session; the client holds an opaque id in a cookie | The token carries the claims and a signature; the server stores nothing |
| Verification | A lookup in a session store (a cache) | Check the signature and expiry: no lookup |
| Revocation | Immediate: delete the session | Hard: valid until expiry unless you keep a deny list |
| Scaling | Needs a shared session store | Stateless; any server can verify |
| Size | Tiny id | Larger: carried on every request |
| Best for | Browser apps on one domain; immediate logout | Service-to-service and APIs; cross-service calls |
A JWT has three parts, header.payload.signature. The payload holds claims: who (sub), who it is for (aud), who issued it (iss),
when it expires (exp), and scopes. A service verifies the signature with the issuer’s public key (or a shared secret), checks exp, iss,
and aud, and trusts the claims. The signature proves the token was not altered; it does not hide the payload (it is only encoded), so keep secrets out.
Revocation, the cost of stateless. Make access tokens short-lived (minutes), and issue a longer-lived refresh token that is stored server-side and can be revoked; the client exchanges it for new access tokens. Logout and compromise are handled by revoking the refresh token, accepting that a stolen access token works until it expires. For high-risk actions, check a revocation list or re-authenticate.
OAuth 2.0 and OpenID Connect
OAuth 2.0 lets an application act on a user’s behalf without ever seeing their password, through delegated, scoped tokens. OpenID Connect adds an identity layer on top (an ID token saying who the user is), which is what “Sign in with …” uses.
The flow for a web or mobile app is the authorization code flow with PKCE:
- The app redirects the user to the authorization server, with a one-time code challenge.
- The user authenticates there and consents to the requested scopes.
- The server redirects back with a short-lived authorization code.
- The app exchanges the code (plus the code verifier, proving it started the flow) for an access token and a refresh token.
- The app calls the API with the access token.
PKCE binds the code to the app that requested it, so an intercepted code is useless. Use it for every public client (single-page and mobile apps); the older implicit flow, which returned tokens in the browser, should not be used. For service-to-service calls with no user, the client credentials flow gives a service its own token.
| Credential | Use |
|---|---|
| Session cookie | A browser user; set HttpOnly, Secure, and SameSite |
| Access token (JWT or opaque) | An API call on behalf of a user or service; short-lived |
| Refresh token | Obtaining new access tokens; stored server-side, revocable, rotated |
| API key | A third party or a script identifying an application; store only a hash, support rotation, scope it, and rate-limit it |
| mTLS certificate | Service identity inside a network (often managed by a service mesh) |
Where to enforce what
- Authentication at the edge. The gateway (or a sidecar) verifies the token once, rejects invalid ones, and passes the verified identity to services in a trusted header or an internal token.
- Authorization in the owning service. Only the service that owns an order knows who may read it. A gateway can enforce coarse rules (scopes, rate limits), but fine-grained checks belong at the data.
- Identity from context, never from parameters. A
user_idtaken from the request body invites changing123to124; derive it from the verified token. - Defense in depth. Internal services still authenticate each other (mutual TLS or service tokens), because a flat trusted network lets one compromised service reach everything.
Authorization models
| Model | Idea | Fits | Weakness |
|---|---|---|---|
| RBAC (role-based) | Users have roles; roles have permissions | Admin, editor, viewer | Role explosion; coarse |
| ABAC (attribute-based) | Rules over attributes of the user, resource, and context (“department matches, during business hours”) | Complex policies | Harder to audit and test |
| ReBAC (relationship-based) | Permissions follow relationships in a graph (“viewer of a folder can view its documents”) | Sharing models: documents, drives, social | Needs a graph store and care with latency |
Relationship-based systems (as in Google’s Zanzibar) store object#relation@user tuples and answer “can user U do R on object O?” with
a fast, consistent check; the hard parts are consistency (a revoked permission must take effect promptly) and performance at huge scale.
Passwords and the browser
- Store password hashes, never passwords: a slow, salted algorithm (bcrypt, scrypt, or Argon2), with a per-user salt. A fast hash (plain SHA-256) lets a stolen table be cracked quickly.
- Add multi-factor authentication, rate-limit login attempts, and lock or throttle on repeated failures.
- Browser protections:
HttpOnlycookies (not readable by scripts, limiting theft through cross-site scripting),SameSite(limits cross-site request forgery),Secure(HTTPS only), and a content security policy.
Warning
Production reality: Tokens are bearer credentials: whoever holds one is the user. Keep lifetimes short, send them only over TLS, never log them, scope them narrowly, and rotate signing keys on a schedule with the issuer publishing the current keys. A stolen long-lived token is a breach that no firewall sees.
When to pick what
| Situation | Choose | Why |
|---|---|---|
| A browser app on one domain, immediate logout | Server-side sessions in a cookie | Simple and revocable |
| Many services verifying without a lookup | Short-lived signed tokens, plus refresh tokens | Stateless verification |
| ”Sign in with …” or third-party access | OAuth 2.0 with PKCE and OpenID Connect | No password sharing, scoped delegation |
| Service-to-service, no user | Client credentials or mutual TLS | Service identity |
| Third-party developers | API keys (hashed, scoped, rotated), or OAuth | Identify and limit an application |
| Resource sharing (documents, folders) | Relationship-based authorization | Permissions follow relationships |
| Simple admin and user roles | RBAC | Easy to reason about |
Caution
Common mistake:
Trusting a client-supplied identity or role (a user_id parameter, an isAdmin claim the server did not issue), or putting
authorization only in the gateway. Verify the token, take identity from it, and enforce permissions in the service that owns the
data.
Related
- Reference: API Design Patterns — the API surface these tokens and scopes protect.
- Walkthrough: Designing a Rate Limiter — per-key and per-tenant limits, which need an authenticated identity to key on.
- Reference: Load Balancing and Service Discovery — the gateway and mesh layer where authentication usually happens.