The sign-in flow can now sign people into your own website
Until now the redirect flow had one ending. It took the user to Microsoft, Google, or Cloudflare Access, brought them back, and then always did the MCP thing: show a consent screen, mint a token for an MCP client, set a cookie. If all you wanted was to know who the visitor is, you had to write that yourself against your provider, with your own callback, your own state handling, and your own verification.
The same flow can now stop as soon as the identity is proven and hand it to you. You set complete: "identity" and give it an onIdentity function. It exchanges the provider's response, verifies it, and calls your function with the verified claims. It renders no consent page, mints no token, sets no cookie, and binds no user. Every decision after "this is who they are" belongs to your site.
It mounts at /login with its return endpoint at /login/callback, and it works the same through the Fastify, Express, and Hono adapters. If you run both endings in one application, the library refuses to start when their routes would collide, including the cases that only collide after a framework lowercases a path or ignores a trailing slash.
One thing to size correctly: a completed sign-in costs two rate-limit checks, one when the visitor leaves for the provider and one when they come back. With a limit of 60 per window, that is at most 30 completed sign-ins in a window, not 60.
The stateless example is no longer the unthrottled one
The 0.4.0 notes said this plainly rather than letting anyone assume otherwise: the shipped examples only created a rate limiter in stored mode, so in the default stateless mode the sign-in and its return endpoint had no limit at all.
That is fixed. The Fastify example supplies its bounded limiter in stateless mode too, so the mode you get by default is limited like the other one.
The last unbounded download is bounded
0.4.0 capped what your provider's discovery and token endpoints could send you. The key document was still uncapped: your server fetched the provider's signing keys with no size limit and held whatever came back in memory before anything checked it.
Key documents are now capped at 64KB, configurable up to 1MB. Real ones are a few kilobytes. If you run a provider whose key document is genuinely larger than 64KB, raise the limit in configuration; nothing else changes.
Pre-release verification runs itself now
Every release before this one was verified by a person sitting at a browser for an evening. That is why 0.3.5 shipped unable to sign in two of the clients people actually use: the checks that would have caught it were too expensive to run often.
One dispatch now brings up the Cloudflare Access, Microsoft Entra ID, and Google deployments behind a tunnel, drives nineteen checks against them including the real Claude Code and Codex CLI sign-in flows and five Microsoft Entra denial cases, and records what it observed at the exact commit it ran against. Nothing is typed by hand and no result is transcribed from memory.
Two things are still done by a person, and we would rather say so than imply otherwise. The ChatGPT and claude.ai connectors are added by hand, because driving those web applications with a script is not something their terms invite. The Google sign-in is likewise manual. Both were run against this exact commit before it was tagged, and every result in the compatibility record was read from the server's own audit trail rather than from what the client claimed on screen.
jose is still the only runtime dependency, and there are still no install scripts.