Authentication And Identity Systems
Authentication Model And Threat Boundaries
Authentication answers one question — "who is making this request?" — and it is worth separating from the three questions people routinely confuse it with. Identification is the claim ("I am alice@example.com"). Authentication is the proof of that claim. Authorization ("may this user do this?") is a separate decision made after authentication. And a session is how you avoid re-proving identity on every request. Systems break at the seams between these, so naming them precisely is the foundation everything else in this track builds on.
Factors And Why Multiple Factors Help
Proof of identity comes in three classic kinds: something you know (a password), something you have (a phone, a hardware key), and something you are (a fingerprint, a face). Single-factor authentication relies on one; multi-factor requires two of different kinds, which is the important detail — a password plus a security question is still one factor, because both are "something you know" and both leak the same way. The reason multi-factor works is that the attacker must now compromise two independent channels at once, and the cost of doing so rarely scales.
The Threat Boundary
Every authentication system has a boundary where an untrusted claim becomes a trusted identity. Draw it explicitly, because that boundary is exactly what attackers push on. Before the boundary, everything is hostile input: the username, the password, the token, the cookie, the "forgot password" email address. After it, your code acts on a trusted user_id. Most authentication vulnerabilities are really boundary failures — trusting a claim that was never proven, or proving it against the wrong thing.
The threats worth modeling by name, because each demands a specific defense:
<?php
declare(strict_types=1);
$threats = [
'credential stuffing' => 'reused passwords from other breaches tried in bulk',
'brute force' => 'many guesses against one account',
'phishing' => 'user tricked into handing credentials to an attacker',
'session hijacking' => 'stolen cookie or token used to impersonate',
'account enumeration' => 'learning which emails have accounts from system responses',
'password reset abuse' => 'taking over an account through its recovery flow',
];
foreach ($threats as $threat => $description) {
echo $threat . ': ' . $description . PHP_EOL;
}
// Prints:
// credential stuffing: reused passwords from other breaches tried in bulk
// brute force: many guesses against one account
// phishing: user tricked into handing credentials to an attacker
// session hijacking: stolen cookie or token used to impersonate
// account enumeration: learning which emails have accounts from system responses
// password reset abuse: taking over an account through its recovery flow
Notice that the recovery flow is on that list. The single most common way accounts get taken over is not cracking the password — it is the password reset, which is a parallel authentication path that teams often secure far more weakly than the front door. Your account is only as secure as its weakest login path, and every "forgot password", "log in with Google", and "email me a link" is another path to the same boundary.
The Enumeration Principle
Whether an account exists is itself a secret worth keeping, and it shapes the entire system's responses. Login failure, password-reset request, and registration must all answer identically for existing and non-existing accounts — same message, same timing, same shape — or the system becomes an oracle that hands attackers a validated list of your users to target. This principle recurs in nearly every lesson in this track; meet it here as a first-class design rule, not a per-feature afterthought.
What To Check
Before moving on, make sure you can:
- distinguish identification, authentication, authorization, and session
- explain why multi-factor requires factors of different kinds to help
- locate the trust boundary where an untrusted claim becomes a trusted identity
- name the standard authentication threats and what each targets
- explain why every login path — including recovery — is part of the attack surface
- state the enumeration principle and why responses must be uniform
What You Should Be Able To Do
After this lesson, you should be able to map any authentication feature onto these four concepts, draw its trust boundary, enumerate the threats it faces, and recognize when a system leaks account existence or secures its recovery path more weakly than its front door.
Practice
Practice: Map An Auth System's Boundaries
Show solution
- Email/password. Untrusted: the submitted email and password. The boundary is the password verification against the stored hash; after it, a trusted
user_id. Threats: credential stuffing, brute force, enumeration on the failure message. - Log in with Google. Untrusted: the authorization code / ID token arriving from the browser. The boundary is validating that token against Google's keys and issuer, plus matching or linking to a local account. After it, a trusted
user_id. Threat: accepting an unvalidated or wrong-audience token, and account-linking confusion (a Google email that collides with an existing password account). - Forgot password. Untrusted: the email address entered and, later, the reset token from the link. The boundary is the token's validity check. After it, the holder may set a new password — which means this path can become any account. Threats: enumeration on the request response, weak/guessable tokens, tokens that do not expire or single-use.
The key observation for the write-up: all three end at "trusted user_id", so the weakest of the three sets the system's real security. The reset path is the most dangerous because success grants full account control, so it deserves the strongest controls, not the afterthought treatment it usually gets.
Practice: Diagnose An Enumeration Leak
A login form returns "No account with that email" for unknown emails and "Incorrect password" for known ones. The password reset page says "We've sent a reset link" only for real accounts and "Email not found" otherwise. Registration says "That email is already taken."
Task
Explain what an attacker can build from these responses and how to fix each without hurting usability.
Show solution
All three surfaces leak account existence, and together they let an attacker compile a validated list of real user emails — the reconnaissance step before credential stuffing and phishing. Any one of the three is enough; having all three makes it trivial and cross-checkable.
Fixes, each preserving usability:
- Login: return one message for both cases — "Email or password is incorrect" — and ensure the timing matches too, by verifying against a dummy hash when the account does not exist so the response is not suspiciously fast. The user experience is unchanged; a legitimate user mistypes rarely and the combined message is still clear.
- Password reset: always respond "If an account exists for that email, we've sent a link." The real user gets their link; the attacker learns nothing. This is strictly better UX-neutral messaging.
- Registration: this one is genuinely hard, because you cannot silently accept a duplicate signup. The mitigation is to move the disclosure behind a rate-limited, human-paced flow: accept the registration attempt and send an email that says either "here's how to verify" (new) or "you already have an account, log in or reset" (existing), so the server response is uniform and the branching happens in the user's own inbox, which the attacker does not control.
The through-line: uniform responses across existing/non-existing, matched in message and timing, with any unavoidable disclosure pushed to a channel only the real owner can read.
Practice: Define Auth Model Review Checks
Write the review checklist that verifies a new authentication feature respects the identity model and threat boundary before it ships.
Show solution
- The trust boundary is identifiable in the code: one place where untrusted claim becomes trusted
user_id, and everything before it treats input as hostile. - Authorization is a separate decision after authentication, not conflated with it — being logged in is never mistaken for being allowed.
- Every response that could reveal account existence (login, reset, registration, MFA-challenge) is uniform for existing and non-existing accounts in both message and timing; a test asserts the timing bound.
- If the feature is a new login or recovery path, it is secured to the same standard as the existing strongest path, and someone has explicitly asked "does this become a weaker way in?"
- Multi-factor claims are checked for factor independence — no "two factors" that are really two things-you-know.
- The feature's threats are named in the PR description and each has a stated control, so review is against an explicit model rather than vibes.
The single most valuable habit encoded here: whenever a login or recovery path is added, re-evaluate the whole system's weakest path, because security is set by the minimum, not the average.