Express course · No. 08
Two questions hide behind one word, 'auth.' Authentication proves who you are; authorization decides what you're allowed to do. Confuse them, skip either, or trust the client, and the door is open.
22 concepts · a diagram for each · cheat sheet at the end
Behind the word "auth" hide two different questions. Most security bugs come from confusing them — or from answering one and forgetting the other.
Authentication (authN) — who are you?
Showing your passport at the airport — proving you really are the person you claim to be.
This is the concept of Authentication (authN), and it means proving identity: a login, a passkey, a token that says "this really is Alice." It usually happens once per session. Get it wrong and an impostor walks in as someone else. But proving who you are says nothing about what you may do — that's the other question entirely.
- What it is
- Checking that the user is who they claim to be.
- How it works
The user presents a credential (password, passkey, token); the system verifies it and establishes an identity.
- Example
- Email-and-password login: "this is Alice."
- Catch
- Knowing who someone is doesn't tell you what they may do.
- Related
Feeds Authorization; done via Password Hashing, MFA, Passkeys, OIDC.
Authorization (authZ) — what may you do?
A hotel keycard — it proves nothing about your name, but it opens your room and not the others, and not the manager's office.
This is the concept of Authorization (authZ): deciding what an identity is allowed to do — read this, edit that, never touch the admin panel. Unlike authN, it has to be checked on every request, because one logged-in user may touch a hundred different things. Knowing who someone is is not permission to do anything in particular.
- What it is
- Deciding what this identity may do with this resource.
- How it works
On every request: who + action + resource → rule → allow or deny.
- Example
- Alice the editor may edit doc #42, but may not open the admin panel.
- Catch
Checking once and forgetting. It must be checked on every request, server-side.
- Related
Models — Least Privilege, RBAC, ABAC, Ownership Check; failure — Broken Access Control.
Two layers — never collapse them
A nightclub: the bouncer checks your ID at the door once, and the bartender still checks you're of age every time you order. Two checks, two moments.
The classic mistake is fusing the two — proving identity once and trusting that forever, or stuffing every permission into a login token and never re-checking. AuthN happens at the door; authZ happens at every door inside. Keep them separate, and check the second one constantly.
Don't Roll Your Own Auth — never build your own
You don't weld your own bank vault from sheet metal in the garage — you buy one built and tested by people who do nothing else.
This is the practice of Don't Roll Your Own Auth. Identity is the foundation everything else stands on, and it's brutally easy to get subtly wrong. Use proven standards and libraries — established protocols, a battle-tested auth library or provider — not a clever scheme you invented. The history of security is paved with home-made auth that looked fine until it suddenly wasn't.
- Problem
- Home-made auth looks fine until someone breaks it.
- Idea
- Use proven protocols, libraries, and providers.
- Example
- Instead of your own token scheme — OIDC and your framework's auth.
- Cost
- You have to learn someone else's tool and wire it in correctly.
- When to use
- Almost always.
- Related
- Leads to Auth Provider; standards — OAuth 2.0, OIDC.
The map: 22 concepts in six groups
A keyring: first you see the whole bunch, then you learn which key opens which door.
The course's concepts fall into six groups. Each answers its own question: what the two "auths" are, how to prove identity, how to stay logged in, how to grant access without a password, how to decide permissions, and how it all usually breaks.
Every concept below gets the same treatment: its English name (that's the term you'll search docs for), a picture from life, a diagram, and a card with fields. At the end, a cheat sheet with every concept.
Authentication is the door. Authorization is every door inside. Skip the second and the first means nothing.
Authentication comes down to proving you're you. The factors range from the weak-but-universal password to the modern, phishing-resistant passkey.
Password Hashing — passwords, the weak default
A secret word shared with the doorman — fine until someone overhears it, guesses it, or you use the same one everywhere.
Passwords are universal and the weakest common factor: reused, guessable, phishable. If you must use them, the non-negotiable rule is never store them as text. Store a slow, salted hash (Argon2, bcrypt) — that's Password Hashing — so a stolen database doesn't hand over everyone's password. (More on that in the mistakes chapter.)
- Problem
- Passwords stored as text: one leak and the attacker has them all.
- Idea
Store not the password but a slow hash with a salt — a different one per user.
- Example
- On login, compute Argon2/bcrypt of the typed password and compare.
- Cost
- Login is a bit slower — on purpose.
- When to use
- Always, if you store passwords.
- Related
- The anti-example is Plaintext / Fast Hash; add MFA on top.
MFA — something you know, have, are
A safe-deposit box needs your key and the bank's key. One alone opens nothing.
MFA (Multi-Factor Authentication), or two-factor, demands a second proof beyond the password — something you have (a code from an app, a security key) or something you are (a fingerprint). Even a stolen password is then useless alone. An authenticator-app code (TOTP) is solid; SMS codes are weak (interceptable) but better than nothing. MFA is the single biggest, cheapest security win you can add.
- Problem
- One stolen password = full access to the account.
- Idea
- Require a second factor of a different kind: "have" or "are."
- Example
- Password + a six-digit TOTP code from an authenticator app.
- Cost
- An extra step at login; SMS codes can be intercepted.
- When to use
- Wherever it matters; mandatory on admin accounts.
- Related
- Strengthens Password Hashing; Passkeys are the next step.
Passkeys — the passwordless future
A key that only works at the one door it was cut for — fake the website, copy the lock's photo, and the key simply won't turn.
Passkeys (built on the WebAuthn standard) replace passwords with a cryptographic key pair tied to your device and the real site, so there's nothing to phish, reuse, or leak — and Apple, Google, and Microsoft now default to them. The private key never leaves your device; the site only ever sees a signature. This is where authentication is heading: nothing to steal.
- Problem
- Passwords get phished, reused, and leaked.
- Idea
A key pair: the private key stays on the device, the site holds the public key. Logging in is a signature.
- Example
Signing in with a fingerprint or Face ID instead of typing a password.
- Cost
- Needs device support and a fallback recovery path.
- When to use
- As the main login wherever users are on modern devices.
- Related
- Replaces Password Hashing; on its own, stronger than SMS MFA.
A password is a secret you can lose. A passkey is a proof you can't. Add a second factor either way.
HTTP forgets you between requests, so after you log in the system needs a way to keep recognising you. Two approaches, with a real trade-off.
Stateless HTTP — HTTP has no memory
A shop where the clerk forgets you the instant you turn around — so you need a ticket to prove, on your next step, that you already paid.
HTTP is stateless: every request is independent; the server doesn't remember that you logged in a second ago. So after authentication you're handed something — a cookie or a token — that you send with each request to say "it's still me." Everything below is two ways to do that. (This is the statelessness from the protocols course.)
- What it is
- The server doesn't remember the client's previous requests.
- Consequence
- After login you need a "pass" that travels with every request.
- Example
The browser attaches the cookie to every request to that site by itself.
- Related
- Two answers — Session and JWT.
Session — the server remembers
A coat check — you hold a little numbered ticket, and all the real information sits behind the counter under that number.
With sessions, the server stores who you are and hands you a cookie holding only a random session id. Each request, it looks the id up. The server is the source of truth, so logging out or banning someone is instant — just delete the session. The cost is that the server must store and look up that state. The right default for most web apps.
- Problem
- HTTP doesn't remember who you are between requests.
- Idea
The server keeps the login data; the client holds only a random session id.
- Example
- Cookie
sid=8f3a…→ in the store,8f3a → Alice. - Cost
- The server needs a store and a lookup on every request.
- When to use
- A normal web app. This is the default.
- Related
The alternative is JWT; the id lives in an httpOnly Cookie; revocation is instant.
JWT — the token carries the truth
A festival wristband with your access level already printed and tamper-sealed on it — the guard checks the seal, not a guest list.
A JWT (JSON Web Token) is a signed token the client holds that contains the identity and its claims; the server just verifies the signature, no lookup needed. That makes it stateless and easy to scale across services — but hard to revoke: a leaked token stays valid until it expires. So you keep lifetimes short and pair it with a refresh token. Great for APIs and microservices; overkill for a simple web app.
- Problem
Many services, and making each one hit a shared session store is expensive.
- Idea
- Put the identity and permissions into the token itself and sign it.
- Example
Payload
sub: alice, role: editor, exp: …; each service verifies the signature itself.- Cost
- Hard to revoke: a leaked token is valid until it expires.
- When to use
- APIs and microservices — a short-lived JWT + a refresh token.
- Related
- The alternative to Session; the threat is Token Leakage.
httpOnly Cookie — where you keep it matters
A key in a locked drawer versus a key taped to the front door — same key, very different safety.
A token in the browser belongs in an httpOnly cookie (JavaScript can't read it), not in localStorage, where any injected script can steal it. The store is part of the security: get it wrong and one XSS bug hands an attacker every session. How you transport and store the credential is as important as the credential itself.
- Problem
- A token in
localStorageis stolen by any injected script. - Idea
- Keep the token in a cookie with the
httpOnlyflag — JS can't see it. - Example
Set-Cookie: sid=…; HttpOnly.- Cost
- Almost none — it's the right default in the browser.
- Related
Holds a Session id or a JWT; guards against Token Leakage.
Sessions: the server remembers, and can forget you instantly. JWT: the token remembers, and you can't easily take it back.
Often you don't want to handle the password at all — you want users to sign in with an account they already have, or one service to act for another. That's what OAuth2 and OIDC are for.
OAuth 2.0 — grant access without sharing a password
A valet key that starts the car and opens the door, but not the boot or the glovebox — you hand over limited access, not your whole keyring.
This is the OAuth 2.0 protocol. It lets a user grant your app limited access to their account on another service — your app reads their Google Calendar — without ever giving you their Google password. The other service hands your app an access token limited by a scope ("read calendar, nothing else"). It's about delegated authorisation, not identity.
- Problem
An app needs access to someone's account elsewhere, but can't have the password.
- Idea
The owning service issues the app an access token with a narrow scope.
- Example
- Your app reads the user's Google Calendar — and only that.
- Cost
- A complex protocol; doing the token dance by hand is dangerous.
- When to use
- Acting on a user's behalf in another service.
- Related
- Doesn't say who the user is — that's OIDC on top.
OIDC — OAuth2 that also says who you are
The valet key, now with a photo ID clipped to it — not just "what this key opens," but "and here's who it belongs to."
OAuth2 alone tells you what an app may access, not who the user is. OpenID Connect (OIDC) adds a thin identity layer — an ID token with verified claims (this is Alice, here's her email) — on top. This is what powers "Log in with Google" and single sign-on (SSO). When you want social login or SSO, OIDC is the standard.
- Problem
- OAuth2 grants access but doesn't say who the user is.
- Idea
- Add an ID token with verified identity data.
- Example
- The "Log in with Google" button; signing into work tools via SSO.
- Cost
- You depend on an external identity provider.
- When to use
- Social login and single sign-on.
- Related
- Built on OAuth 2.0; a way to do Authentication.
API Key — the simple machine credential
A spare key you give a trusted contractor — no name attached, just "whoever holds this may come in."
An API key is a long secret string that identifies a program, not a person — used for service-to-service and programmatic access. Simple and effective, but it is the credential: anyone who finds it is in. So you scope it, rotate it, and never commit it to git or put it in a URL. Good for machines; not a substitute for real user identity.
- Problem
- A program needs into an API with no human and no login.
- Idea
- A long secret that identifies the program.
- Example
- A backend calls a payments API with the key in a header.
- Cost
- The key is the access: find it and you're in.
- When to use
- Machine-to-machine. Not for logging in people.
- Related
Scope it by Least Privilege; the main threat is Token Leakage.
OAuth2 borrows access without the password. OIDC adds "and here's who they are." Use the standard — don't invent your own token dance.
Once you know who someone is, you decide what they may touch. The models run from coarse roles to fine-grained policy — and the rule under all of them is deny by default.
Deny by Default / Least Privilege — forbidden unless allowed
A new employee's badge that opens nothing until each door is explicitly granted — rather than opening everything until someone remembers to lock a few.
The foundation of authorisation is two rules. Deny by default: everything is forbidden unless explicitly allowed. Least privilege: each identity gets the minimum access it needs, no more. Default-open systems leak — someone always forgets to restrict the new endpoint. Default-closed systems fail safe. Start from "no" and grant deliberately.
- Problem
- Someone forgot to lock the new endpoint — and it's open to everyone.
- Idea
- "No" by default; grant only the minimum needed.
- Example
- A new employee gets access to their own project only.
- Cost
- You have to grant permissions explicitly more often.
- When to use
- Always — it's the base of every permission model.
- Related
- The foundation for RBAC, ABAC, Ownership Check.
RBAC — permissions by role
A theatre where your ticket type — stalls, circle, backstage pass — decides where you can go, without naming you personally.
RBAC (Role-Based Access Control) groups permissions into roles — admin, editor, viewer — and assigns users to roles. Simple, readable, and enough for most apps: "editors can publish, viewers can't." It gets coarse when rules depend on context or on the specific record, which is where the next models come in.
- Problem
- Granting permissions to each person individually is chaos.
- Idea
- Permissions go to roles; roles go to users.
- Example
- "Editors can publish; viewers can only read."
- Cost
- Coarse when a rule depends on context or the specific record.
- When to use
- Most apps.
- Related
- Finer-grained — ABAC; always add an Ownership Check.
ABAC — permissions by policy
A rule rather than a list — "managers may approve expenses under 10k, in business hours, from a company device" — evaluated fresh each time.
ABAC (Attribute-Based Access Control) decides from attributes of the user, the resource, and the context — role and amount and time and device. Far more flexible than fixed roles, and far more complex. The signal to adopt it: you're stacking more than a couple of custom if conditions onto your role checks.
- Problem
- Permissions depend not just on the role but on amount, time, device.
- Idea
- A policy rule over attributes of user, resource, and context.
- Example
A manager approves an expense under 10k, in business hours, from a work laptop.
- Cost
- Much harder to write, read, and test.
- When to use
When more than a couple of custom
ifs are stuck onto your roles.- Related
- Grows out of RBAC; stands on Least Privilege.
Ownership Check — can you touch this record?
You have a valid library card (a role), but that doesn't let you read the notes scribbled in someone else's borrowed book.
The ownership check (object-level authorization) is the most-missed check: even a valid "editor" must only edit their own documents, not everyone's. The role says "editors may edit"; you still have to verify this editor owns this row. Forgetting this is the single most common access-control bug — change the id in the URL and you're in someone else's data. Always check the object, not just the role.
- Problem
- The role allows "edit documents" — and the user edits other people's.
- Idea
- Besides the role, check that this object belongs to this user.
- Example
- Before
PUT /docs/42, check that doc 42's owner is the current user. - Cost
- An extra check on every object access.
- When to use
- Always, whenever data has an owner.
- Related
- Complements RBAC/ABAC; without it — IDOR.
Deny by default. Grant the least. And always ask not just "what role" but "whose record."
Auth fails in a handful of well-known ways — the same ones, over and over, in app after app. Knowing them is half the defence.
Broken Access Control / IDOR — forgetting to check
A building where the front door is guarded but every internal door is unlocked — once you're in, you can walk anywhere.
Broken Access Control is the number-one web vulnerability: an endpoint authenticates the user but forgets to check they're authorised for what they asked. Its commonest form is IDOR (Insecure Direct Object Reference) — changing /orders/123 to /orders/124 and seeing someone else's order, because the server never checked ownership. Breaches of millions of records trace to exactly this. Check authZ on every request, server-side, against the actual object.
- What it is
The user is authenticated, but rights to the object aren't checked.
- Looks like
/orders/123→/orders/124— and someone else's order shows up.- Damage
- Breaches of millions of records.
- Fix
authZ on every request, server-side:
WHERE id = 124 AND owner = current_user.- Related
- Cured by Ownership Check and Least Privilege.
Plaintext / Fast Hash — storing passwords wrong
Keeping the spare keys to every house on the street in a glass jar on the porch.
Storing passwords as plaintext — or with a fast hash like MD5 — means one database leak exposes everyone, and reused passwords compromise their other accounts too. Passwords must be slowly and individually hashed (Argon2, bcrypt) so they're expensive to crack even when stolen. The hash being slow is the point, not a flaw.
- What it is
- Passwords in the database as-is or under a fast hash.
- Damage
One leak exposes every password; reused passwords open other accounts too.
- Fix
- A slow salted hash: Argon2 or bcrypt.
- Related
- The right version is Password Hashing.
Token Leakage — leaking and trusting tokens
Writing the alarm code on a sticky note stuck to the door — the lock is fine; you just handed out the secret.
Token leakage is how tokens and keys escape. They leak through URLs (which get logged), browser localStorage (which a script can read), and secrets committed to git. And a token must be verified, not trusted because it showed up — a tampered or expired one must be rejected. Treat every credential as something that will leak someday: short lifetimes, rotation, and never in a place that gets logged or scraped.
- What it is
- A credential ends up somewhere it can be read or found.
- Looks like
?token=in a URL, a token inlocalStorage, a key in a git commit.- Damage
- Whoever finds the token is in.
- Fix
Verify signature and expiry; short lifetimes, rotation; an httpOnly Cookie in the browser.
- Related
- Hurts most with JWT and API Key.
Trusting the Client — believing what the browser says
A shop that lets customers write their own price tags and never checks them at the till.
This is the anti-pattern of trusting the client. Anything the client sends — a hidden form field, a role in a cookie, an isAdmin=false flag — can be forged. The browser, the app, the request are all under the user's control. Every real check must happen on the server, which the user can't edit. The client is a suggestion; the server is the authority.
- What it is
- Permission decisions made from data the client sent.
- Looks like
A role in a cookie, a hidden field, an
isAdminflag in the request.- Damage
- The user grants themselves permissions.
- Fix
- Every check on the server; the client only suggests.
- Related
- A common cause of Broken Access Control.
Almost every auth breach is one of these: a check that was skipped, a secret stored badly, or the client believed.
Good auth is mostly about using proven tools correctly and checking permissions relentlessly — not cleverness. Here's how to choose.
Auth Provider — use a library or a provider
You don't manufacture your own seatbelts — you fit certified ones designed by specialists and crash-tested for years.
For almost everyone, the right move is to not build auth yourself: use your framework's auth, or an auth provider (Auth0, Clerk, Supabase, Cognito, and the like) that handles hashing, sessions, MFA, OAuth, and the edge cases you'd never think of. Your job is to wire it in correctly and own the authorisation — the part only you know.
- Problem
- Hand-built auth almost always ends up with holes.
- Idea
- Hand authN to a proven tool; keep authZ for yourself.
- Example
- Auth0, Clerk, Supabase, Cognito, or your framework's built-in auth.
- Cost
- A vendor dependency; it has to be wired in correctly.
- When to use
- Almost always.
- Related
- Puts Don't Roll Your Own Auth into practice.
Match the mechanism to the app
A house key, a car key, and a hotel keycard are all "keys" — you pick by the door, not by fashion.
Sessions for a normal web app (simple, instantly revocable); short-lived JWTs plus refresh when you need stateless scale across services; OIDC for social login and SSO; API keys for machine-to-machine. Add MFA everywhere it matters, especially admin accounts. Choose by the shape of your app, not by what sounds advanced.
- Are passwords slowly hashed (Argon2/bcrypt), never stored as text?
- Is MFA available, and required for admins?
- Is every endpoint checking authZ — the role and the ownership of the object?
- Are tokens in httpOnly cookies, short-lived, and never in URLs or git?
- Is the real check on the server, never trusting client-sent roles?
- Am I using a proven library/provider instead of a hand-rolled scheme?
- Changing an id in the URL shows someone else's data.
- A permission lives in a cookie or hidden field the client can edit.
- Passwords stored in plain text or MD5.
- No way to revoke a logged-in user except waiting for expiry.
- No MFA on the accounts that can do the most damage.
- You wrote your own token or crypto scheme.
- AuthN and authZ are separate, and authZ is checked on every request.
- Every object access verifies this user owns this thing.
- Credentials are hashed, scoped, short-lived, and rotatable.
- The server is the authority; nothing trusts the client.
- You can revoke access and see who did what in a log.
- The heavy lifting sits in a proven library or provider, not your own code.
How it all connects
A hotel guest's path: a passport at reception, a keycard in hand, and every door decides for itself whether to open.
Login factors (password, MFA, passkey, OIDC, API key) lead to authN. Its result travels with every request in a session or a JWT, best kept in an httpOnly cookie. On every request, authZ decides by roles or policy — and always checks ownership of the object.
Cheat sheet: all 22 concepts
Two questions
- Authentication (authN)proving identity
Prove who you are. Usually once per login.
- Authorization (authZ)deciding permissions
Decide what you may do. On every request.
- Don't Roll Your Own Authstandards over DIY
Use proven standards and libraries, not your own scheme.
Proving who you are
- Password Hashingsalted slow hash
Store a slow salted hash (Argon2, bcrypt), never the password.
- MFAmulti-factor, 2FA
A second factor: something you have or are. The cheapest big win.
- PasskeysWebAuthn
A key pair on the device, login by signature. Nothing to phish.
Staying logged in
- Stateless HTTPno memory between requests
The server forgets requests — every one needs a pass.
- Sessionserver-side session
The cookie holds an id; the truth is on the server. Instant revocation. The default.
- JWTJSON Web Token
A signed token with claims inside. No lookup, but hard to revoke.
- httpOnly Cookiea cookie JS can't read
Where a browser token belongs. XSS can't read it.
Delegated access
- OAuth 2.0delegated authorization
Access to someone's account via a narrowly scoped token, no password.
- OIDCOpenID Connect
OAuth2 + an ID token: "and here's who it is." Social login and SSO.
- API Keymachine credential
A secret for programs. Narrow scope, rotation, never in git or a URL.
Permissions
- Least Privilegedeny by default
"No" by default; grant only the minimum needed.
- RBACRole-Based Access Control
Permissions by role: admin, editor, viewer.
- ABACAttribute-Based Access Control
Permissions by a policy over user, resource, and context attributes.
- Ownership Checkobject-level authZ
Not just "what role" but "whose record."
How it breaks
- Broken Access Control / IDORmissing permission check
The check was skipped: change the id in the URL, see someone else's data.
- Plaintext / Fast Hashpasswords as text or MD5
One database leak — every password out.
- Token Leakageleaked tokens
Tokens in URLs, localStorage, git. Whoever finds one is in.
- Trusting the Clientclient-side checks
The client is a suggestion; the server is the authority.
Doing it right
- Auth Providerauth library or service
Hand authN to a proven tool; keep authZ for yourself.
Auth isn't where you get clever. It's where you use standards correctly, deny by default, and never, ever trust the client.