fix(ai): align oauth callback flows closes #2316

This commit is contained in:
Mario Zechner
2026-03-17 23:13:44 +01:00
parent 3563cc4df6
commit 94ba13ccef
5 changed files with 180 additions and 98 deletions
@@ -7,6 +7,7 @@
*/
import type { Server } from "node:http";
import { oauthErrorHtml, oauthSuccessHtml } from "./oauth-page.js";
import { generatePKCE } from "./pkce.js";
import type { OAuthCredentials, OAuthLoginCallbacks, OAuthProviderInterface } from "./types.js";
@@ -67,8 +68,15 @@ async function startCallbackServer(): Promise<CallbackServerInfo> {
const createServer = await getNodeCreateServer();
return new Promise((resolve, reject) => {
let result: { code: string; state: string } | null = null;
let cancelled = false;
let settleWait: ((value: { code: string; state: string } | null) => void) | undefined;
const waitForCodePromise = new Promise<{ code: string; state: string } | null>((resolveWait) => {
let settled = false;
settleWait = (value) => {
if (settled) return;
settled = true;
resolveWait(value);
};
});
const server = createServer((req, res) => {
const url = new URL(req.url || "", `http://localhost:51121`);
@@ -79,28 +87,22 @@ async function startCallbackServer(): Promise<CallbackServerInfo> {
const error = url.searchParams.get("error");
if (error) {
res.writeHead(400, { "Content-Type": "text/html" });
res.end(
`<html><body><h1>Authentication Failed</h1><p>Error: ${error}</p><p>You can close this window.</p></body></html>`,
);
res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
res.end(oauthErrorHtml("Google authentication did not complete.", `Error: ${error}`));
return;
}
if (code && state) {
res.writeHead(200, { "Content-Type": "text/html" });
res.end(
`<html><body><h1>Authentication Successful</h1><p>You can close this window and return to the terminal.</p></body></html>`,
);
result = { code, state };
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
res.end(oauthSuccessHtml("Google authentication completed. You can close this window."));
settleWait?.({ code, state });
} else {
res.writeHead(400, { "Content-Type": "text/html" });
res.end(
`<html><body><h1>Authentication Failed</h1><p>Missing code or state parameter.</p></body></html>`,
);
res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
res.end(oauthErrorHtml("Missing code or state parameter."));
}
} else {
res.writeHead(404);
res.end();
res.writeHead(404, { "Content-Type": "text/html; charset=utf-8" });
res.end(oauthErrorHtml("Callback route not found."));
}
});
@@ -112,15 +114,9 @@ async function startCallbackServer(): Promise<CallbackServerInfo> {
resolve({
server,
cancelWait: () => {
cancelled = true;
},
waitForCode: async () => {
const sleep = () => new Promise((r) => setTimeout(r, 100));
while (!result && !cancelled) {
await sleep();
}
return result;
settleWait?.(null);
},
waitForCode: () => waitForCodePromise,
});
});
});