mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 04:11:22 +00:00
fix(chutes): accept manual OAuth code input
This commit is contained in:
@@ -21,10 +21,9 @@ describe("parseOAuthCallbackInput", () => {
|
|||||||
expect((result as { error: string }).error).toMatch(/state mismatch/i);
|
expect((result as { error: string }).error).toMatch(/state mismatch/i);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("rejects bare code input without fabricating state", () => {
|
it("accepts bare code input for manual flow", () => {
|
||||||
const result = parseOAuthCallbackInput("bare_auth_code", EXPECTED_STATE);
|
const result = parseOAuthCallbackInput("bare_auth_code", EXPECTED_STATE);
|
||||||
expect(result).toHaveProperty("error");
|
expect(result).toEqual({ code: "bare_auth_code", state: EXPECTED_STATE });
|
||||||
expect(result).not.toHaveProperty("code");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("rejects empty input", () => {
|
it("rejects empty input", () => {
|
||||||
|
|||||||
@@ -50,14 +50,19 @@ export function parseOAuthCallbackInput(
|
|||||||
return { error: "Missing 'code' parameter in URL" };
|
return { error: "Missing 'code' parameter in URL" };
|
||||||
}
|
}
|
||||||
if (!state) {
|
if (!state) {
|
||||||
return { error: "Missing 'state' parameter. Paste the full URL." };
|
return { error: "Missing 'state' parameter. Paste the full URL (or just the code)." };
|
||||||
}
|
}
|
||||||
if (state !== expectedState) {
|
if (state !== expectedState) {
|
||||||
return { error: "OAuth state mismatch - possible CSRF attack. Please retry login." };
|
return { error: "OAuth state mismatch - possible CSRF attack. Please retry login." };
|
||||||
}
|
}
|
||||||
return { code, state };
|
return { code, state };
|
||||||
} catch {
|
} catch {
|
||||||
return { error: "Paste the full redirect URL, not just the code." };
|
// Manual flow: users often paste only the authorization code.
|
||||||
|
// In that case we can't validate state, but the user is explicitly opting in by pasting it.
|
||||||
|
if (!/\s/.test(trimmed) && !trimmed.includes("://") && trimmed.length > 0) {
|
||||||
|
return { code: trimmed, state: expectedState };
|
||||||
|
}
|
||||||
|
return { error: "Paste the redirect URL (or authorization code)." };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ export async function loginChutes(params: {
|
|||||||
await params.onAuth({ url });
|
await params.onAuth({ url });
|
||||||
params.onProgress?.("Waiting for redirect URL…");
|
params.onProgress?.("Waiting for redirect URL…");
|
||||||
const input = await params.onPrompt({
|
const input = await params.onPrompt({
|
||||||
message: "Paste the redirect URL",
|
message: "Paste the redirect URL (or authorization code)",
|
||||||
placeholder: `${params.app.redirectUri}?code=...&state=...`,
|
placeholder: `${params.app.redirectUri}?code=...&state=...`,
|
||||||
});
|
});
|
||||||
const parsed = parseOAuthCallbackInput(String(input), state);
|
const parsed = parseOAuthCallbackInput(String(input), state);
|
||||||
@@ -176,7 +176,7 @@ export async function loginChutes(params: {
|
|||||||
}).catch(async () => {
|
}).catch(async () => {
|
||||||
params.onProgress?.("OAuth callback not detected; paste redirect URL…");
|
params.onProgress?.("OAuth callback not detected; paste redirect URL…");
|
||||||
const input = await params.onPrompt({
|
const input = await params.onPrompt({
|
||||||
message: "Paste the redirect URL",
|
message: "Paste the redirect URL (or authorization code)",
|
||||||
placeholder: `${params.app.redirectUri}?code=...&state=...`,
|
placeholder: `${params.app.redirectUri}?code=...&state=...`,
|
||||||
});
|
});
|
||||||
const parsed = parseOAuthCallbackInput(String(input), state);
|
const parsed = parseOAuthCallbackInput(String(input), state);
|
||||||
|
|||||||
Reference in New Issue
Block a user