AuthN vs AuthZ
Sessions vs JWTs. OAuth 2.1, OIDC, social login flows.
Two words sound the same, get confused constantly, and they are not the same thing. Authenticationanswers "who are you?" Authorizationanswers "what are you allowed to do?" Knowing the difference is the foundation for every login system, every API key, every "you must be logged in" redirect on the web.
AuthN vs AuthZ in one table
- Authentication (AuthN)= identity. A username and password. A passkey. A magic link. A fingerprint. Returns "yes, you are Alice." Status code for failure:
401. - Authorization (AuthZ) = permission. Alice is logged in, but can she delete this article? Is she an admin? Does her plan include this feature? Status code for failure:
403.
You always do AuthN first. Then AuthZ on every protected operation. Forgetting the second is how data leaks happen: the user is logged in, sure, but logged in users can't look at anyone's invoices.
Session vs JWT: the two main flavors
Once a user authenticates, the server needs a way to remember it on subsequent requests without asking for the password every time. There are two dominant patterns.
Sessions (stateful)
- User submits username + password.
- Server verifies, generates a random opaque session id, stores it in a database/Redis keyed to the user.
- Server sends back a cookie:
Set-Cookie: session=abc123; HttpOnly; Secure; SameSite=Lax. - On every request, browser sends the cookie automatically. Server looks up
abc123in the session store to find the user.
JWT (stateless)
- User submits username + password.
- Server verifies, builds a JSON Web Token containing the user's id (and roles, expiry, etc), signs it with a secret.
- Client stores it (in memory or a cookie) and sends it back.
- On every request, server just verifies the signature. No database lookup needed.
JWT: stateless, scales across services, but revocation is hard (you have to maintain a denylist, which is alsoa session store). Good for microservices or third-party API access. Don't use JWTs as primary session cookies just because the docs sound clever.
OAuth 2.1 with PKCE: "sign in with Google"
OAuth is how your app delegates authentication to another service (Google, GitHub, Microsoft) without ever seeing the user's password. OAuth 2.1 is the consolidated modern version. PKCE (pronounced "pixie") is a mandatory addition that protects the flow from a class of attacks. Here is the whole dance for a typical browser-based app:
- Your app generates a random code_verifier (a long random string) and a code_challenge = SHA-256(code_verifier), base64url encoded.
- App redirects the browser to the provider's authorize URL, passing
client_id,redirect_uri,scope,state(random anti-CSRF token), andcode_challenge. - User logs in at the provider's site and approves the requested scopes.
- Provider redirects the browser back to
redirect_uriwith a short-livedcodeand the originalstate. - Your app verifies the
statematches what it sent (anti-CSRF). - Your app makes a server-to-server POST to the provider's token endpoint with
code,client_id,redirect_uri, and the originalcode_verifier. - Provider hashes the verifier, compares to the original challenge. If it matches, returns an
access_token(and optionally arefresh_token). - Your app uses the access token to call the provider's APIs, or it just reads the user's profile and creates its own session.
code for tokens. PKCE locks each code to the verifier that only your app knows.OpenID Connect: the identity layer on top
OAuth gives you an access token for an API. It does not tell you who the user is. OpenID Connect (OIDC) adds that on top: alongside the access token, the provider returns an id_token(a JWT) that contains the user's identity claims (subject id, email, name, etc).
{
"iss": "https://accounts.google.com",
"sub": "117345209824712345678",
"aud": "your-client-id.apps.googleusercontent.com",
"email": "alice@example.com",
"email_verified": true,
"name": "Alice Anderson",
"exp": 1716592800,
"iat": 1716589200
}Verify it (signature + iss + aud + exp), then provision a user account in your database keyed by the sub claim. Never key off email alone: emails can change.
Bearer tokens, API keys, and mTLS
- Bearer tokens- "whoever holds the token gets in." Sent via
Authorization: Bearer <token>. JWTs and opaque session tokens both qualify. Lose the token, lose the keys to the castle. - API keys - long-lived secret strings, usually for server-to-server use.
X-API-Key: sk_live_.... Easier to manage than OAuth but should be scoped (restricted permissions) and rotatable. - mTLS (mutual TLS) - both client and server present TLS certificates. Authentication happens at the connection layer, no token needed. Used in service meshes and high-security B2B integrations. Strong, painful to set up.
Quiz
A user is logged in but tries to access another user's data. Which fails?
Recap
- AuthN = identity (401). AuthZ = permissions (403). Both, every request.
- Sessions = server-stored, easy to revoke. JWTs = stateless, hard to revoke. Pick on purpose.
- OAuth 2.1 + PKCE is the modern delegated-auth flow. OpenID Connect adds an id_token so you know who the user is.
- Bearer tokens, API keys, mTLS each have their place. Scope and rotate them.
- Don't roll your own. Use a real library.