Authentication Concepts
On this page
Identity and Access Management#
Two words that sound alike but mean different things sit at the centre of every access decision:
- Authentication (AuthN) answers "who are you?" It verifies identity with passwords, tokens, biometrics or hardware keys.
- Authorization (AuthZ) answers "what are you allowed to do?" It checks whether an already-identified user may perform this action on this resource.
Confusing the two is the source of the most common web flaw. An app that only asks "are you logged in?" but never "may this user touch this object" has broken access control (see IDOR). Authentication without authorization is a locked front door with every internal door wide open.
1. Session-Based Authentication (Stateful)#
The traditional web model. On login, the server creates a session, stores it server-side (in a database or Redis), and sends the client an opaque session_id in an HttpOnly cookie. Each later request carries the cookie; the server looks the id up to know who the user is.
import secrets
SESSIONS = {} # in real systems: Redis or a database, with a TTL
def login(user_id: str) -> str:
sid = secrets.token_urlsafe(32) # opaque, unguessable
SESSIONS[sid] = {"user_id": user_id}
return sid # set as HttpOnly; Secure; SameSite=Lax
def whoami(sid: str):
return SESSIONS.get(sid) # None -> not authenticated
def logout(sid: str):
SESSIONS.pop(sid, None) # instant, server-side revocation
- Pros: you can revoke a session instantly by deleting it server-side. The cookie is opaque, so it carries no data an attacker can read or tamper with.
- Cons: every service that authenticates must reach the session store, which is a scaling and coupling cost across many microservices.
The session cookie must be HttpOnly (invisible to JavaScript, limiting XSS theft), Secure (HTTPS only), and SameSite (limits CSRF). Regenerate the session id on login and on any privilege change to prevent session fixation.
2. JWT (Stateless Tokens)#
A JSON Web Token moves the state to the client. On login the server builds a JSON payload (the user id, an expiry, maybe roles) and signs it. The client sends it back, usually as Authorization: Bearer <token>. Any service can verify the signature with a shared secret or the issuer's public key and trust the contents without a database lookup.
import jwt, datetime # PyJWT
SECRET = "keep-this-in-a-secret-manager"
def issue(user_id: str) -> str:
payload = {
"sub": user_id,
"iat": datetime.datetime.now(tz=datetime.timezone.utc),
"exp": datetime.datetime.now(tz=datetime.timezone.utc)
+ datetime.timedelta(minutes=15),
}
return jwt.encode(payload, SECRET, algorithm="HS256")
def verify(token: str):
# verify_signature and exp are checked; a bad/expired token raises
return jwt.decode(token, SECRET, algorithms=["HS256"])
- Pros: stateless and decoupled; ideal for microservices and APIs.
- Cons: you cannot easily revoke a JWT before it expires, because nothing is looked up. A stolen token works until
exp.
That revocation gap drives the standard pattern: short-lived access tokens (minutes) plus a longer-lived refresh token that is stored server-side, can be revoked, and is rotated on each use. Two JWT pitfalls to avoid in review: pin algorithms= explicitly on decode (historic libraries accepted an attacker-set alg: none or an RSA-to-HMAC downgrade), and remember the payload is only base64-encoded, not encrypted, so never put secrets in it (the hashing vs encoding lesson explains why encoding is not protection).
| Session (stateful) | JWT (stateless) | |
|---|---|---|
| Where state lives | Server store | Inside the token |
| Revoke before expiry | Instant | Hard (needs a denylist) |
| Scales across services | Needs shared store | Verify anywhere with the key |
| Token readable by holder | No (opaque) | Yes (base64 payload) |
| Best fit | Classic web apps | APIs, microservices, SPAs |
3. OAuth 2.0 (Delegated Authorization)#
OAuth 2.0 is not a login protocol; it is a framework for delegated authorization. It lets one application access some of your data on another service without ever seeing your password. When a scheduling app reads your calendar, you log in at the calendar provider, consent to a specific scope, and the provider hands the app a scoped access token. The app uses that token against the API; it never learns your password and can be revoked independently.
For web and mobile apps, use the Authorization Code flow with PKCE, which protects the code exchange against interception. Avoid the old Implicit flow. The tokens OAuth issues are frequently JWTs, tying this back to the section above.
4. OpenID Connect (OIDC)#
OAuth grants access to APIs but says nothing standard about who the user is. OpenID Connect is a thin authentication layer on top of OAuth 2.0 that fills that gap. It adds an ID token (a JWT with verified identity claims: name, email, subject id). "Sign in with Google/Apple/Microsoft" is OIDC. The distinction to keep straight: OAuth answers "what may this app access," OIDC answers "who is this user."
A frequently missed corner of authentication is the password reset flow, which is an alternate way into the account and is attacked as such. A reset link must carry a single-use, expiring, high-entropy token, must not reveal whether an email address is registered (return the same "check your inbox" either way), and must invalidate existing sessions once the password changes. A weak reset flow undoes an otherwise strong login.
Multi-Factor Authentication#
Passwords fail predictably (reuse, phishing, weak entropy). MFA requires evidence from more than one category:
- Something you know: a password or PIN.
- Something you have: a TOTP code from an authenticator app, or a hardware security key.
- Something you are: a fingerprint or face scan.
Not all factors are equal against phishing. A TOTP code can be entered into a fake login page and replayed by the attacker within its short window. FIDO2/WebAuthn hardware keys and passkeys are phishing-resistant because the credential is cryptographically bound to the real site's origin, so it simply does not produce a valid response for a look-alike domain. Prefer them for high-value accounts.
The Authorization Half: RBAC, ABAC, Least Privilege#
Once identity is established, authorization decides what is permitted. Two common models:
- RBAC (Role-Based Access Control): users belong to roles (
viewer,editor,admin) and roles carry permissions. Simple and auditable; the default choice for most apps. - ABAC (Attribute-Based Access Control): decisions combine attributes of the user, the resource and the context (department, region, time, ownership). More expressive when access depends on data, not just a job title.
The rule that spans both is least privilege: every user, token and service account gets the minimum permission that still does the job, and nothing more. Enforce authorization checks server-side on every request, deny by default, and never trust a role claim the client can set. A menu that hides the admin button is not authorization; the server must reject the admin action regardless of what the client sends.
Token and Session Lifecycle Discipline#
Tokens and sessions are keys, and keys need a lifecycle:
- Set an expiry on everything; nothing is valid forever.
- Keep access tokens short-lived so a leak is a narrow window; use rotating, revocable refresh tokens for longevity.
- Regenerate the session id at login and privilege elevation; invalidate it fully on logout, server-side, not just by clearing the cookie.
- Store any server-side tokens hashed, not in plaintext.
- Never place a token in a URL, where it leaks into server logs, browser history and
Refererheaders. Use headers orSecure; HttpOnlycookies.
Checklist#
- Cookies:
HttpOnly,Secure,SameSite; session id regenerated on login. - JWT: explicit
algorithms=, short expiry, no secrets in the payload, refresh-token rotation. - MFA available, phishing-resistant factors offered for privileged accounts.
- Every request runs a server-side authorization check scoped to the acting user.
- Logout and password change invalidate existing sessions.
Check your understanding
4 quick questions on this lesson. Get them all right and the lesson is marked complete.