fix: use TLS 1.2 for OAuth requests via proxy

This commit is contained in:
PrentissLiu 2026-01-27 16:06:14 +08:00
parent 2ad550abe8
commit 1b2a573012
2 changed files with 72 additions and 8 deletions

View File

@ -2,6 +2,7 @@ import { createHash, randomBytes } from "node:crypto";
import { readFileSync } from "node:fs";
import { createServer } from "node:http";
import { emptyPluginConfigSchema } from "clawdbot/plugin-sdk";
import { ProxyAgent } from "undici";
// OAuth constants - decoded from pi-ai's base64 encoded values to stay in sync
const decode = (s: string) => Buffer.from(s, "base64").toString();
@ -28,6 +29,37 @@ const CODE_ASSIST_ENDPOINTS = [
"https://daily-cloudcode-pa.sandbox.googleapis.com",
];
const PROXY_ENV_KEYS = [
"CLAWDBOT_HTTPS_PROXY",
"CLAWDBOT_HTTP_PROXY",
"HTTPS_PROXY",
"HTTP_PROXY",
"https_proxy",
"http_proxy",
];
function resolveProxyUrl(): string | undefined {
for (const key of PROXY_ENV_KEYS) {
const value = process.env[key]?.trim();
if (value) return value;
}
return undefined;
}
const proxyUrl = resolveProxyUrl();
const proxyAgent = proxyUrl
? new ProxyAgent({
uri: proxyUrl,
requestTls: { maxVersion: "TLSv1.2" },
})
: undefined;
function fetchWithProxy(input: RequestInfo | URL, init?: RequestInit) {
if (!proxyAgent) return fetch(input, init);
const nextInit = init ? { ...init, dispatcher: proxyAgent } : { dispatcher: proxyAgent };
return fetch(input, nextInit);
}
const RESPONSE_PAGE = `<!DOCTYPE html>
<html lang="en">
<head>
@ -178,7 +210,7 @@ async function exchangeCode(params: {
code: string;
verifier: string;
}): Promise<{ access: string; refresh: string; expires: number }> {
const response = await fetch(TOKEN_URL, {
const response = await fetchWithProxy(TOKEN_URL, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
@ -215,7 +247,7 @@ async function exchangeCode(params: {
async function fetchUserEmail(accessToken: string): Promise<string | undefined> {
try {
const response = await fetch("https://www.googleapis.com/oauth2/v1/userinfo?alt=json", {
const response = await fetchWithProxy("https://www.googleapis.com/oauth2/v1/userinfo?alt=json", {
headers: { Authorization: `Bearer ${accessToken}` },
});
if (!response.ok) return undefined;
@ -241,7 +273,7 @@ async function fetchProjectId(accessToken: string): Promise<string> {
for (const endpoint of CODE_ASSIST_ENDPOINTS) {
try {
const response = await fetch(`${endpoint}/v1internal:loadCodeAssist`, {
const response = await fetchWithProxy(`${endpoint}/v1internal:loadCodeAssist`, {
method: "POST",
headers,
body: JSON.stringify({

View File

@ -2,6 +2,7 @@ import { createHash, randomBytes } from "node:crypto";
import { existsSync, readFileSync, readdirSync, realpathSync } from "node:fs";
import { createServer } from "node:http";
import { delimiter, dirname, join } from "node:path";
import { ProxyAgent } from "undici";
const CLIENT_ID_KEYS = ["CLAWDBOT_GEMINI_OAUTH_CLIENT_ID", "GEMINI_CLI_OAUTH_CLIENT_ID"];
const CLIENT_SECRET_KEYS = [
@ -23,6 +24,37 @@ const TIER_FREE = "free-tier";
const TIER_LEGACY = "legacy-tier";
const TIER_STANDARD = "standard-tier";
const PROXY_ENV_KEYS = [
"CLAWDBOT_HTTPS_PROXY",
"CLAWDBOT_HTTP_PROXY",
"HTTPS_PROXY",
"HTTP_PROXY",
"https_proxy",
"http_proxy",
];
function resolveProxyUrl(): string | undefined {
for (const key of PROXY_ENV_KEYS) {
const value = process.env[key]?.trim();
if (value) return value;
}
return undefined;
}
const proxyUrl = resolveProxyUrl();
const proxyAgent = proxyUrl
? new ProxyAgent({
uri: proxyUrl,
requestTls: { maxVersion: "TLSv1.2" },
})
: undefined;
function fetchWithProxy(input: RequestInfo | URL, init?: RequestInit) {
if (!proxyAgent) return fetch(input, init);
const nextInit = init ? { ...init, dispatcher: proxyAgent } : { dispatcher: proxyAgent };
return fetch(input, nextInit);
}
export type GeminiCliOAuthCredentials = {
access: string;
refresh: string;
@ -312,7 +344,7 @@ async function exchangeCodeForTokens(code: string, verifier: string): Promise<Ge
body.set("client_secret", clientSecret);
}
const response = await fetch(TOKEN_URL, {
const response = await fetchWithProxy(TOKEN_URL, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body,
@ -348,7 +380,7 @@ async function exchangeCodeForTokens(code: string, verifier: string): Promise<Ge
async function getUserEmail(accessToken: string): Promise<string | undefined> {
try {
const response = await fetch(USERINFO_URL, {
const response = await fetchWithProxy(USERINFO_URL, {
headers: { Authorization: `Bearer ${accessToken}` },
});
if (response.ok) {
@ -387,7 +419,7 @@ async function discoverProject(accessToken: string): Promise<string> {
} = {};
try {
const response = await fetch(`${CODE_ASSIST_ENDPOINT}/v1internal:loadCodeAssist`, {
const response = await fetchWithProxy(`${CODE_ASSIST_ENDPOINT}/v1internal:loadCodeAssist`, {
method: "POST",
headers,
body: JSON.stringify(loadBody),
@ -441,7 +473,7 @@ async function discoverProject(accessToken: string): Promise<string> {
(onboardBody.metadata as Record<string, unknown>).duetProject = envProject;
}
const onboardResponse = await fetch(`${CODE_ASSIST_ENDPOINT}/v1internal:onboardUser`, {
const onboardResponse = await fetchWithProxy(`${CODE_ASSIST_ENDPOINT}/v1internal:onboardUser`, {
method: "POST",
headers,
body: JSON.stringify(onboardBody),
@ -495,7 +527,7 @@ async function pollOperation(
): Promise<{ done?: boolean; response?: { cloudaicompanionProject?: { id?: string } } }> {
for (let attempt = 0; attempt < 24; attempt += 1) {
await new Promise((resolve) => setTimeout(resolve, 5000));
const response = await fetch(`${CODE_ASSIST_ENDPOINT}/v1internal/${operationName}`, {
const response = await fetchWithProxy(`${CODE_ASSIST_ENDPOINT}/v1internal/${operationName}`, {
headers,
});
if (!response.ok) continue;