Authentication & Authorization
Almost every security system rests on two questions, asked in order. First: who are you? Second: what are you allowed to do? The first is authentication (often shortened to AuthN) — proving an identity. The second is authorization (AuthZ) — deciding what that proven identity may access. These are two distinct steps, and confusing them is one of the most common — and most expensive — sources of security bugs. This section walks from proving identity, through modern passwordless login, into how systems decide what each user can touch.
- Identity
- The claimed and verified set of attributes about an entity — a user, a service, or a device (for example "this is account alice@acme.com, an admin in the Finance department").
- Authentication (AuthN)
- The act of proving an identity is genuinely who it claims to be.
- Authorization (AuthZ)
- The act of granting or denying a specific action to an already-authenticated identity.
Request --> [ AuthN: who are you? ] --pass--> [ AuthZ: may you do X? ]
| |
fail fail
v v
401 Unauthorized 403 Forbidden
4.1 Authentication factors
Proof of identity comes from factors, grouped into three classic categories:
- Something you KNOW — a password, PIN, or security question. A shared secret in your head.
- Something you HAVE — a phone, a hardware key (like a YubiKey), a smartcard, or an authenticator app generating TOTP codes (Time-based One-Time Passwords — the rotating 6-digit numbers).
- Something you ARE — biometrics: fingerprint, face, or iris.
Two extra categories are sometimes added: somewhere you are (geolocation) and something you do (behavioral patterns like typing rhythm). MFA (Multi-Factor Authentication, or 2FA for exactly two) means requiring proof from two or more different categories.
4.2 Passwords: why they are weak
Passwords are a shared secret the server must store, and humans handle them badly: they reuse one password everywhere (so one breach enables credential stuffing — replaying stolen logins across many sites), pick guessable ones, and fall for phishing. The data is damning. IBM's Cost of a Data Breach 2025 found the global average breach cost fell to $4.44M, but the US hit a record $10.22M. Phishing overtook stolen credentials as the #1 initial attack vector (16% of breaches), while breaches using stolen/compromised credentials took the longest to detect and contain — an average of 292 days.
Modern password policy (NIST SP 800-63B Rev. 4)
NIST's 2024/2025 guidance reversed decades of bad advice:
- Minimum 8 characters (15 if the password is the only factor); support at least 64; allow all printable and Unicode characters, including spaces.
- Do NOT force periodic rotation — no 60/90-day expiry. Forced changes just produce
Password1 → Password2. Require a change only on evidence of compromise. - Drop composition rules (no mandatory "1 uppercase + 1 symbol").
- Screen new passwords against breach blocklists (e.g. HaveIBeenPwned).
- Allow paste — it helps password managers.
Password storage done right
Never store plaintext, never a plain fast hash (MD5/SHA-256 — GPUs crack billions per second), and never encrypt (that's reversible). Use a slow, memory-hard password hashing function with a unique random salt per password (random data mixed in, stored alongside the hash, defeating precomputed "rainbow tables"). OWASP currently recommends Argon2id (min 19 MiB memory, 2 iterations). Acceptable alternatives: bcrypt (work factor ≥10, 72-byte input limit) or PBKDF2 (for FIPS environments). Optionally add a pepper — a secret HMAC key kept out of the database. Use a reputable library; never hand-roll this.
4.3 MFA: strong, but not bulletproof
MFA dramatically reduces account takeover, but attackers adapt. Factors form a strength hierarchy:
weaker SMS OTP < TOTP app < push approval < FIDO2/passkey stronger
(SIM-swap) (AiTM) (fatigue) (phishing-resistant)
- SIM swapping — attacker ports your phone number to their SIM, intercepting SMS codes (eSIM provisioning can do this in minutes).
- MFA fatigue / push bombing — spamming approval prompts until the tired user taps "approve." The 2025 Verizon DBIR ties this to roughly 14% of incidents.
- Adversary-in-the-Middle (AiTM) — a fake login page relays your password AND your OTP to the real site in real time, defeating TOTP and push. Phishing-as-a-service kits like Tycoon 2FA and EvilProxy industrialize this.
4.4 Passkeys, WebAuthn, and FIDO2 (the 2025 trend)
The clearest direction of travel is passwordless login built on public-key cryptography. WebAuthn (a W3C browser API) plus CTAP (the device protocol) together make up FIDO2. At registration your device generates a key pair; the private key never leaves the device (sealed in a secure enclave or TPM), and the server stores only the public key. Logging in means signing a server-issued challenge, unlocked by your biometric or PIN.
Why it is phishing-resistant: the credential is cryptographically bound to the site's origin (its domain), so a fake site can't trigger it; there is no shared secret to steal, phish, or breach. "Synced passkeys" roam across your devices via Apple/Google/Microsoft/1Password clouds; "device-bound" passkeys (hardware keys) stay put.
4.5 Sessions vs tokens, and cookie flags
Once authenticated, the server must remember you across requests. Two approaches:
| Aspect | Stateful Session | Stateless Token (JWT) |
|---|---|---|
| Where state lives | On the server; client holds an opaque session ID | Inside the token itself (the claims) |
| Each request | Server looks up the session | Server only verifies the signature |
| Revocation | Easy — delete server-side | Hard — valid until it expires |
| Scaling | Needs a shared session store | Great for APIs/microservices |
A common hybrid: a short-lived access token (5–15 min) plus a long-lived refresh token that rotates on each use. Store the refresh token in an HttpOnly cookie; keep the access token in memory.
Cookie security flags are the seatbelts here: HttpOnly stops JavaScript from reading the cookie (blunting theft via XSS); Secure sends it only over HTTPS; SameSite (Strict / Lax / None) controls cross-site sending and is the primary defense against CSRF (Cross-Site Request Forgery). In 2025 browsers block third-party cookies and enforce SameSite by default.
4.6 OAuth 2.0 vs OpenID Connect, SSO, and SAML
OAuth 2.0 is an authorization framework, not a login protocol. It lets an app get delegated access to your resources without your password ("let this app read your Google Drive files"). Roles: resource owner (you), client (the app), authorization server, and resource server; access is granted via scopes. Use the Authorization Code flow with PKCE for web, mobile, and single-page apps.
OpenID Connect (OIDC) is a thin identity layer on top of OAuth 2.0. It adds an ID Token (a JWT with verified identity claims like sub and email) so the app knows who logged in. OIDC powers "Sign in with Google/Apple."
SSO (Single Sign-On) means logging in once to reach many apps — a user experience built on a federation protocol. SAML 2.0 is the older XML-based enterprise standard (an Identity Provider like Okta or Entra sends signed XML assertions to a Service Provider). Rule of thumb: SAML for legacy enterprise SSO, OIDC for new or consumer apps.
4.7 JWTs: structure and pitfalls
A JWT (JSON Web Token) has three base64url parts: header.payload.signature. The header names the algorithm, the payload holds claims, and the signature covers the first two. Base64 is not encryption — anyone can read the payload, so never put secrets in it.
alg:none— the spec allows an "unsigned" token; a careless library lets an attacker strip the signature and forgeadmin:true. Fix: a server-side algorithm allowlist (e.g. only RS256); rejectnone.- alg confusion (RS256→HS256) — attacker signs using the public key as if it were the HMAC secret.
- Weak HMAC secret — brute-forced; use a ≥256-bit random secret.
- No/long expiry — always set a short
exp, and validateexp,nbf,iat,iss,aud,jti. - Storing JWTs in localStorage — XSS-stealable; prefer HttpOnly cookies.
4.8 Authorization models
| Model | Decides access by… | Strength / weakness |
|---|---|---|
| RBAC (Role-Based) | roles assigned to users (admin/editor/viewer) | Simple, ubiquitous; "role explosion," no row-level rules |
| ABAC (Attribute-Based) | attributes of user/resource/environment | Flexible ("manager in Finance, work hours"); hard to audit |
| ReBAC (Relationship-Based) | relationships between entities | Powerful ("view a doc if you can view its folder"); a superset |
| PBAC (Policy-Based) | centralized policies outside app code | Auditable, reusable (OPA, Cedar); extra infrastructure |
object#relation@user tuples — over 2 trillion of them, ~10M requests/sec, p95 under 10ms. It inspired OpenFGA, SpiceDB, and Auth0 FGA. Modern apps increasingly move authorization into a dedicated service instead of scattering if-checks through the code.4.9 Least privilege and Zero Trust
The principle of least privilege grants the minimum access needed, for the shortest time — shrinking the "blast radius" if an account is compromised. It pairs with just-in-time access and regular access reviews (orphaned accounts are a top breach vector).
Zero Trust (NIST SP 800-207) means "never trust, always verify." It abolishes implicit trust based on network location — "inside the firewall" is meaningless. Every request is authenticated, authorized, and encrypted per session, evaluated on signals like user identity, device health, and behavior. A Policy Decision Point (PDP) decides; a Policy Enforcement Point (PEP) enforces — replacing the old "castle-and-moat" perimeter model.
4.10 The #1 mistake: Broken Access Control
Broken Access Control is #1 (A01) in the OWASP Top 10:2025 — essentially every tested app had some form of it. The canonical case is IDOR / BOLA (Insecure Direct Object Reference / Broken Object Level Authorization): an app fetches an object by a user-controllable ID without checking ownership. Change GET /api/orders/12345 to 12346 and you read someone else's order. The root cause is authorization checks done client-side, forgotten on an endpoint, or relying on "they can't see the link" (security by obscurity).
Common mistakes
- Confusing AuthN with AuthZ — proving identity but never checking permission per action.
- Using OAuth for login without OIDC, so identity is never actually verified.
- Trusting JWT claims without validating signature, algorithm, and expiry.
- Shipping 2010-era password rules (forced rotation, composition) NIST now warns against.
- No MFA on privileged or admin accounts.
- Relying on unguessable IDs alone instead of a real ownership/tenant check.
Best practices
- Enforce authorization on every request, server-side, deny-by-default.
- Check ownership and tenant scope on every object lookup.
- Hash passwords with Argon2id (or bcrypt/PBKDF2) and screen against breach lists.
- Roll out phishing-resistant passkeys/FIDO2; require MFA on all privileged accounts.
- Use short-lived access tokens with rotating refresh tokens in HttpOnly+Secure+SameSite cookies.
- Centralize authorization (PBAC/ReBAC) rather than scattering ad-hoc checks.
- Apply least privilege with just-in-time access and routine de-provisioning.