Chapter 7: Your First OpenID Application — The Handshake, End to End

This is Part 7 of a chapter-by-chapter walkthrough of my book OpenID: Modern Identity for Developers and Architects. In the previous chapter we looked at the discovery and metadata machinery that lets RPs and providers find each other. Chapter 7 is where we finally go hands-on: a minimal, correct web login, built the way you'd actually ship it.


7.1 — The Shortest Correct Implementation

A working OpenID Connect login is three operations stitched together: register your app with the provider, redirect to the authorization endpoint with the right parameters, and handle the callback by exchanging the code for tokens. Simple — but every one of these steps has a sharp edge, and most tutorials skip the sharp edges.

Chapter 7 walks the whole path — registration, the exact parameters you send to the authorization endpoint, what you must validate when the user comes back, and how to translate successful token exchange into an actual logged-in session. If you've ever wondered where "should I use a library for this?" really matters, the answer starts here.

7.2 — state and nonce: Tiny Parameters, Huge Consequences

Two small random strings carry disproportionate weight. state protects the login flow from CSRF at the redirect step — generate it before redirecting, store it in the user's session, and reject any callback that doesn't return the exact same value. nonce protects against ID-token replay by binding the token you receive to the specific authorization request you made.

Both must be cryptographically random. Both must be validated. Both are trivial to implement and catastrophic to forget.

Important: "I don't need state because I use POST" is wrong. "I don't need nonce because I use the Authorization Code Flow" is mostly right but not fully — nonce is still defense-in-depth, and some libraries will reject ID tokens without it. When in doubt, generate both.

7.3 — Redirect URI Matching Is Strict on Purpose

If there is one thing that blindsides new implementers, it's redirect URI matching. The comparison is exact: scheme, host, port, path, and any query parameters must match what you registered, byte for byte. No wildcards, no subdomain flexibility, no "the provider will just ignore the extra parameter."

This rigidity is the point. A wildcard redirect URI is how attackers steal authorization codes by redirecting to a URL they control. Chapter 13 has the case studies; Chapter 7 just asks you to stop trying to work around exact matching.

7.4 — Turning Tokens into a Session

Validating an ID token gets you a set of verified claims. It does not automatically log the user in. Your application has to create a session: establish a session identifier, store user state server-side, set a cookie with the right flags (Secure, HttpOnly, SameSite), and define inactivity and absolute lifetime timeouts.

Chapter 7 walks through the practical choices — where to store sessions (Redis, a DB, signed cookies), how long they should live, and how to make absolute session lifetime coexist with refresh tokens without surprising the user. The goal is a session you can actually revoke and audit.

7.5 — Logout: Three Flavors, All of Them Necessary

"Logout" is not one thing. There are three patterns you'll meet:

  • RP-initiated logout — the user clicks a button, you kill your session and redirect them to the provider's logout endpoint.
  • Front-channel logout — the provider loads hidden iframes to each RP's logout URL through the user's browser. Historically used; increasingly distrusted.
  • Back-channel logout — the provider makes server-to-server calls to each RP's logout endpoint with a signed logout token. More reliable, more secure; now recommended.

Real deployments mix these. And no matter which you pick, always delete your own local session first — don't let a failed call to the provider leave your user logged in on your side.

7.6 — Common Beginner Mistakes (And They Are Terrifyingly Common)

Tokens in URL fragments because "the server never sees them" (they do, via browser history, logs, referrers). Skipped aud or signature validation. Access tokens treated as proof of identity. Client secrets in version control. Non-HTTPS redirect URIs in development that leak into production config. Chapter 7 closes with a gallery of these, because they're the ones I personally have caught in reviews over and over.


What Chapter 7 Sets Up

By the end of Chapter 7 you should be able to implement — or, more realistically, audit — an OIDC login that doesn't embarrass you. You should be able to explain what state and nonce protect, why exact redirect matching is a feature, and why logout isn't "one button."


Next up — Chapter 8: Securing Backend APIs. With login working, we turn to the APIs sitting behind it: bearer tokens in the Authorization header, scope design, token introspection, and the different shapes of service-to-service authentication. A login that leads to an API with no real token validation is just a much slower way to get breached.

Want the full picture? Grab OpenID: Modern Identity for Developers and Architects here for the complete code-level walkthrough, the full logout matrix, and the rest of the 22-chapter journey through modern identity.
2026-03-13

Sho Shimoda

I share and organize what I’ve learned and experienced.