Authentication and authorization cheat sheet
The 12 Authentication and authorization interview answers in 30 seconds each, plus quick facts. Printable, free.
One page per topic: print it or save it as PDF from your browser's print menu. Practise these questions
Rules of thumb
- Browser app to own backend: server sessions in HttpOnly, Secure, SameSite cookies.
- JWT: verify signature with a pinned alg, then exp, nbf, iss, aud.
- Short access tokens; rotate refresh tokens and revoke the family on reuse.
- Authorization code + PKCE for every client; no implicit flow.
- Key users on (iss, sub), not email.
- Passwords: Argon2id, scrypt or bcrypt with per-user salt.
- Check ownership on every resource, not just the role.
Server sessions vs stateless tokens
A server session stores state on the server (in a database or Redis) and gives the browser a random session id in an HttpOnly cookie; logging out or revoking is just deleting the row. A stateless token like a JWT carries signed claims, so any service can check it without a lookup, but it stays valid until it expires unless you add a deny list. For a browser app talking to its own backend, sessions are usually simpler and safer; tokens shine between services and for third-party API access.
Validating a JWT properly
The payload of a signed JWT (JWS) is only base64url-encoded, not encrypted: anyone holding the token can read it, so never put secrets in it. Before trusting it, verify the signature with a key you chose (and an algorithm you pinned, never 'none' or whatever the header says), then check exp and nbf, the issuer (iss) and the audience (aud) for your service. Only then use sub and scopes.
Access tokens, refresh tokens and rotation
Short access tokens (minutes) limit the damage if one leaks and need no revocation checks. A long-lived refresh token, sent only to the auth server, gets new access tokens. With rotation, every refresh returns a new refresh token and invalidates the old one; if an old one is ever used again, that signals theft, so the server revokes the whole token family and forces a new login.
OAuth 2.0 authorization code with PKCE
The app sends the user to the authorization server with a code challenge (a SHA-256 hash of a random verifier) and gets back a short-lived code on its redirect URI. It then exchanges the code plus the original verifier for tokens over a back channel. An attacker who intercepts the code can't use it without the verifier. The implicit flow, which returned tokens in the URL, is deprecated; current guidance (OAuth 2.1 drafts and the security BCP) is code + PKCE for every client.
import base64
import hashlib
import secrets
verifier = secrets.token_urlsafe(48)
digest = hashlib.sha256(verifier.encode()).digest()
challenge = base64.urlsafe_b64encode(digest).rstrip(b"=").decode()
print(43 <= len(verifier) <= 128, len(challenge))OpenID Connect and ID tokens
OpenID Connect is an identity layer on OAuth 2.0: with the openid scope, the provider returns an ID token, a signed JWT describing who signed in (sub, iss, aud, exp, nonce, often email). Your backend verifies it like any JWT, checks aud is your client id and the nonce matches what you sent, then maps the stable iss + sub pair to a local user. The access token is for calling APIs; the ID token is for your app to know the user.
Storing passwords
Never store them, only a slow, salted hash made for passwords: Argon2id first choice, scrypt or bcrypt if that's what your stack offers. A unique random salt per password stops precomputed tables and makes equal passwords hash differently; the work factor makes each guess expensive. Compare in constant time, re-hash on login when you raise the cost, and rate-limit login attempts. Fast hashes like SHA-256 are wrong here, even salted.
MFA and TOTP codes
The server and the app share a secret at enrolment (usually via a QR code). Each code is an HMAC of the secret and the current 30-second time step, truncated to 6 digits (RFC 6238). The server computes the expected code for the current step, usually also one step either side for clock drift, compares in constant time, and records the used step so the same code can't be replayed. Rate-limit attempts, because 6 digits is only a million options.
API keys done right
Generate long random keys (for example 32 bytes from a CSPRNG) with a recognisable prefix (sk_live_…) so leaks are easy to scan for. Show the full key once, store only a hash (SHA-256 is fine for random keys), and look up by hash on each request. Tie each key to an owner, scopes and environment; allow several active keys so customers can rotate without downtime; log last-used time; and revoke instantly.
Authenticating service-to-service calls
Don't trust the network. Give each service an identity and verify it on every call: mutual TLS with short-lived certificates (often issued automatically by a service mesh or SPIFFE), or signed tokens from an internal issuer using the OAuth client credentials flow with a narrow audience. The receiving service checks the caller's identity and what it's allowed to do; a user's identity, if needed, travels as a separate verified token.
RBAC, ABAC and resource ownership
Role-based access control assigns permissions to roles (admin, editor, viewer) and roles to users: simple to reason about and audit, but it can't say 'only their own invoices'. Attribute-based control evaluates rules over user, resource and context attributes (department, owner, time). Relationship-based control (as in Google's Zanzibar paper) derives access from links like 'member of team that owns doc'. Most apps combine coarse roles with per-resource ownership checks.
Revoking tokens and sessions
Delete all their server-side sessions, revoke all refresh tokens (or their token families), and make outstanding access tokens fail. Short-lived JWTs will die soon on their own; to cut them off instantly, keep a per-user 'tokens valid after' timestamp or version that services check, or a deny list of token ids until they expire. Then require re-authentication and, if credentials may be exposed, a password reset and MFA re-enrolment.
Cookie flags for session security
HttpOnly stops JavaScript from reading it, so an XSS bug can't steal the session. Secure sends it only over HTTPS. SameSite=Lax (or Strict) stops browsers sending it on most cross-site requests, which blocks classic CSRF. Scope it tightly with Path and no broad Domain, give it a sensible lifetime, and use the __Host- prefix so it can only be set by your exact host over HTTPS.
Quick facts
- JWT payloads
- A signed JWT's payload is base64url, readable by anyone holding the token. Signing isn't encryption.
- RFC 6238 vector
- With secret 12345678901234567890 (SHA-1) at T = 59 s, the 8-digit TOTP is 94287082.
- PKCE verifier length
- RFC 7636: 43 to 128 characters; the S256 challenge is 43 characters.
- bcrypt input limit
- bcrypt uses only the first 72 bytes of the password.
- Implicit flow
- Deprecated by the OAuth 2.0 Security Best Current Practice; use authorization code with PKCE.
- Token introspection
- RFC 7662 lets a resource server ask the authorization server whether a token is active.