-
Notifications
You must be signed in to change notification settings - Fork 7
Description
Summary
When integrating with Ory Hydra for OAuth authentication, several issues prevent DCR (Dynamic Client Registration) and token introspection from working correctly.
This issue covers three related fixes that build on #95 (OIDC Discovery):
Problem 1: /oauth/register requires authentication
File: src/auth/prehandler.ts
The DCR endpoint /oauth/register is not in the auth skip list, but DCR is the first step before a client has credentials. This creates a chicken-and-egg problem.
Fix: Add /oauth/register to the skip list alongside /oauth/authorize and /oauth/callback.
Problem 2: Token introspection authentication
File: src/auth/token-validator.ts
Current behavior sends no Authorization header to the introspection endpoint. Ory Hydra's admin introspection endpoint (/admin/oauth2/introspect) requires an API key.
Fix: Add introspectionAuth configuration:
tokenValidation: {
introspectionEndpoint: 'https://ory.example.com/admin/oauth2/introspect',
introspectionAuth: {
type: 'bearer',
token: 'ory-api-key'
}
// Also supports: { type: 'basic', clientId, clientSecret } or { type: 'none' }
}Problem 3: DCR infinite loop when acting as proxy
Files: src/routes/auth-routes.ts, src/types/auth-types.ts
When the authorization server's registration_endpoint (from OIDC discovery) points to the MCP server itself (a valid pattern for adding custom logic like response cleaning), fastify-mcp tries to forward to itself → infinite loop → crash.
Use case: Ory returns empty strings (client_uri: "") in DCR responses that break Claude Code's Zod validation. The MCP server needs to proxy DCR requests to clean the response.
Fix: Add dcrHooks configuration:
authorization: {
enabled: true,
dcrHooks: {
// REQUIRED: bypasses OIDC discovery to avoid loop
upstreamEndpoint: 'https://ory.example.com/oauth2/register',
onRequest: async (request, log) => {
// Transform request before forwarding
return request;
},
onResponse: async (response, request, log) => {
// Clean empty fields that break clients
if (response.client_uri === '') delete response.client_uri;
return response;
}
}
}When dcrHooks is not configured, /oauth/register returns 501 Not Implemented (prevents accidental loops).
Breaking Change
/oauth/register now returns 501 unless dcrHooks is configured. This is intentional - the previous behavior would cause infinite loops when OIDC discovery pointed back to the MCP server.
Files Changed
src/types/auth-types.ts- AddIntrospectionAuthConfig,DCRRequest,DCRResponse,DCRHookstypessrc/auth/prehandler.ts- Add/oauth/registerto skip listsrc/auth/token-validator.ts- SupportintrospectionAuthconfigsrc/routes/auth-routes.ts- Implement DCR proxy with hookssrc/index.ts- Export new types, passdcrHooksto auth routes
Related
- Builds on feat(auth): OIDC Discovery and redirect_uri support for OAuth compliance #95 (OIDC Discovery)
- Reference implementation: https://github.com/getlarge/fastify-mcp/tree/feat/dcr-hooks-and-introspection-auth
I have a working implementation tested against Ory Hydra. Happy to submit a PR.