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 - Add IntrospectionAuthConfig, DCRRequest, DCRResponse, DCRHooks types
src/auth/prehandler.ts - Add /oauth/register to skip list
src/auth/token-validator.ts - Support introspectionAuth config
src/routes/auth-routes.ts - Implement DCR proxy with hooks
src/index.ts - Export new types, pass dcrHooks to auth routes
Related
I have a working implementation tested against Ory Hydra. Happy to submit a PR.
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/registerrequires authenticationFile:
src/auth/prehandler.tsThe DCR endpoint
/oauth/registeris 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/registerto the skip list alongside/oauth/authorizeand/oauth/callback.Problem 2: Token introspection authentication
File:
src/auth/token-validator.tsCurrent behavior sends no Authorization header to the introspection endpoint. Ory Hydra's admin introspection endpoint (
/admin/oauth2/introspect) requires an API key.Fix: Add
introspectionAuthconfiguration:Problem 3: DCR infinite loop when acting as proxy
Files:
src/routes/auth-routes.ts,src/types/auth-types.tsWhen 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-mcptries 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
dcrHooksconfiguration:When
dcrHooksis not configured,/oauth/registerreturns 501 Not Implemented (prevents accidental loops).Breaking Change
/oauth/registernow returns 501 unlessdcrHooksis 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 routesRelated
I have a working implementation tested against Ory Hydra. Happy to submit a PR.