fedorthinks

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

§ 01

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.

Credentialpassword · passkey · tokenVerifyAuthNIdentityuser = Aliceanswers one question: WHO is this?
AuthN turns a credential (password, passkey, token) into an identity — and answers only the question “who is this?”
ConceptAuthentication (authN)· proving identity
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.

WhoAlice, editorActioneditResourcedoc #42Policy checkAuthZAllowDenyon every request
AuthZ takes who, what action, and which resource — and on every request decides: allow or deny.
ConceptAuthorization (authZ)· deciding permissions
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.

front door: who are you?every inner door: may you?UserAuthNonce per loginGET /orders/7AuthZ checkPUT /docs/3AuthZ checkDELETE /users/9AuthZ check
AuthN is one front door. AuthZ is a check at every inner door — that is, on every request.

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.

PracticeDon't Roll Your Own Auth· standards over DIY
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.

Two questionsAuthenticationAuthorizationNo DIY authProving identityPassword hashingMFAPasskeysStaying logged inSessionJWThttpOnly cookieDelegated accessOAuth 2.0OIDCAPI keyPermissionsLeast privilegeRBACABACOwnership checkFailure modesIDORToken leakageTrusting the client
Six groups of concepts — chapters 1–6 follow them. Chapter 7 is how to choose, plus the cheat sheet.

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.

§ 02

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.)

Passwordhunter2+ saltrandom, per userSlow hashArgon2 · bcryptDB stores$argon2id$…Stolen DBonly hashescracking is slow, one user at a time
Password + random salt → slow hash → only the hash is stored. A stolen database gives up no passwords.
PracticePassword Hashing· salted slow hash
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.

Something you knowpasswordSomething you haveTOTP app · security keySomething you arefingerprint · faceAccess2+ factorsany 2 of 3SMS code: weak (interceptable)
Three kinds of factors: know, have, are. Logging in takes at least two different ones.
PracticeMFA· Multi-Factor Authentication, 2FA
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.

Your deviceholds private keyreal-site.comstores public keyfake-site.comwrong origin1. challenge2. signature✗ no signature herekey never leaves device
The site sends a challenge, the device answers with a signature. A phishing site at a different address gets no signature.
ConceptPasskeys· WebAuthn, passwordless login
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.

§ 03

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.)

Request 1+ cookie / tokenit's still meRequest 2+ cookie / tokenit's still meRequest 3+ cookie / tokenit's still meServerforgets between calls
The server remembers no previous request, so every request carries a cookie or token with it.
ConceptStateless HTTP· no memory between requests
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.

Browsercookie: sid=8f3a…Serverlooks up the sidSession store8f3a → AliceDelete sessionlogout / ban = instantrevoke
The cookie holds only an id. The server looks it up in the session store. Delete the session — the user is out at once.
PatternSession· server-side session
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.

headeralgpayloadsub: alice · role · expsignaturesigned by issuer..Clientholds the JWTAPIverify signatureLeaked JWTvalid until expno lookup · statelessso: short expiry + refresh token
JWT = header.payload.signature. The API checks the signature without a database. A leaked token lives until exp — hence short lifetimes and a refresh token.
PatternJWT· JSON Web Token
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.

httpOnly cookieJS can't read itlocalStorageany script can read itInjected scriptXSSsteals token✗ blocked
An injected script (XSS) can read localStorage, but cannot read an httpOnly cookie.
PracticehttpOnly Cookie· a cookie JavaScript can't read
Problem
A token in localStorage is stolen by any injected script.
Idea
Keep the token in a cookie with the httpOnly flag — 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.

§ 04

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.

UserAuth serverGoogleYour appclientResource APICalendar1. logs in there + consentsyour app never sees the password2. access token · scope: read calendar3. call the API with the token
The user logs in at Google and consents. The app gets a narrowly scoped token and calls the API with it. It never sees the password.
ProtocolOAuth 2.0· delegated authorization
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.

"Log in with Google" · SSOOpenID Connect (OIDC)ID token: WHO the user isOAuth 2.0access token: WHAT the app may reachidentityaccess
OIDC is a thin layer over OAuth 2.0: next to the access token (“what's allowed”) comes an ID token (“who this is”).
ProtocolOIDC· OpenID Connect
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.

Service Aa program, not a personAPIchecks key + scopeheader: X-API-Key: sk_…✗ never in git✗ never in a URL↻ scope + rotate
The program sends the key in a header; the API checks the key and its scope. Never in git, never in a URL, rotated regularly.
ToolAPI Key· machine credential
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.

§ 05

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.

RequestExplicit grant?Allowthe minimum neededDenythe defaultyesno / unknown
An explicit grant — let it through, and only to the minimum needed. No grant, or unclear — deny.
PrincipleLeast Privilege· deny by default
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.

usersrolespermissionsAliceadmineverythingBobeditorpublish · editChenviewerread
User → role → set of permissions. Permissions go to roles, not to people directly.
ModelRBAC· Role-Based Access Control
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.

User attributesrole = managerResource attributesexpense: 8kContext14:00 · company laptopPolicyamount < 10k AND …Allow / Denyevaluated every time
Attributes of the user, the resource, and the context go into a policy that decides afresh on every request.
ModelABAC· Attribute-Based Access Control
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.

PUT /docs/42by BobRole checkeditor? ✓Owner checkdoc 42 owner = Bob?Allowonly if both passthe most-missed check
Two checks: the role (“an editor?”) and ownership (“is this doc theirs?”). Allow only if both pass.
PracticeOwnership Check· object-level authZ
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."

§ 06

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.

Boblogged inGET /orders/123Bob's order ✓GET /orders/124Ann's order!ServerauthN ✓ · owner check ✗fix: WHERE id = 124 AND owner = current_user
Bob logged in honestly, but the server doesn't check ownership — change the id in the URL and you see someone else's order.
Anti-patternBroken Access Control / IDOR· missing permission check
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.

MD5 / SHA-1fast hashCracked fastevery leaked user exposedArgon2 / bcryptslow + saltedToo costlyeven when stolenattacker guesses: cheapattacker guesses: expensive
A fast hash — cheap guesses, passwords cracked fast. A slow salted hash — every guess is expensive.
Anti-patternPlaintext / Fast Hash· passwords as text or MD5
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.

Token / API key?token= in URLends up in logslocalStorageread by XSSgit commitpublic foreverServer verifiessignature · expiry · scopeshort-lived · rotatable
Three classic leak paths: URLs in logs, localStorage, and git. The defence: the server verifies every token, lifetimes are short, keys rotate.
Anti-patternToken Leakage· leaked tokens and keys
What it is
A credential ends up somewhere it can be read or found.
Looks like

?token= in a URL, a token in localStorage, 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.

CLIENT · user-controlledhidden form fieldcookie: role=adminisAdmin=falseServerre-checks everything itselfsuggestionthe authority
Everything on the client side can be changed by the user. Only the server makes the decision.
Anti-patternTrusting the Client· client-side checks
What it is
Permission decisions made from data the client sent.
Looks like

A role in a cookie, a hidden field, an isAdmin flag 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.

§ 07

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.

Auth library / providerPassword hashingSessions · tokensMFA · passkeysOAuth · OIDCYour appowns AuthZ: the rulesidentity
The provider handles identity: hashes, sessions, MFA, OAuth. Your app owns the access rules.
ToolAuth Provider· auth library or service
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.

Normal web appSessionsMany services / APIsShort JWT + refreshSocial login / SSOOIDCMachine to machineAPI keyAdmin / high risk+ MFA
The need on the left, the mechanism on the right. Choose by the shape of the app.
Before shipping auth
  • 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?
Smell tests that it's broken
  • 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.
Signs you did it right
  • 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.

Password + hashMFAPasskeyOAuth2 / OIDCAPI keyAuthNwho?SessionJWThttpOnly cookiecarries itAuthZmay?every requestRBAC / ABACOwnership
Left to right: factors → authN → session or JWT → authZ on every request → roles/policy + ownership.

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.

End of express course · 7 chapters · 22 concepts

Next comes depth: the OWASP Top 10 and its cheat sheets, the OAuth 2.0 and OpenID Connect specs, and your auth provider's own guides. But carry the two questions everywhere — who are you, and what may you do? Answer the first with a proven tool, and the second on your server, every single time.