diff --git a/src/lib/oauth/providers/claude.ts b/src/lib/oauth/providers/claude.ts index 9be6e75938..6f3f3eb560 100644 --- a/src/lib/oauth/providers/claude.ts +++ b/src/lib/oauth/providers/claude.ts @@ -3,12 +3,12 @@ import { CLAUDE_CONFIG } from "../constants/oauth"; export const claude = { config: CLAUDE_CONFIG, flowType: "authorization_code_pkce", - buildAuthUrl: (config, redirectUri, state, codeChallenge) => { + buildAuthUrl: (config, _redirectUri, state, codeChallenge) => { const params = new URLSearchParams({ code: "true", client_id: config.clientId, response_type: "code", - redirect_uri: redirectUri, + redirect_uri: config.redirectUri, scope: config.scopes.join(" "), code_challenge: codeChallenge, code_challenge_method: config.codeChallengeMethod, @@ -16,7 +16,7 @@ export const claude = { }); return `${config.authorizeUrl}?${params.toString()}`; }, - exchangeToken: async (config, code, redirectUri, codeVerifier, state) => { + exchangeToken: async (config, code, _redirectUri, codeVerifier, state) => { let authCode = code; let codeState = ""; if (authCode.includes("#")) { @@ -36,7 +36,7 @@ export const claude = { state: codeState || state, grant_type: "authorization_code", client_id: config.clientId, - redirect_uri: redirectUri, + redirect_uri: config.redirectUri, code_verifier: codeVerifier, }), }); diff --git a/tests/unit/claude-oauth-provider.test.mjs b/tests/unit/claude-oauth-provider.test.mjs index 479a14429c..6189f60664 100644 --- a/tests/unit/claude-oauth-provider.test.mjs +++ b/tests/unit/claude-oauth-provider.test.mjs @@ -10,17 +10,22 @@ test.afterEach(() => { globalThis.fetch = originalFetch; }); -test("Claude OAuth provider uses the runtime redirectUri when building the auth URL", () => { - const redirectUri = "http://localhost:43121/callback"; - const authUrl = claude.buildAuthUrl(CLAUDE_CONFIG, redirectUri, "state-123", "challenge-456"); +test("Claude OAuth provider always uses the configured redirectUri when building the auth URL", () => { + const runtimeRedirectUri = "http://localhost:43121/callback"; + const authUrl = claude.buildAuthUrl( + CLAUDE_CONFIG, + runtimeRedirectUri, + "state-123", + "challenge-456" + ); const parsed = new URL(authUrl); - assert.equal(parsed.searchParams.get("redirect_uri"), redirectUri); + assert.equal(parsed.searchParams.get("redirect_uri"), CLAUDE_CONFIG.redirectUri); assert.equal(parsed.searchParams.get("state"), "state-123"); assert.equal(parsed.searchParams.get("code_challenge"), "challenge-456"); }); -test("Claude OAuth provider uses the runtime redirectUri during token exchange", async () => { +test("Claude OAuth provider always uses the configured redirectUri during token exchange", async () => { let captured = null; globalThis.fetch = async (url, init = {}) => { @@ -37,18 +42,18 @@ test("Claude OAuth provider uses the runtime redirectUri during token exchange", }); }; - const redirectUri = "http://localhost:43121/callback"; + const runtimeRedirectUri = "http://localhost:43121/callback"; await claude.exchangeToken( CLAUDE_CONFIG, "auth-code#state-from-fragment", - redirectUri, + runtimeRedirectUri, "verifier-123", "state-from-request" ); assert.equal(captured.url, CLAUDE_CONFIG.tokenUrl); assert.equal(captured.method, "POST"); - assert.equal(captured.body.redirect_uri, redirectUri); + assert.equal(captured.body.redirect_uri, CLAUDE_CONFIG.redirectUri); assert.equal(captured.body.code, "auth-code"); assert.equal(captured.body.state, "state-from-fragment"); assert.equal(captured.body.code_verifier, "verifier-123");