All writing

Hand-Rolling PKCE OAuth for an MCP Server

The Model Context Protocol (MCP) ecosystem is young enough that the tooling for HTTP-based auth is thinner than you'd expect. When we built the backend HTTP MCP server exposing roughly 80 tools across Rectify's product surface, the client landscape (Claude Desktop, various IDE integrations, custom agents) all expected standard OAuth 2.0 with PKCE — but the available server-side libraries either assumed a traditional web app redirect flow or dragged in dependencies we didn't want in a lean Node.js service.

Why not just use a library

Most OAuth libraries are built around browser redirect flows with session cookies. MCP clients aren't browsers — they're CLI tools and IDE extensions that need to complete an authorization code exchange without a persistent session. Fitting that shape into a library designed for Express + cookies meant fighting the abstraction more than it saved us. Implementing RFC 6749 (the core authorization code grant) and RFC 7636 (PKCE) directly gave us exact control over token lifetime, refresh behavior, and how credentials map to our existing user/project model.

RFC 9728 made discovery trivial

The part that paid off unexpectedly was RFC 9728 (OAuth Protected Resource Metadata). Publishing a well-known metadata endpoint meant MCP clients could discover our authorization server, supported scopes, and token endpoint automatically — no manual configuration step for the people connecting to it. Given how many different clients need to talk to the server, that discovery step removed an entire category of "how do I configure this" support questions.

The trade-off

Hand-rolling auth is usually the wrong call — it's exactly the kind of security-critical code you want a well-audited library for. It made sense here only because the surface area was narrow (one grant type, one PKCE flow, no third-party identity federation) and because we could lean on our existing session and permissions infrastructure for everything downstream of the token exchange. If the scope had included social login or SAML, this would have been a different decision.