A tutorial on web authentication from the front end's point of view, starting from why HTTP forgets you. Ten traced conversations between the browser, your app, the authorization server and your API — the authorization code flow request by request, what PKCE adds, why the implicit flow was retired, a JWT decoded live to show the payload is not encrypted — and then the decision every frontend developer has to make, with the same XSS and the same CSRF run against localStorage and an HttpOnly cookie side by side. Every claim carries its RFC section, and the cookie behaviour was measured in a real browser.
A frontend app built by Ananda Rizki. More of them at Labs.
Authentication, from the front end
Your app never sees the password. The question is who else can see the token.
This page assumes nothing: no cryptography, no backend experience, and none of the vocabulary the rest of the tutorial uses. It starts at why a web request needs to carry anything at all, because every strange thing about OAuth — the redirect to somebody else's website, the code that turns into a token, the second request nobody can see — is a consequence of that one fact, and none of it looks motivated until you have it.
WHY ANY OF THIS EXISTS
Every request arrives as a stranger
It cannot carry the password
So the browser carries a stand-in
And somebody has to decide whether to issue one
None of that is specific to JavaScript, or to single-page apps, or to any framework. It is true of a mobile app and a command-line tool in the same words.
WHAT A FRONTEND DEVELOPER ACTUALLY CONTROLS
You do not check the password
You hold a credential, and choose where
You choose what attaches it
You cannot hide anything from your own page
A short list, and mostly good news: the dangerous part of authentication is somebody else's job. What is left to you is a small number of decisions about where a string lives and who attaches it — which is exactly what the rest of this page is about.
THE FOUR PARTIES, AND WHAT THE SPECIFICATION CALLS THEM
Every diagram in this tutorial has the same cast, and the names are worth learning because the specifications use them and nothing else does. RFC 6749 §1.1 defines all four.
The person
resource owner
Whoever owns the data and is being asked to approve access to it. The specification's word is “resource owner”, and where that person is a human being it calls them the end-user.
Your app
client
The thing asking on their behalf — which OAuth calls the client even though it is your application and even when it runs on a server. The word means “asking on someone's behalf”, not “running in a browser”.
The authorization server
authorization server
The one that knows the password and issues the tokens. Google, Okta, Auth0, Keycloak, your own identity service. Never your frontend, and it is the only party that can say yes.
Your API
resource server
Where the data actually lives. It receives a token, checks it, and serves the request — and in the general case it has never heard of the person and does not need to.
AND ONE SENTENCE TO CARRY THROUGH ALL OF IT
The tokens in this tutorial are bearer tokens, and RFC 6750 §1.2 defines that word precisely: a security token with the property that any party in possession of the token can use the token in any way that any other party in possession of it can. There is no second factor inside the string and nothing in it identifies the holder.
Everything downstream is a consequence. The flow has two legs so the valuable one never travels in a URL. Tokens expire in an hour so that a leaked one stops mattering quickly. PKCE exists so that possession of a code is not enough. And the argument about localStorage versus cookies is an argument about who else can come into possession of the string — which, once you have read that definition, is obviously the only question worth arguing about.
Next: the same ground stated compactly, as something to look a fact up in.
Simulator
Auth Flow
A first-principles tutorial on authentication as a frontend developer meets it. It opens by explaining why a web request has to carry anything at all — HTTP keeps no memory, a password cannot be sent on every request, so the browser carries a stand-in — and only then introduces the four parties OAuth 2.0 defines. Then ten traced conversations, each drawn as a sequence diagram with the actual request beside every step: what a token replaced and why the server stopped remembering you; the authorization code flow one message at a time, with every parameter named and glossed; what PKCE adds and the exact problem it solves, using RFC 7636's own published test vector recomputed locally; why the implicit flow was retired, and the difference between RFC 9700 saying SHOULD NOT and RFC 10017 saying MUST NOT; a real JWT taken apart to show that the payload is base64url rather than ciphertext, that editing a claim breaks the signature, and that signed is not secret; and how an ID token differs from an access token, a distinction most tutorials blur. The second half is the decision: the same cross-site scripting and the same cross-site request forgery run against localStorage and an HttpOnly cookie at the same time, side by side, with what survives each — and the honest conclusion, which is that HttpOnly converts token theft into session riding rather than granting immunity, and that neither choice touches the attack the current best current practice considers worst. Then refresh tokens and rotation, including where rotation's detection provably fails, and the backend-for-frontend pattern that RFC 10017 strongly recommends instead. It is defensive throughout: mechanisms and mitigations, never payloads. Nothing is asserted without a citation, every credential on screen is fabricated, and the cookie claims — what SameSite=Lax really stops, and the two-minute grace period the attribute-less default still grants a cross-site POST — were measured in headless Chrome rather than repeated.
Built by Ananda Rizki, a frontend developer — one of the small web apps he writes for fun at Labs.
More like this