mirror of
https://github.com/okieselbach/AutopilotMonitor.git
synced 2026-10-24 04:32:32 +00:00
fix(mcp): the OAuth proxy advertises and returns token auth method none
The proxy issues no client secrets, but its authorization server metadata
named no token_endpoint_auth_methods_supported (RFC 8414 then implies
client_secret_basic) and /oauth/register echoed a requested secret-based
method. A client following that default registered with client_secret_basic
and then failed the token exchange ("client_secret_basic authentication
requires a client_secret"), reproduced with the SDK calls a self-hosted chat
client makes. The metadata now lists ["none"] and registration always answers
none (RFC 7591 lets the server replace a requested value).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -84,6 +84,12 @@ describe('/.well-known/oauth-authorization-server — RFC 8414 metadata', () =>
|
||||
expect(json.issuer).toBe(baseUrl);
|
||||
expect(json.authorization_response_iss_parameter_supported).toBe(true);
|
||||
});
|
||||
|
||||
it('advertises public clients only, so clients never pick a secret-based token auth method', async () => {
|
||||
const res = await fetch(`${baseUrl}/.well-known/oauth-authorization-server`);
|
||||
const json = (await res.json()) as Record<string, unknown>;
|
||||
expect(json.token_endpoint_auth_methods_supported).toEqual(['none']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('/oauth/register — dynamic client registration (RFC 7591)', () => {
|
||||
@@ -99,6 +105,19 @@ describe('/oauth/register — dynamic client registration (RFC 7591)', () => {
|
||||
expect(json.token_endpoint_auth_method).toBe('none');
|
||||
});
|
||||
|
||||
it('answers a requested secret-based auth method with none and issues no secret', async () => {
|
||||
// A self-hosted client following the RFC 8414 default registers with client_secret_basic;
|
||||
// echoing it back made the client demand a secret at the token endpoint.
|
||||
const { status, json } = await register({
|
||||
client_name: 'Self-hosted client',
|
||||
redirect_uris: ['http://localhost:3080/api/mcp/example/oauth/callback'],
|
||||
token_endpoint_auth_method: 'client_secret_basic',
|
||||
});
|
||||
expect(status).toBe(201);
|
||||
expect(json.token_endpoint_auth_method).toBe('none');
|
||||
expect(json.client_secret).toBeUndefined();
|
||||
});
|
||||
|
||||
it('registers a VS Code client (loopback + vscode.dev redirect) — GitHub Copilot DCR', async () => {
|
||||
const { status, json } = await register({
|
||||
client_name: 'Visual Studio Code',
|
||||
|
||||
@@ -581,6 +581,10 @@ export function createOAuthRouter(): Router {
|
||||
response_types_supported: ['code'],
|
||||
grant_types_supported: ['authorization_code', 'refresh_token'],
|
||||
code_challenge_methods_supported: ['S256'],
|
||||
// Every client of this proxy is public (PKCE); it issues no client secrets. Without this
|
||||
// field RFC 8414 implies client_secret_basic, and clients that follow it (LibreChat) register
|
||||
// with a secret method and then fail the token exchange for lack of a secret.
|
||||
token_endpoint_auth_methods_supported: ['none'],
|
||||
// MCP spec 2026-07-28 client registration: clients that support Client ID
|
||||
// Metadata Documents pick them over dynamic registration when this flag
|
||||
// is present (cimd.ts resolves the document at /oauth/authorize).
|
||||
@@ -602,7 +606,7 @@ export function createOAuthRouter(): Router {
|
||||
// registration itself, an org-wide auth DoS. The .well-known discovery docs
|
||||
// stay unthrottled (static JSON, no outbound work, no logging).
|
||||
router.post('/oauth/register', oauthRateLimit, (req, res) => {
|
||||
const { client_name, redirect_uris, grant_types, response_types, token_endpoint_auth_method } = req.body ?? {};
|
||||
const { client_name, redirect_uris, grant_types, response_types } = req.body ?? {};
|
||||
|
||||
// Field-level bounds — backstop the route-level body-size limit. The
|
||||
// parser caps total body bytes; these caps stop a single registration
|
||||
@@ -671,7 +675,10 @@ export function createOAuthRouter(): Router {
|
||||
redirect_uris: uris,
|
||||
grant_types: grant_types ?? ['authorization_code', 'refresh_token'],
|
||||
response_types: response_types ?? ['code'],
|
||||
token_endpoint_auth_method: token_endpoint_auth_method ?? 'none',
|
||||
// Always 'none', whatever was requested: no secret is ever issued (RFC 7591 §3.2.1 lets the
|
||||
// server replace a requested value). Echoing a secret-based method made SDK clients demand a
|
||||
// client_secret at the token endpoint that does not exist.
|
||||
token_endpoint_auth_method: 'none',
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user