fix(docs): correct OAuth redirect URI to /callback in Fly.io guide (#3732) (#3769)

The Fly.io deployment guide told users to register
<BASE_URL>/api/oauth/<provider>/callback, but OmniRoute's browser OAuth
flow (OAuthModal.startOAuthFlow) uses a single <BASE_URL>/callback handler
— there is no per-provider callback route. The mismatch made GitLab Duo
(and every OAuth provider) reject the flow with 'The redirect URI included
is not valid'. Fix the doc to the real /callback path and add a guard test.

Refs #3732
This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-06-13 08:56:18 -03:00
committed by GitHub
parent f7d895e8ff
commit 32b0f9303a
3 changed files with 53 additions and 6 deletions

View File

@@ -8,6 +8,8 @@
### 🐛 Fixed
- fix(docs): correct the OAuth redirect URI in the Fly.io deployment guide. It told users to register `<NEXT_PUBLIC_BASE_URL>/api/oauth/<provider>/callback`, but OmniRoute's browser OAuth flow uses a single `<NEXT_PUBLIC_BASE_URL>/callback` handler (there is no per-provider callback route). The mismatch caused GitLab Duo (and any OAuth provider) to reject the flow with "The redirect URI included is not valid". Added a regression guard test. ([#3732](https://github.com/diegosouzapw/OmniRoute/issues/3732))
- fix(providers): give Ollama Cloud's `kimi-k2.7-code` its real capabilities (262K context, 262K max output, vision + thinking + tools) instead of the degraded `128000 / 8192` defaults. The model had no spec/registry entry, so importing it via "Import from /models" (whose `/v1/models` upstream returns no per-model metadata) left it as a bare custom model with fallback capabilities. Added a global `kimi-k2.7-code` model spec (parity with `kimi-k2.6`) plus a registry entry on `ollama-cloud`. ([#3761](https://github.com/diegosouzapw/OmniRoute/issues/3761) — thanks @SultanKs4)
- fix(providers): repair qwen-web (chat.qwen.ai) connection validation, which failed with a misleading `provider.validation.ssrf_blocked` error. qwen-web had no specialty validator, so the generic OpenAI-compatible path probed a non-existent `/api/v2/models` URL that answers with a 307 redirect — the outbound guard blocked the redirect and the route mislabeled it as an SSRF security block. Added a `qwen-web` specialty validator that probes the real session endpoint (`GET /api/v2/user`, mirroring the executor's anti-bot headers + cookie-jar replay). Also hardened `toValidationErrorResult` so a blocked redirect is only flagged `securityBlocked` when its target is a private/internal host — a benign 3xx to a public host is no longer mislabeled as an SSRF attempt (this affected every web-cookie provider, not just qwen). ([#3288](https://github.com/diegosouzapw/OmniRoute/issues/3288), [#3758](https://github.com/diegosouzapw/OmniRoute/issues/3758))

View File

@@ -206,16 +206,14 @@ flyctl deploy
2. **在 provider 控制台配置回调 URL (configure the callback URL on the provider console / configure a URL de callback no painel do provider)**
通常格式为 (typical format / formato típico)
所有 OAuth provider 共用同一个回调路径 `/callback`,不带 provider 段 (all OAuth providers share the single callback path `/callback` — there is NO per-provider callback route / todos os providers OAuth usam o mesmo callback `/callback`, sem segmento por provider)
```text
<NEXT_PUBLIC_BASE_URL>/api/oauth/<provider>/callback
<NEXT_PUBLIC_BASE_URL>/callback
```
例如 (e.g. / p.ex.)
- `https://omniroute.fly.dev/api/oauth/gemini/callback`
- `https://omniroute.fly.dev/api/oauth/antigravity/callback`
- `https://omniroute.fly.dev/api/oauth/cursor/callback`
例如 (e.g. / p.ex.),无论是 Gemini、Antigravity、Cursor 还是 GitLab Duo (regardless of Gemini / Antigravity / Cursor / GitLab Duo, etc.)
- `https://omniroute.fly.dev/callback`
如果 `NEXT_PUBLIC_BASE_URL` 与 provider 控制台中注册的回调 URL 不一致OAuth 流程会在浏览器回跳阶段失败 (mismatch between `NEXT_PUBLIC_BASE_URL` and the registered callback URL will cause OAuth to fail at the browser redirect step / divergência entre `NEXT_PUBLIC_BASE_URL` e a URL de callback registrada quebra o OAuth no redirect do navegador)。

View File

@@ -0,0 +1,47 @@
import test from "node:test";
import assert from "node:assert/strict";
import { existsSync, readFileSync } from "node:fs";
import { resolve, join } from "node:path";
// Regression guard for #3732. Browser OAuth (GitLab Duo, Gemini, Antigravity, Cursor, …)
// completes at the single App Router page `/callback` — there is NO per-provider
// `/api/oauth/<provider>/callback` route. `OAuthModal.startOAuthFlow` builds the
// redirect_uri as `<origin>/callback`, so a user who registers a different path in the
// provider console gets "The redirect URI included is not valid". Docs that told users
// to register `<BASE_URL>/api/oauth/<provider>/callback` were wrong and caused #3732.
// These assertions lock the documented path to the real handler.
const cwd = process.cwd();
test("the real browser OAuth callback handler lives at /callback", () => {
assert.ok(
existsSync(resolve(join(cwd, "src/app/callback/page.tsx"))),
"src/app/callback/page.tsx (the /callback handler) must exist"
);
// There is no `callback` action in the [provider]/[action] OAuth route, so a
// `/api/oauth/<provider>/callback` URL is NOT a real endpoint.
const oauthRoute = readFileSync(
resolve(join(cwd, "src/app/api/oauth/[provider]/[action]/route.ts")),
"utf8"
);
assert.ok(
!/action === ["']callback["']/.test(oauthRoute),
"there must be no `callback` action in the OAuth [provider]/[action] route"
);
});
test("the Fly.io deployment guide documents the correct /callback redirect URI (#3732)", () => {
const guide = readFileSync(
resolve(join(cwd, "docs/ops/FLY_IO_DEPLOYMENT_GUIDE.md")),
"utf8"
);
assert.ok(
!/\/api\/oauth\/[^/\s]+\/callback/.test(guide),
"the guide must not tell users to register a per-provider /api/oauth/<provider>/callback URI"
);
assert.match(
guide,
/<NEXT_PUBLIC_BASE_URL>\/callback/,
"the guide must document the real `<NEXT_PUBLIC_BASE_URL>/callback` redirect URI"
);
});