Technology · Protocol

OAuth 2.0

I built an OAuth 2.0 authorization server for commercial AI assistants, with consent as the real control, and my memory gateway validates tokens from several issuers against published keys.

Auth and access planeFAFOHive and FAFO CoreServer, resource and client sides
Three assistants register
ABCpending
a person approves
ABCtokens issued
One published key setPython issuer · Rust gateway · edge verifier
  • 30 daysa newly registered client waits for a person's approval before it lapses

Auth and access plane exists because I wanted commercial AI assistants to reach my private tool estate through standard OAuth, with a person approving every grant. I built the authorization server. It implements the authorization-code grant with PKCE, with the S256 check written from primitives, plus client credentials, refresh tokens and revocation, with separate authentication paths for confidential and public clients. Tokens are RS256-signed JWTs. The signing key persists, so tokens survive restarts, and the public keys are published for every verifier.

Assistants find the server through authorization-server and protected-resource metadata, register through dynamic client registration and name the resource they want, with a default when they do not. I set the pending-client lifetime from how real assistants behave: some cache their client ID and never register again. When a hardening pass gated registration itself, every assistant with an expired cached client was locked out. I reverted it, because consent already stops an attacker-registered client from getting a code. See The hardening fix that locked everyone out.

1Registers, temporary
2Pending
✓A person approves, sets lifetimes
4First code issued

The consent screen is the control point, so open registration works with every assistant and no token is issued without a person's approval.

A protected resource that fails closed

FAFOHive is a protected resource with its own OAuth service, and I wanted an outage there to deny access. It publishes discovery metadata and accepts dynamic registration for MCP clients. Every bearer token is introspected on every request.

Introspection answers
proceeds
Introspection cannot answer
refused; no cached allow
introspect

Shared grants that refresh atomically

The in-house X API module uses OAuth 2.0 with PKCE and a loopback redirect for user-context access, and it refreshes tokens atomically so two workers cannot spend the same refresh token. See Agents with real tools and no keys.

Worker A
Worker B
Navy is the one refresh in flight; the other worker waits, so the same refresh token is never spent twice.

Offline validation with a scope tree

FAFO Core and Function Forge validates tokens at its model-facing gateway offline, against published keys from more than one issuer. Scopes are organised as a tree, sensitive actions need a step-up with a one-time code, and tokens minted for sub-agents end with their parent, so a sub-agent identity cannot outlive the agent that created it. Together they limit what a leaked low-level token can do.

Token lifetimes
Parent agent
Sub-agent

A client in Helix

Helix is an OAuth client. Its voice engine connects to its MCP tool servers with the authorization-code flow and PKCE S256, and its Teams tab signs people in through Microsoft's library with PKCE.

the voice engine to its MCP tool servers · authorization code, PKCE S256

a person to the Teams tab · Microsoft's library, PKCE

I can implement OAuth 2.0 on the server side, the resource side and the client side, and make it work with several vendors' assistants whose behaviour differs within the standards. The standards are the IETF's; the server, the validation and the consent-centred design are mine.