fix(auth): parse baseUrl/consoleSite/consoleRegion/consoleSwitchAgent from POST body

These fields were only extracted from query params but the console
sends them in the JSON POST body. Add parseExtrasFromRawBody() to
handle JSON and form-urlencoded bodies.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
lishengzxc
2026-06-14 22:56:54 +08:00
parent e68abb6975
commit 51d9833a3d
2 changed files with 83 additions and 4 deletions
@@ -210,6 +210,66 @@ function parseApiKeyFromRawBody(raw: string, contentType: string): string | null
return null;
}
type CallbackExtras = Pick<
CallbackCredentials,
"baseUrl" | "consoleSite" | "consoleRegion" | "consoleSwitchAgent"
>;
function stringField(o: Record<string, unknown>, ...keys: string[]): string | null {
for (const k of keys) {
const v = o[k];
if (typeof v === "string" && v.trim()) return v.trim();
}
return null;
}
function parseExtrasFromRawBody(raw: string, contentType: string): CallbackExtras {
const empty: CallbackExtras = {
baseUrl: null,
consoleSite: null,
consoleRegion: null,
consoleSwitchAgent: null,
};
if (!raw.trim()) return empty;
let obj: Record<string, unknown> | null = null;
const ct = contentType.toLowerCase();
if (ct.includes("application/json") || ct.includes("text/json")) {
try {
const parsed = JSON.parse(raw.trim());
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) obj = parsed;
} catch {
/* */
}
}
if (!obj && ct.includes("application/x-www-form-urlencoded")) {
try {
const params = new URLSearchParams(raw.trim());
obj = Object.fromEntries(params);
} catch {
/* */
}
}
if (!obj) {
try {
const parsed = JSON.parse(raw.trim());
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) obj = parsed;
} catch {
/* */
}
}
if (!obj) return empty;
return {
baseUrl: stringField(obj, "base_url", "baseUrl"),
consoleSite: stringField(obj, "console_site", "consoleSite"),
consoleRegion: stringField(obj, "console_region", "consoleRegion"),
consoleSwitchAgent: stringField(obj, "console_switch_agent", "consoleSwitchAgent"),
};
}
interface CallbackCredentials {
accessToken: string | null;
apiKey: string | null;
@@ -264,7 +324,17 @@ async function extractCredentialsFromRequest(
const accessToken = accessTokenFromQuery?.trim() || parseAccessTokenFromRawBody(raw, contentType);
const apiKey = apiKeyFromQuery?.trim() || parseApiKeyFromRawBody(raw, contentType);
return { accessToken, apiKey, ...extras };
const bodyExtras = parseExtrasFromRawBody(raw, contentType);
return {
accessToken,
apiKey,
baseUrl: extras.baseUrl || bodyExtras.baseUrl,
consoleSite: extras.consoleSite || bodyExtras.consoleSite,
consoleRegion: extras.consoleRegion || bodyExtras.consoleRegion,
consoleSwitchAgent: extras.consoleSwitchAgent || bodyExtras.consoleSwitchAgent,
};
}
function listenServerOnFreeLocalPort(server: http.Server): Promise<number> {
@@ -298,7 +368,10 @@ function openInBrowser(url: string): Promise<void> {
export async function runConsoleLogin(
consoleOrigin: string,
opts?: { needApiKey?: boolean; onApiKey?: (key: string) => Promise<void> },
opts?: {
needApiKey?: boolean;
onApiKey?: ({ apiKey, baseUrl }: { apiKey: string; baseUrl?: string }) => Promise<void>;
},
): Promise<void> {
const state = randomBytes(16).toString("hex");
let callbackError: unknown;
@@ -323,6 +396,8 @@ export async function runConsoleLogin(
const { accessToken, apiKey, baseUrl, consoleSite, consoleRegion, consoleSwitchAgent } =
await extractCredentialsFromRequest(req);
console.log({ accessToken, apiKey, baseUrl, consoleSite, consoleRegion, consoleSwitchAgent });
const hasConfig =
accessToken || baseUrl || consoleSite || consoleRegion || consoleSwitchAgent;
@@ -339,7 +414,7 @@ export async function runConsoleLogin(
process.stderr.write(`Config saved to ${getConfigPath()}\n`);
}
if (apiKey && opts?.onApiKey) {
await opts.onApiKey(apiKey);
await opts.onApiKey({ apiKey, baseUrl: baseUrl ?? undefined });
}
} catch (err: unknown) {
callbackError = err;
+5 -1
View File
@@ -41,6 +41,8 @@ function canRetry(err: unknown): boolean {
async function validateKeyAndPersist(config: Config, key: string): Promise<void> {
process.stderr.write("Testing key... ");
process.stderr.write("\r\n" + JSON.stringify(config));
const testConfig = { ...config, apiKey: key };
const requestOpts = {
url: chatEndpoint(testConfig.baseUrl),
@@ -102,7 +104,9 @@ export default defineCommand({
const hasApiKey = !!(config.apiKey || config.fileApiKey);
await runConsoleLogin(resolveConsoleOrigin(), {
needApiKey: !hasApiKey,
onApiKey: (key) => validateKeyAndPersist(config, key),
onApiKey: ({ apiKey, baseUrl }) => {
return validateKeyAndPersist({ ...config, ...(baseUrl ? { baseUrl } : {}) }, apiKey);
},
});
return;
}