Skip to content

Commit 4b5fdcb

Browse files
christsoclaudepcarletonfelixweinberger
authored
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>
1 parent b2326af commit 4b5fdcb

File tree

3 files changed

+63
-1
lines changed

3 files changed

+63
-1
lines changed

.changeset/oauth-error-http200.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@modelcontextprotocol/client': patch
3+
---
4+
5+
Fix OAuth error handling for servers returning errors with HTTP 200 status
6+
7+
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.

packages/client/src/client/auth.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1088,7 +1088,18 @@ async function executeTokenRequest(
10881088
throw await parseErrorResponse(response);
10891089
}
10901090

1091-
return OAuthTokensSchema.parse(await response.json());
1091+
const json: unknown = await response.json();
1092+
1093+
try {
1094+
return OAuthTokensSchema.parse(json);
1095+
} catch (parseError) {
1096+
// Some OAuth servers (e.g., GitHub) return error responses with HTTP 200 status.
1097+
// Check for error field only if token parsing failed.
1098+
if (typeof json === 'object' && json !== null && 'error' in json) {
1099+
throw await parseErrorResponse(JSON.stringify(json));
1100+
}
1101+
throw parseError;
1102+
}
10921103
}
10931104

10941105
/**
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/**
2+
* Regression test for https://github.com/modelcontextprotocol/typescript-sdk/issues/1342
3+
*
4+
* Some OAuth servers (e.g., GitHub) return error responses with HTTP 200 status
5+
* instead of 4xx. Previously, the SDK would try to parse these as tokens and fail
6+
* with a confusing Zod validation error. This test verifies that the SDK properly
7+
* detects the error field and surfaces the actual OAuth error message.
8+
*/
9+
10+
import { exchangeAuthorization } from '@modelcontextprotocol/client';
11+
import { describe, expect, it, vi } from 'vitest';
12+
13+
const mockFetch = vi.fn();
14+
vi.stubGlobal('fetch', mockFetch);
15+
16+
describe('Issue #1342: OAuth error response with HTTP 200 status', () => {
17+
const validClientInfo = {
18+
client_id: 'test-client',
19+
client_secret: 'test-secret',
20+
redirect_uris: ['http://localhost:3000/callback'],
21+
token_endpoint_auth_method: 'client_secret_post' as const
22+
};
23+
24+
it('should throw OAuth error when server returns error with HTTP 200', async () => {
25+
// GitHub returns errors with HTTP 200 instead of 4xx
26+
mockFetch.mockResolvedValueOnce({
27+
ok: true,
28+
status: 200,
29+
json: async () => ({
30+
error: 'invalid_client',
31+
error_description: 'The client_id and/or client_secret passed are incorrect.'
32+
})
33+
});
34+
35+
await expect(
36+
exchangeAuthorization('https://auth.example.com', {
37+
clientInformation: validClientInfo,
38+
authorizationCode: 'code123',
39+
codeVerifier: 'verifier123',
40+
redirectUri: 'http://localhost:3000/callback'
41+
})
42+
).rejects.toThrow('The client_id and/or client_secret passed are incorrect.');
43+
});
44+
});

0 commit comments

Comments
 (0)