fix: handle OAuth error responses returned with HTTP 200 status (#1343)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: Paul Carleton <paulcarletonjr@gmail.com> Co-authored-by: Felix Weinberger <fweinberger@anthropic.com> Co-authored-by: Felix Weinberger <3823880+felixweinberger@users.noreply.github.com>
This commit is contained in:
parent
b2326afea6
commit
4b5fdcba02
3 changed files with 63 additions and 1 deletions
7
.changeset/oauth-error-http200.md
Normal file
7
.changeset/oauth-error-http200.md
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
'@modelcontextprotocol/client': patch
|
||||
---
|
||||
|
||||
Fix OAuth error handling for servers returning errors with HTTP 200 status
|
||||
|
||||
Some OAuth servers (e.g., GitHub) return error responses with HTTP 200 status instead of 4xx. The SDK now checks for an `error` field in the JSON response before attempting to parse it as tokens, providing users with meaningful error messages.
|
||||
|
|
@ -1088,7 +1088,18 @@ async function executeTokenRequest(
|
|||
throw await parseErrorResponse(response);
|
||||
}
|
||||
|
||||
return OAuthTokensSchema.parse(await response.json());
|
||||
const json: unknown = await response.json();
|
||||
|
||||
try {
|
||||
return OAuthTokensSchema.parse(json);
|
||||
} catch (parseError) {
|
||||
// Some OAuth servers (e.g., GitHub) return error responses with HTTP 200 status.
|
||||
// Check for error field only if token parsing failed.
|
||||
if (typeof json === 'object' && json !== null && 'error' in json) {
|
||||
throw await parseErrorResponse(JSON.stringify(json));
|
||||
}
|
||||
throw parseError;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
/**
|
||||
* Regression test for https://github.com/modelcontextprotocol/typescript-sdk/issues/1342
|
||||
*
|
||||
* Some OAuth servers (e.g., GitHub) return error responses with HTTP 200 status
|
||||
* instead of 4xx. Previously, the SDK would try to parse these as tokens and fail
|
||||
* with a confusing Zod validation error. This test verifies that the SDK properly
|
||||
* detects the error field and surfaces the actual OAuth error message.
|
||||
*/
|
||||
|
||||
import { exchangeAuthorization } from '@modelcontextprotocol/client';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
const mockFetch = vi.fn();
|
||||
vi.stubGlobal('fetch', mockFetch);
|
||||
|
||||
describe('Issue #1342: OAuth error response with HTTP 200 status', () => {
|
||||
const validClientInfo = {
|
||||
client_id: 'test-client',
|
||||
client_secret: 'test-secret',
|
||||
redirect_uris: ['http://localhost:3000/callback'],
|
||||
token_endpoint_auth_method: 'client_secret_post' as const
|
||||
};
|
||||
|
||||
it('should throw OAuth error when server returns error with HTTP 200', async () => {
|
||||
// GitHub returns errors with HTTP 200 instead of 4xx
|
||||
mockFetch.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
status: 200,
|
||||
json: async () => ({
|
||||
error: 'invalid_client',
|
||||
error_description: 'The client_id and/or client_secret passed are incorrect.'
|
||||
})
|
||||
});
|
||||
|
||||
await expect(
|
||||
exchangeAuthorization('https://auth.example.com', {
|
||||
clientInformation: validClientInfo,
|
||||
authorizationCode: 'code123',
|
||||
codeVerifier: 'verifier123',
|
||||
redirectUri: 'http://localhost:3000/callback'
|
||||
})
|
||||
).rejects.toThrow('The client_id and/or client_secret passed are incorrect.');
|
||||
});
|
||||
});
|
||||
Loading…
Reference in a new issue