Skip to content

Commit 17ace55

Browse files
committed
fix: enhance OAuth token handling in useConnection hook to prevent infinite auth loops
1 parent 861812e commit 17ace55

File tree

2 files changed

+45
-2
lines changed

2 files changed

+45
-2
lines changed

client/src/lib/hooks/__tests__/useConnection.test.tsx

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -913,6 +913,35 @@ describe("useConnection", () => {
913913
expect(headers).toHaveProperty("Authorization", "Bearer mock-token");
914914
});
915915

916+
test("replaces empty Bearer token placeholder with OAuth token", async () => {
917+
// This test prevents regression of the bug where default "Bearer " header
918+
// prevented OAuth token injection, causing infinite auth loops
919+
const customHeaders: CustomHeaders = [
920+
{
921+
name: "Authorization",
922+
value: "Bearer ", // Empty Bearer token placeholder
923+
enabled: true,
924+
},
925+
];
926+
927+
const propsWithEmptyBearer = {
928+
...defaultProps,
929+
customHeaders,
930+
};
931+
932+
const { result } = renderHook(() => useConnection(propsWithEmptyBearer));
933+
934+
await act(async () => {
935+
await result.current.connect();
936+
});
937+
938+
const headers = mockSSETransport.options?.requestInit?.headers;
939+
// Should replace the empty "Bearer " with actual OAuth token
940+
expect(headers).toHaveProperty("Authorization", "Bearer mock-token");
941+
// Should not have the x-custom-auth-headers since Authorization is standard
942+
expect(headers).not.toHaveProperty("x-custom-auth-headers");
943+
});
944+
916945
test("prioritizes custom headers over legacy auth", async () => {
917946
const customHeaders: CustomHeaders = [
918947
{ name: "Authorization", value: "Bearer custom-token", enabled: true },

client/src/lib/hooks/useConnection.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -400,11 +400,25 @@ export function useConnection({
400400
// Use custom headers (migration is handled in App.tsx)
401401
let finalHeaders: CustomHeaders = customHeaders || [];
402402

403-
// Add OAuth token if available and no custom headers are set
404-
if (finalHeaders.length === 0) {
403+
// Check if we need to inject OAuth token
404+
// This handles both empty headers and default "Bearer " placeholder headers
405+
const isEmptyAuthHeader = (header: CustomHeaders[number]) =>
406+
header.name.trim().toLowerCase() === "authorization" &&
407+
header.value.trim() === "Bearer";
408+
409+
const needsOAuthToken =
410+
finalHeaders.length === 0 ||
411+
finalHeaders.some(
412+
(header) => header.enabled && isEmptyAuthHeader(header),
413+
);
414+
415+
if (needsOAuthToken) {
405416
const oauthToken = (await serverAuthProvider.tokens())?.access_token;
406417
if (oauthToken) {
418+
// Add the OAuth token
407419
finalHeaders = [
420+
// Remove any existing Authorization headers with empty tokens
421+
...finalHeaders.filter((header) => !isEmptyAuthHeader(header)),
408422
{
409423
name: "Authorization",
410424
value: `Bearer ${oauthToken}`,

0 commit comments

Comments
 (0)