Authentication vs Authorisation
Date: 2026-08-17
Authentication is who you are; authorisation is what you may do. They’re separate systems, they fail separately, and the second is where nearly every real breach happens — because authentication is a solved problem people buy, and authorisation is bespoke logic people write.
Authentication (authn) establishes identity. Authorisation (authz) determines permissions.
AUTHENTICATION AUTHORISATION
who are you? what may you do?
login permission check
once per session on EVERY request
401 Unauthorised 403 Forbidden
← misnamed; ← correctly named
means "not
authenticated"
HTTP’s 401 is historically misnamed. It means “authenticate and retry”; 403 means “authenticated, still not allowed” — HTTP Semantics.
The factors
SOMETHING YOU KNOW password, PIN
SOMETHING YOU HAVE phone, hardware key
SOMETHING YOU ARE fingerprint, face
Multi-factor means factors from different categories. A password plus a security question is one category twice, and adds very little.
STRENGTH, ROUGHLY
SMS codes weakest — SIM swapping,
interception
TOTP app good
push approval good, vulnerable to
fatigue attacks
passkeys / strongest — phishing-
WebAuthn resistant by design,
because the credential is
bound to the origin
Passkeys are phishing-resistant for a structural reason: the credential only works on the domain it was created for, so a lookalike site cannot use it. No amount of user education achieves that.
Why authorisation is where breaches happen
Authentication is largely a solved problem you can buy. Authorisation is business logic, written per application, and rarely tested adversarially.
The most common failure has a name — broken object level authorisation, and it’s mundane:
GET /api/orders/1042
→ checks: are you logged in? ✓
→ does NOT check: is order 1042 yours?
change the number → read someone
else's order
Authentication passed. Authorisation was never performed. This is consistently among the most commonly found web vulnerabilities, and it appears in mature codebases constantly because each endpoint has to remember the check.
Related shapes:
FUNCTION-LEVEL the admin endpoint is
hidden in the UI but
not protected server-side
MASS ASSIGNMENT POST includes
{"role": "admin"} and
the ORM writes it
CLIENT-SIDE ONLY the button is hidden,
the API is open
Anything enforced only in the UI is not enforced. The browser is the attacker’s tool, not your control point.
Models
RBAC role-based
user has roles, roles have permissions
simple, ubiquitous, coarse
ABAC attribute-based
rules over attributes of user,
resource and context
flexible, harder to reason about
ReBAC relationship-based
"can edit if they own it, or if their
team owns it"
fits real products well
Most applications need “does this user own this thing”, which is relationship-based, and end up bolting it onto RBAC one endpoint at a time. Recognising that early saves a lot of scattered checks — Authorisation Models for the models in full and where the check belongs.
Making it hard to get wrong
- Deny by default. Authorisation should be a middleware or policy layer that must be explicitly satisfied, not a check each route remembers to include
- Authorise the object, not just the route. Fetch the resource, then check ownership, on every request
- Scope the query instead of checking after.
WHERE user_id = ?in the query cannot be forgotten downstream, whereas a post-fetch check can - Never trust a client-supplied identity. The user ID comes from the session, never from the request body
- Allow-list the fields you accept. Mass assignment is prevented by never binding raw input to a model
- Test with a second account. The single most effective check: log in as user B and request user A’s resources by ID
Where it sits
Authentication produces a credential; authorisation consumes it on every request — Sessions and Tokens, OAuth and OpenID Connect, Common Vulnerabilities. Where each lives in a system is Authentication Architecture.