Command Palette

Search for a command to run...

UnylyUnyly
All posts
·engineering·Fasad Salatov

Real OAuth for MCP: how "Authorize" replaces pasting tokens

claude.ai and ChatGPT expect an OAuth flow, not a token in the URL. Here is the small OAuth 2.1 server we built for the gateway — discovery, dynamic client registration, PKCE.

If you paste a gateway URL into ChatGPT and leave auth on OAuth, it fails: "MCP server does not implement OAuth". The client is trying to discover an OAuth config that isn't there. So we built one.

The moving parts

The MCP authorization spec is standard OAuth 2.1:

  • Discovery/.well-known/oauth-protected-resource points the client at the authorization server; /.well-known/oauth-authorization-server lists the endpoints. A 401 from the gateway carries a WWW-Authenticate header so clients find it automatically.
  • Dynamic Client Registration (RFC 7591) — claude.ai / ChatGPT register themselves and get a client_id. No manual setup.
  • Authorize + consent — the user signs into Unyly and approves.
  • Token exchange with PKCE — the code is swapped for a bearer token.

The trick that kept it simple

The issued access token is a per-user gateway token. So the gateway's existing Authorization: Bearer check authenticates it unchanged — no second auth path to maintain.

One caveat

Session cookies are host-only, so the authorization server lives on unyly.org while the resource is gateway.unyly.org/mcp. Standard split — resource server ≠ authorization server — and it sidesteps cross-subdomain cookie pain.

Now you just pick OAuth and click Authorize. Try it.

Read next