Skip to main content

Reference: Authentication and Authorization for Applications

A reference on application auth — sessions vs JWTs, revocation, OAuth 2.0 with PKCE, where to enforce what, and RBAC, ABAC, and relationship-based models.

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 sessionSigned token (JWT)
StateThe server stores the session; the client holds an opaque id in a cookieThe token carries the claims and a signature; the server stores nothing
VerificationA lookup in a session store (a cache)Check the signature and expiry: no lookup
RevocationImmediate: delete the sessionHard: valid until expiry unless you keep a deny list
ScalingNeeds a shared session storeStateless; any server can verify
SizeTiny idLarger: carried on every request
Best forBrowser apps on one domain; immediate logoutService-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:

  1. The app redirects the user to the authorization server, with a one-time code challenge.
  2. The user authenticates there and consents to the requested scopes.
  3. The server redirects back with a short-lived authorization code.
  4. The app exchanges the code (plus the code verifier, proving it started the flow) for an access token and a refresh token.
  5. 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.

CredentialUse
Session cookieA 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 tokenObtaining new access tokens; stored server-side, revocable, rotated
API keyA third party or a script identifying an application; store only a hash, support rotation, scope it, and rate-limit it
mTLS certificateService 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_id taken from the request body invites changing 123 to 124; 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

ModelIdeaFitsWeakness
RBAC (role-based)Users have roles; roles have permissionsAdmin, editor, viewerRole explosion; coarse
ABAC (attribute-based)Rules over attributes of the user, resource, and context (“department matches, during business hours”)Complex policiesHarder 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, socialNeeds 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: HttpOnly cookies (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

SituationChooseWhy
A browser app on one domain, immediate logoutServer-side sessions in a cookieSimple and revocable
Many services verifying without a lookupShort-lived signed tokens, plus refresh tokensStateless verification
”Sign in with …” or third-party accessOAuth 2.0 with PKCE and OpenID ConnectNo password sharing, scoped delegation
Service-to-service, no userClient credentials or mutual TLSService identity
Third-party developersAPI keys (hashed, scoped, rotated), or OAuthIdentify and limit an application
Resource sharing (documents, folders)Relationship-based authorizationPermissions follow relationships
Simple admin and user rolesRBACEasy 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.