diff --git a/.changeset/fix-auth-fallback-non-root-path.md b/.changeset/fix-auth-fallback-non-root-path.md new file mode 100644 index 000000000..41ccc693c --- /dev/null +++ b/.changeset/fix-auth-fallback-non-root-path.md @@ -0,0 +1,7 @@ +--- +'@modelcontextprotocol/client': patch +--- + +Throw error on auth fallback for non-root AS paths instead of silently using incorrect absolute paths. Fixes URL path prefix loss when authorization server metadata discovery fails. + +Fixes modelcontextprotocol/typescript-sdk#1716 diff --git a/packages/client/src/client/auth.ts b/packages/client/src/client/auth.ts index 58ec23ddd..76d35bd7b 100644 --- a/packages/client/src/client/auth.ts +++ b/packages/client/src/client/auth.ts @@ -1200,6 +1200,11 @@ export async function startAuthorization( ) { throw new Error(`Incompatible auth server: does not support code challenge method ${AUTHORIZATION_CODE_CHALLENGE_METHOD}`); } + } else if (authorizationServerUrl.pathname !== '/') { + throw new Error( + `Authorization server metadata discovery failed and the server URL (${authorizationServerUrl}) has a non-root path. ` + + `Cannot determine the authorization endpoint. Please ensure the authorization server is reachable and supports metadata discovery.` + ); } else { authorizationUrl = new URL('/authorize', authorizationServerUrl); } @@ -1283,7 +1288,14 @@ async function executeTokenRequest( fetchFn?: FetchLike; } ): Promise { - const tokenUrl = metadata?.token_endpoint ? new URL(metadata.token_endpoint) : new URL('/token', authorizationServerUrl); + const tokenUrl = metadata?.token_endpoint + ? new URL(metadata.token_endpoint) + : authorizationServerUrl.pathname !== '/' + ? (() => { throw new Error( + `Authorization server metadata discovery failed and the server URL (${authorizationServerUrl}) has a non-root path. ` + + `Cannot determine the token endpoint.` + ); })() + : new URL('/token', authorizationServerUrl); const headers = new Headers({ 'Content-Type': 'application/x-www-form-urlencoded', @@ -1530,6 +1542,12 @@ export async function registerClient( registrationUrl = new URL(metadata.registration_endpoint); } else { + if (authorizationServerUrl.pathname !== '/') { + throw new Error( + `Authorization server metadata discovery failed and the server URL (${authorizationServerUrl}) has a non-root path. ` + + `Cannot determine the registration endpoint.` + ); + } registrationUrl = new URL('/register', authorizationServerUrl); }