import { spawn } from 'node:child_process';
import { timingSafeEqual } from 'node:crypto';
import { createServer } from 'node:http';
// rfc 6749 / oidc error codes; anything else is reported as a generic denial
const OAUTH_ERRORS = new Set([
'access_denied',
'invalid_request',
'unauthorized_client',
'unsupported_response_type',
'invalid_scope',
'server_error',
'temporarily_unavailable',
'interaction_required',
'login_required',
'consent_required',
]);
const HEADERS = {
'content-type': 'text/html; charset=utf-8',
'cache-control': 'no-store',
'referrer-policy': 'no-referrer',
'x-content-type-options': 'nosniff',
connection: 'close',
};
function sameSecret(a, b) {
const x = Buffer.from(a);
const y = Buffer.from(b);
return x.length === y.length && timingSafeEqual(x, y);
}
const PAGE = (msg) => `<!doctype html><meta charset="utf-8"><title>colab-mcp</title><p>${msg}</p>`;
// invalid callbacks must not consume the login attempt.
export async function listenForCode({ state, signal, timeoutMs }) {
if (signal?.aborted) throw new Error('login cancelled');
let finish;
let fail;
const code = new Promise((resolve, reject) => {
finish = resolve;
fail = reject;
});
code.catch(() => {});
const server = createServer((req, res) => {
const reply = (status, msg) => {
res.writeHead(status, HEADERS);
res.end(PAGE(msg));
};
const { port } = server.address();
const origin = `http://127.0.0.1:${port}`;
let url;
try {
// origin-form only: an absolute-form target must not smuggle another origin
url = req.url.startsWith('/') ? new URL(req.url, origin) : null;
} catch {}
if (
req.method !== 'GET' ||
req.headers.host !== `127.0.0.1:${port}` ||
url?.origin !== origin
) {
return reply(404, 'not found');
}
if (url.pathname !== '/') return reply(404, 'not found');
const only = (name) => {
const all = url.searchParams.getAll(name);
return all.length === 1 ? all[0] : null;
};
const got = only('state');
if (got === null || !sameSecret(got, state)) return reply(400, 'bad request');
const error = url.searchParams.get('error');
if (error) {
reply(200, 'authorization was not granted; return to the terminal.');
return fail(
new Error(
`google authorization failed (${OAUTH_ERRORS.has(error) ? error : 'access_denied'})`,
),
);
}
const value = only('code');
if (!value) return reply(400, 'bad request');
reply(200, 'authorization received; return to the terminal to finish.');
finish(value);
});
server.headersTimeout = 5000;
server.requestTimeout = 5000;
server.keepAliveTimeout = 1000;
server.timeout = 5000;
server.maxHeadersCount = 50;
server.maxRequestsPerSocket = 1;
server.maxConnections = 16;
await new Promise((resolve, reject) => {
server.once('error', reject);
server.listen(0, '127.0.0.1', resolve);
});
if (signal?.aborted) {
server.close();
server.closeAllConnections();
throw new Error('login cancelled');
}
const timer = setTimeout(() => fail(new Error('login timed out')), timeoutMs);
const onAbort = () => fail(new Error('login cancelled'));
signal?.addEventListener('abort', onAbort, { once: true });
return {
redirectUri: `http://127.0.0.1:${server.address().port}/`,
code,
close() {
clearTimeout(timer);
signal?.removeEventListener('abort', onAbort);
server.close();
server.closeAllConnections();
},
};
}
// no shell: the url is a single argv entry
export function openBrowser(url, platform = process.platform) {
const [cmd, ...args] =
platform === 'darwin'
? ['open', url]
: platform === 'win32'
? ['rundll32', 'url.dll,FileProtocolHandler', url]
: ['xdg-open', url];
return new Promise((resolve, reject) => {
const child = spawn(cmd, args, { stdio: 'ignore', detached: true, windowsHide: true });
child.once('error', reject);
child.once('spawn', () => {
child.unref();
resolve();
});
});
}