Connecting to the HipTrip MCP server via OAuth
HipTrip's MCP server (/api/mcp) supports OAuth 2.1 with Dynamic Client
Registration (RFC 7591), so a compatible harness — Claude Code, Claude
Desktop, or any other MCP client that implements the MCP Authorization
spec —
can connect without you ever copying or storing a key.
This is additive: the static x-api-key / per-user API key flow documented
in mcp-user-access.md still works unchanged and
remains the only option for headless/non-interactive harnesses that can't
complete a browser redirect.
What happens when you connect
- The harness calls
POST /api/mcpwith no credential and gets401with aWWW-Authenticate: Bearer resource_metadata="..."header. - It fetches
/.well-known/oauth-protected-resource/api/mcp, which points at this server as the authorization server. - It fetches
/.well-known/oauth-authorization-serverfor the actual endpoints, thenPOST /api/oauth/registerto self-register — no manual setup, no client secret to distribute (HipTrip issues public clients only, proven via mandatory PKCE instead of a shared secret). - It opens
/oauth/authorizein a browser. You sign in (if not already) and see a consent screen listing exactly which scopes the application is requesting, then approve or deny. - It exchanges the resulting authorization code at
POST /api/oauth/tokenfor a Bearer access token (1 hour) and refresh token (30 days). - Every subsequent
/api/mcpcall sendsAuthorization: Bearer <token>.
Scopes
OAuth-issued tokens can only ever carry the same user scopes the manual
per-user API key UI grants — trips:read, trips:write, packing:read,
packing:write — never the editorial scopes (catalog:read,
editorial:*, places:search). A client requesting more than that gets the
intersection of what it asked for, what it's registered for, and what
HipTrip's OAuth flow is willing to grant at all; it never gets an error for
asking too much.
offline_access is advertised in discovery so clients that gate their own
refresh-token request on seeing it (Claude Code) will ask for it — HipTrip
issues a refresh token on every authorization either way.
Managing connections
Profile → Connected apps lists every application you've authorized — name, redirect host, granted scopes, last-used time — with a Disconnect button that immediately revokes every live token for that connection. This is independent of the API-key list above it; revoking one has no effect on the other.
For implementers: server-side pieces
lib/oauth/— DCR validation, PKCE, redirect-URI matching, code/token minting and rotation, discovery metadata. See inline comments for the security reasoning (claim-before-validate ordering, refresh-token-family reuse detection, the loopback redirect-URI port exception, etc.).app/oauth-discovery/oauth-authorization-server,app/oauth-discovery/openid-configuration,app/oauth-discovery/oauth-protected-resource,app/oauth-discovery/oauth-protected-resource/api/mcp— discovery documents, rewritten to their required external/.well-known/*URLs vianext.config.mjs'srewrites()rather than living under a literalapp/.well-known/folder — that folder builds fine locally but its routes silently 404 once deployed to Vercel (confirmed: present innext build's own manifest and served correctly bynext start, but 404 withx-matched-path: /404— Vercel's build-output packaging drops routes under a dot-prefixed path segment). Pure functions of the deployment origin, no DB call.app/oauth/authorize,app/api/oauth/consent— login+consent UI and its CSRF-safe signed-blob handoff (lib/oauth/consent-sign.ts).app/api/oauth/register,app/api/oauth/token,app/api/oauth/revoke— the three protocol endpoints.lib/mcp-auth.ts'sauthenticateMcpKeydispatches on credential prefix (htoac_→ OAuth access token,htmcp_→ static per-user key, else → legacyHIPTRIP_MCP_KEY) before doing any DB lookup.
Apply prisma/migrations/027_oauth_dcr/migration.sql manually to Aurora
DSQL, one transaction at a time, same as every other migration in this repo.