Identity, authorisation and the security basics
Every design so far assumed the caller is who they claim; that assumption is a subsystem.
Every chapter so far assumed the caller is who they claim. That assumption is a subsystem, and it fails differently from everything else you have built: a down system stops working, a broken auth system keeps working for the wrong people. This chapter is the load-bearing minimum every designer owes the system — not a security career, but the parts you cannot delegate.
Sessions vs tokens — where the risk lives
After login, something must carry “this is Alice” on every request. A server-side session (opaque cookie → session store) keeps state central: revocation is deleting a row, the cookie leaks nothing — at the cost of a lookup per request and a shared store (the reads chapter says: cache it). A JWT moves the state into a signed token: any service verifies it locally, no lookup, beautiful for service-to-service — and the signature is also the trap: a JWT cannot be un-issued. Until expiry it is valid, logout or breach notwithstanding — so JWTs live short (minutes), pair with revocable refresh tokens, and anything sensitive keeps a deny-list check anyway… at which point you have rebuilt half a session store and should ask if you needed the JWT. Browser cookies get the armour: HttpOnly, Secure, SameSite — three flags that neutralise whole attack classes.
OAuth and OIDC without the fog
OAuth 2 answers delegation — “let this app read my calendar without my password” — and OIDC adds “and tell the app who I am”. The modern guidance (this chapter’s reading, by a spec editor) collapses to one flow worth knowing cold: authorization code + PKCE — redirect to provider, user consents, app exchanges a one-time code (plus a proof only it holds) for tokens. PKCE exists because the older implicit flow put tokens in URLs, and URLs leak. Everything else in the spec zoo is a variant for machines (client credentials) or legacy to avoid.
Authorisation — the part you will get paged for
Authentication says who; authorisation says what — and its bugs are the boring catastrophic kind: /invoices/10413 served to the wrong tenant because someone checked logged in but not owns it (IDOR, perennial #1 in the wild). The discipline: RBAC for coarse structure (roles → permissions), attribute checks for the fine grain (owner, tenant, state) — but above all one enforcement point per resource type, because authorisation smeared across handlers is authorisation with gaps. In multi-tenant systems, make the tenant check structural, not disciplinary: tenant_id in every key/query by construction (the sharding chapter’s keys double as an isolation boundary), so forgetting the WHERE clause is hard, not habitual.
The transport and the secrets
Non-negotiables, briefly: TLS everywhere, including inside the perimeter — “internal” networks stopped being trustable a decade ago; mTLS (or a mesh doing it for you) gives services mutual identity. Secrets live in a manager with rotation and audit, never in env-files-forever, never in git (history is immutable; a leaked key is rotated, not deleted). Passwords — if you must hold them — are argon2/bcrypt-hashed, and the better answer is increasingly to not hold them at all (OIDC to an identity provider, passkeys). And the OWASP Top Ten is not compliance theatre; it is a design-review checklist — read your architecture against it once per major change and injection, broken access control and SSRF stop being surprises.