An example-driven field guide

OAuth from browser to server

Learn one common web architecture by growing Maya’s tiny sign-in into a secure CRUD application—one redirect, credential, and trust decision at a time.

The durable idea: the identity provider proves who signed in, your server keeps OAuth and session secrets, and your API decides what that person may do.

One evolving example 13 short chapters OIDC code flow + PKCE Server-side session

Chapter 1 · Mental model

Split trust into three jobs

“Login” looks like one action in the UI. Underneath, three systems make three different promises. Keeping those promises separate is the key to understanding the architecture.

ID

Identity provider

Proves identity. It authenticates Maya and issues a signed statement about the result.

“The subject maya-314 signed in at this issuer.”
S

Application server

Creates a local session. It validates the provider result, maps Maya to a local user, and remembers her browser.

Opaque cookie → server session → user-7
✓

REST API

Authorizes each action. It checks whether user-7 may read or change a specific application record.

user-7 may update item-42 because she owns it.
★

Do not collapse the jobs: a valid provider login does not automatically grant access to every record in your application.

Chapter 2 · Three different jobs

OAuth, OIDC, and the app session

These terms often appear together, but they are not synonyms. Our example uses all three in sequence.

O

OAuth 2.0

A framework for delegated access. It defines how a client obtains limited authority to call a protected resource.

Question: may this client call that API with these scopes?
ID

OpenID Connect

An identity layer on OAuth. It adds an ID Token and rules that let the client learn who authenticated.

Question: who signed in, at which issuer, for which client?
S

Application session

Your application’s own continuity mechanism. It is created after OIDC succeeds and is governed by your expiry and revocation rules.

Question: which local user is making this request now?
!

Common mix-up: an ID Token is evidence for the OIDC client. It is not automatically the credential your browser should send to every application API.

Chapter 3 · Meet the system

Four actors, three boundaries

Maya uses a browser to sign into Tasks. Tasks trusts an external provider, but owns its own users, permissions, sessions, and data.

User device

Browser UI

Follows redirects, sends the application cookie, and renders responses. Treat its runtime as observable and influenceable.

Your application

Tasks server + API

A confidential OAuth client. It runs OIDC, owns sessions, authenticates requests, and enforces business policy.

Your application

Tasks database

Stores local users, hashed session identifiers, permissions, and Maya’s task records.

External trust

Identity provider

Authenticates Maya, obtains consent when needed, and issues a one-time code plus signed identity claims.

Ownership and trust boundaries

★

Our recurring example: Maya signs into Tasks, then creates item-42: “Read RFC 9700.” Every later chapter adds detail to this same journey.

Chapter 4 · Grow the sign-in

From one click to a bound session

Use the numbered buttons to grow the flow. Each stage adds one necessary responsibility without changing the story.

Progressive example · Maya chooses “Sign in”

Browsercarries redirects
1 · startAsk Tasks to sign inGET /auth/login
4 · callbackCarry code + state/callback?code=…
Tasks serverconfidential client
2 · bindCreate state, nonce, PKCE302 /authorize
5 · redeemValidate + exchange codePOST /token
6 · rememberCreate app sessionSet-Cookie: __Host-session
Databaseapplication state
6 · persistStore session → user-7hash(cookie) + expiry
Providerexternal authority
3 · authenticateSign in Mayalogin + consent
4 · issueReturn one-time code303 callback?code=…
5 · tokensVerify redemptionID Token + access token
1

The browser starts at your server

A top-level navigation begins the ceremony. Maya’s provider password never passes through Tasks.

★

Notice the two channels: redirects travel through the browser; code redemption is a direct server-to-provider request. Provider tokens do not need to enter browser JavaScript.

Chapter 5 · Follow credentials

Similar shapes, different powers

The flow is easier to reason about when every artifact has one purpose, owner, audience, and lifetime.

Authorization code
code=Jfb2… · one use · very short lived
browser-visible
PKCE verifier
verifier=4sK… · proves the same transaction redeems the code
server only
ID Token
iss + sub + aud + nonce + exp · signed identity assertion
server consumes
Provider access token
audience + scopes · calls a provider or separate resource API
retain only if needed
Application cookie
__Host-session=opaque-random · Secure · HttpOnly · SameSite
browser stores
!

Power does not come from the format: a JWT is not inherently an ID Token, access token, or session. Meaning comes from its issuer, audience, validation rules, and the protocol context.

Chapter 6 · Make a CRUD request

OAuth leaves the hot path

After sign-in, an ordinary same-origin request usually talks only to your application. The provider does not approve each new task.

1

Browser sends

The cookie is attached automatically; JavaScript also supplies CSRF evidence required by the server.

POST /api/items
2

Server authenticates

Hash the opaque cookie, find an unexpired session, and resolve it to local user-7.

session → user-7
3

API authorizes

Check CSRF, account status, tenant, operation, and record-level policy before writing.

mayCreate(user-7)
4

Database commits

Create item-42 with its owner, then return only the representation Maya may see.

201 { id: "item-42" }

Request

POST /api/items
Cookie: __Host-session=…
X-CSRF-Token: …

{ "title": "Read RFC 9700" }

Response

HTTP/1.1 201 Created
Content-Type: application/json

{ "id": "item-42", "title": "Read RFC 9700" }
★

The application session is now the bridge: it translates a browser request into a local user. OAuth does not replace your API’s authentication and authorization middleware.

Chapter 7 · Authorize the action

Identity is input, not a verdict

The provider says “this is Maya.” Tasks must still decide what Maya can do with each application object.

Validated identity

issuer + subject

https://id.example
maya-314

Mapped locally to user-7
CREATEA task in Maya’s tenantALLOW
UPDATEitem-42, owned by user-7ALLOW
UPDATEitem-99, owned by user-12DENY
DELETEwhile Maya is suspendedDENY
!

Broken object-level authorization: filtering the UI is not enforcement. The API must derive the actor from the authenticated session and constrain every query or command.

Chapter 8 · Secret ownership

Who can see each artifact?

Use this matrix as the architecture’s invariant. “Briefly transports” is different from “is trusted to use.”

Artifact Browser Tasks server Database Provider Purpose
Provider password No No No Yes Provider authenticates Maya
State + code Briefly transports Creates / validates May store transaction Binds / issues Bind and return one login attempt
PKCE verifier No in this pattern Yes May store protected Receives on redemption Bind code redemption
ID Token Not required Validates Usually no Issues Identity assertion for client
Provider access token Not required Only if needed Protected if retained Issues Call its intended resource API
App session cookie Stores, HttpOnly Validates Stores hash / state No Authenticate Tasks requests
CRUD data Authorized view Enforces access Stores No Application business state
★

Browser-visible is not browser-owned: the browser must carry the authorization code and cookie, but application JavaScript need not be able to read either one.

Chapter 9 · Security controls

One control, one specific job

Security names become memorable when tied to the confusion or replay they prevent. No single control “secures OAuth.”

state

Bind the callback

Correlates the returned authorization response to the browser’s initiating transaction.

Not a replacement for PKCE or nonce.

PKCE

Bind code redemption

The authorization request sends a challenge; only the party holding the verifier can redeem the code.

Use S256; make the verifier high entropy.

nonce

Bind the ID Token

Connects the validated ID Token to the authentication request and helps detect replay or mix-up.

Validate it, do not merely send it.

redirect_uri

Fix the return path

Register and compare exact callback URIs so a code is not delivered to an attacker-controlled endpoint.

Do not use broad wildcard callbacks.

cookie flags

Reduce session exposure

Secure limits HTTPS; HttpOnly blocks JavaScript reads; SameSite constrains cross-site sending.

HttpOnly does not stop malicious JS from issuing requests.

CSRF + XSS

Protect browser requests

Require CSRF evidence for state changes and use a strong content-security and output-encoding posture.

SameSite is defense in depth, not the whole design.

Validation checklist for the returned ID Token

Cryptographic context

Verify the permitted signature algorithm and key resolved from the expected issuer’s metadata. Handle key rotation deliberately.

Protocol claims

Validate issuer, audience and authorized party when applicable, expiry and time claims, nonce, and any authentication context your policy requires.

Chapter 10 · Failure paths

Reject at the first trusted boundary

A secure flow is also a failure model: detect the problem where enough trustworthy context exists, then recover without widening access.

Trigger

The callback returns state that does not match the short-lived transaction stored by Tasks.

→

Detect

The Tasks callback compares state before exchanging the authorization code.

→

Respond

Reject, invalidate the transaction, avoid token exchange, log safe context, and let Maya restart.

More failure traces

Invalid ID Token

Detect: server validation. Respond: create no local session; do not trust unvalidated claims.

Expired session

Detect: session middleware. Respond: return 401, clear the cookie, and require fresh sign-in.

CSRF evidence missing

Detect: before mutation. Respond: reject the request even if its cookie maps to Maya.

Wrong record owner

Detect: object-level policy or constrained query. Respond: deny without revealing sensitive existence.

!

Fail closed: provider or network failure may block new sign-ins, but it should never turn into a session or permission bypass.

Chapter 11 · Architecture variants

Change the model deliberately

The main lesson describes a server-side backend-for-frontend pattern. Other constraints produce valid but materially different ownership models.

Primary example

OAuth runs on the confidential server

Provider tokens and the PKCE verifier stay server-side. The browser presents an opaque, Secure, HttpOnly application cookie to the same-origin API.

Browser + cookie→BFF / API↔Provider
★

Architecture test: ask “which component is the OAuth client, which API is the resource server, and which credential crosses each boundary?” If those answers change, the threat model changes too.

Chapter 12 · Design checklist

Reconstruct the architecture

A design is understandable when the team can answer these questions without relying on “the auth library handles it.”

Which component is the OAuth/OIDC client, and is it public or confidential?

Where are state, nonce, and the PKCE verifier created, stored, expired, and validated?

Which exact redirect URIs, issuers, audiences, algorithms, and scopes are accepted?

How does validated (issuer, subject) map to a durable local user?

What credential authenticates ordinary browser-to-API requests after sign-in?

How are sessions rotated, expired, revoked, disabled, logged out, and cleared?

Where are CSRF, XSS, CORS, cookie, and content-security defenses enforced?

Which layer enforces operation-, tenant-, and object-level authorization?

★

Final test: point to any credential and explain who creates it, who can read it, where it may be sent, what validates it, and when it stops working.

Chapter 13 · Continue learning

Standards and guidance

This page teaches one common architecture and intentionally omits provider-specific setup and cryptographic implementation detail. Use the standards for normative requirements.

!

Scope note: production choices depend on your provider, browser/API origins, downstream APIs, session model, platform threats, and current security guidance. This is a reasoning aid, not a complete threat model.