From 858d7629181ce1917926b7dff0f1a8b9c2e7dfaa Mon Sep 17 00:00:00 2001 From: danscMax <153344025+danscMax@users.noreply.github.com> Date: Sat, 18 Jul 2026 17:35:09 +0300 Subject: [PATCH] fix(oauth): repair qwen + codebuddy-cn device-code endpoints (#7517) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both device-code providers failed at the upstream request (surfacing in the companion extension as 'ошибка сервера OmniRoute'). Diagnosed by calling the upstream endpoints directly: - qwen: QWEN_CONFIG used the bare host qwen.ai, whose /api/v1/oauth2/device/code and /token paths return 404 Not Found. The working qwen-code device flow lives at chat.qwen.ai (verified: 200 + a valid device_code/user_code). Point both URLs at chat.qwen.ai. - codebuddy-cn: the Tencent state endpoint reads 'platform' from the QUERY string, not the JSON body. Sending it only in the body returned 400 {"code":10001,"msg":"platform is empty"}. Passing ?platform=CLI returned 200 with {code:0, data:{state, authUrl}}. Build the stateUrl with the platform query param (body kept as-is). Validation: live upstream calls returned 200 for both corrected requests (device flow can't be hit from CI). Regression guard: tests/unit/oauth-device-code-endpoints.test.ts. --- src/lib/oauth/constants/oauth.ts | 6 ++-- src/lib/oauth/providers/codebuddy-cn.ts | 5 ++- .../unit/oauth-device-code-endpoints.test.ts | 32 +++++++++++++++++++ 3 files changed, 40 insertions(+), 3 deletions(-) create mode 100644 tests/unit/oauth-device-code-endpoints.test.ts diff --git a/src/lib/oauth/constants/oauth.ts b/src/lib/oauth/constants/oauth.ts index b9c36adc26..b5012102fc 100644 --- a/src/lib/oauth/constants/oauth.ts +++ b/src/lib/oauth/constants/oauth.ts @@ -74,8 +74,10 @@ export const CODEX_CONFIG = { // Qwen OAuth Configuration (Device Code Flow with PKCE) export const QWEN_CONFIG = { clientId: resolvePublicCred("qwen_id", "QWEN_OAUTH_CLIENT_ID"), - deviceCodeUrl: "https://qwen.ai/api/v1/oauth2/device/code", - tokenUrl: "https://qwen.ai/api/v1/oauth2/token", + // Host is chat.qwen.ai — the bare qwen.ai host 404s on these paths (verified: the qwen-code + // device flow lives at chat.qwen.ai, returning a valid device_code; qwen.ai returns Not Found). + deviceCodeUrl: "https://chat.qwen.ai/api/v1/oauth2/device/code", + tokenUrl: "https://chat.qwen.ai/api/v1/oauth2/token", scope: "openid profile email model.completion", codeChallengeMethod: "S256", }; diff --git a/src/lib/oauth/providers/codebuddy-cn.ts b/src/lib/oauth/providers/codebuddy-cn.ts index 3a4fa93544..f24a4de0db 100644 --- a/src/lib/oauth/providers/codebuddy-cn.ts +++ b/src/lib/oauth/providers/codebuddy-cn.ts @@ -38,7 +38,10 @@ export const codebuddyCn = { flowType: "device_code" as const, requestDeviceCode: async (config: CodeBuddyConfig): Promise => { - const response = await fetch(config.stateUrl, { + // CodeBuddy reads `platform` from the QUERY string, not the JSON body — sending it only in the + // body returns 400 "platform is empty" (verified). Pass it as a query param; body kept as-is. + const stateUrl = `${config.stateUrl}?platform=${encodeURIComponent(config.platform)}`; + const response = await fetch(stateUrl, { method: "POST", headers: { "Content-Type": "application/json", diff --git a/tests/unit/oauth-device-code-endpoints.test.ts b/tests/unit/oauth-device-code-endpoints.test.ts new file mode 100644 index 0000000000..325a1b259d --- /dev/null +++ b/tests/unit/oauth-device-code-endpoints.test.ts @@ -0,0 +1,32 @@ +import { test } from "node:test"; +import assert from "node:assert/strict"; +import { readFileSync } from "node:fs"; +import { fileURLToPath } from "node:url"; +import { dirname, resolve } from "node:path"; + +// Regression guards for the two device-code providers that returned "ошибка сервера OmniRoute": +// +// qwen — QWEN_CONFIG pointed at the bare host qwen.ai, whose device/token paths 404. +// The working qwen-code device flow lives at chat.qwen.ai (verified live: 200 + +// a valid device_code). Guard: the config uses chat.qwen.ai, never bare qwen.ai. +// codebuddy-cn— the Tencent state endpoint reads `platform` from the QUERY string, not the JSON +// body; body-only returned 400 "platform is empty" (verified live). The fix passes +// it as a query param. Guard: requestDeviceCode builds the URL with ?platform=. +// +// Source-level: the real validation is the live upstream 200 (can't be hit from CI); these pin +// the exact change so a revert to the broken host / body-only platform fails here. +const here = dirname(fileURLToPath(import.meta.url)); +const read = (p: string) => readFileSync(resolve(here, "../..", p), "utf8"); + +test("qwen device-code config uses chat.qwen.ai (not the 404ing bare qwen.ai)", () => { + const oauth = read("src/lib/oauth/constants/oauth.ts"); + const qwenBlock = oauth.slice(oauth.indexOf("QWEN_CONFIG"), oauth.indexOf("QWEN_CONFIG") + 700); + assert.match(qwenBlock, /chat\.qwen\.ai\/api\/v1\/oauth2\/device\/code/, "deviceCodeUrl host"); + assert.match(qwenBlock, /chat\.qwen\.ai\/api\/v1\/oauth2\/token/, "tokenUrl host"); + assert.doesNotMatch(qwenBlock, /"https:\/\/qwen\.ai\//, "must not use the bare qwen.ai host"); +}); + +test("codebuddy-cn device-code sends platform as a query param (not body-only)", () => { + const cb = read("src/lib/oauth/providers/codebuddy-cn.ts"); + assert.match(cb, /\?platform=\$\{encodeURIComponent\(config\.platform\)\}/, "platform query param"); +});