mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
fix(oauth): use api.anthropic.com for Claude token exchange to avoid Cloudflare bot block on VPS (#3203)
Integrated into release/v3.8.11
This commit is contained in:
@@ -74,6 +74,12 @@ _Development cycle in progress — entries are added as work merges into `releas
|
||||
|
||||
---
|
||||
|
||||
## [3.8.10] — Unreleased
|
||||
|
||||
_Development cycle in progress — entries are added as work merges into `release/v3.8.10` and finalized by the release flow._
|
||||
|
||||
---
|
||||
|
||||
## [3.8.9] — 2026-06-03
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
@@ -67,8 +67,8 @@ export const OAUTH_ENDPOINTS = {
|
||||
auth: "https://auth.openai.com/oauth/authorize",
|
||||
},
|
||||
anthropic: {
|
||||
token: "https://console.anthropic.com/v1/oauth/token",
|
||||
auth: "https://console.anthropic.com/v1/oauth/authorize",
|
||||
token: "https://api.anthropic.com/v1/oauth/token",
|
||||
auth: "https://api.anthropic.com/v1/oauth/authorize",
|
||||
},
|
||||
qwen: {
|
||||
token: "https://chat.qwen.ai/api/v1/oauth2/token", // From CLIProxyAPI
|
||||
|
||||
@@ -647,7 +647,7 @@ const _REGISTRY_EAGER: Record<string, RegistryEntry> = {
|
||||
oauth: {
|
||||
clientIdEnv: "CLAUDE_OAUTH_CLIENT_ID",
|
||||
clientIdDefault: "9d1c250a-e61b-44d9-88ed-5944d1962f5e",
|
||||
tokenUrl: "https://console.anthropic.com/v1/oauth/token",
|
||||
tokenUrl: "https://api.anthropic.com/v1/oauth/token",
|
||||
},
|
||||
models: [
|
||||
{
|
||||
@@ -1337,7 +1337,12 @@ const _REGISTRY_EAGER: Record<string, RegistryEntry> = {
|
||||
// #2900: big-pickle's upstream runs DeepSeek thinking mode — declare the
|
||||
// interleaved reasoning_content contract so follow-up/tool-use turns replay
|
||||
// it (otherwise DeepSeek returns 400 "reasoning_content ... must be passed back").
|
||||
{ id: "big-pickle", name: "Big Pickle", supportsReasoning: true, interleavedField: "reasoning_content" },
|
||||
{
|
||||
id: "big-pickle",
|
||||
name: "Big Pickle",
|
||||
supportsReasoning: true,
|
||||
interleavedField: "reasoning_content",
|
||||
},
|
||||
{ id: "deepseek-v4-flash-free", name: "DeepSeek V4 Flash Free", supportsReasoning: true },
|
||||
{ id: "minimax-m2.5-free", name: "MiniMax M2.5 Free", contextLength: 204800 },
|
||||
{ id: "ling-2.6-1t-free", name: "Ling 2.6 Free", contextLength: 262000 },
|
||||
@@ -1347,7 +1352,13 @@ const _REGISTRY_EAGER: Record<string, RegistryEntry> = {
|
||||
contextLength: 131000,
|
||||
},
|
||||
{ id: "nemotron-3-super-free", name: "Nemotron 3 Super Free", contextLength: 1000000 },
|
||||
{ id: "qwen3.6-plus-free", name: "Qwen3.6 Plus Free", targetFormat: "claude", supportsVision: false, contextLength: 200000, },
|
||||
{
|
||||
id: "qwen3.6-plus-free",
|
||||
name: "Qwen3.6 Plus Free",
|
||||
targetFormat: "claude",
|
||||
supportsVision: false,
|
||||
contextLength: 200000,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -1409,7 +1420,12 @@ const _REGISTRY_EAGER: Record<string, RegistryEntry> = {
|
||||
// #2900: big-pickle's upstream runs DeepSeek thinking mode — declare the
|
||||
// interleaved reasoning_content contract so follow-up/tool-use turns replay
|
||||
// it (otherwise DeepSeek returns 400 "reasoning_content ... must be passed back").
|
||||
{ id: "big-pickle", name: "Big Pickle", supportsReasoning: true, interleavedField: "reasoning_content" },
|
||||
{
|
||||
id: "big-pickle",
|
||||
name: "Big Pickle",
|
||||
supportsReasoning: true,
|
||||
interleavedField: "reasoning_content",
|
||||
},
|
||||
{ id: "gpt-5-nano", name: "GPT 5 Nano", contextLength: 400000 },
|
||||
{ id: "gpt-5", name: "GPT 5" },
|
||||
{ id: "gpt-5-codex", name: "GPT 5 Codex" },
|
||||
@@ -2346,7 +2362,6 @@ const _REGISTRY_EAGER: Record<string, RegistryEntry> = {
|
||||
models: [{ id: "auto", name: "Auto" }],
|
||||
},
|
||||
|
||||
|
||||
kluster: {
|
||||
id: "kluster",
|
||||
alias: "kluster",
|
||||
@@ -2380,7 +2395,6 @@ const _REGISTRY_EAGER: Record<string, RegistryEntry> = {
|
||||
models: [{ id: "liquid-lfm-40b", name: "Liquid LFM 40B" }],
|
||||
},
|
||||
|
||||
|
||||
monsterapi: {
|
||||
id: "monsterapi",
|
||||
alias: "monster",
|
||||
@@ -2418,7 +2432,6 @@ const _REGISTRY_EAGER: Record<string, RegistryEntry> = {
|
||||
],
|
||||
},
|
||||
|
||||
|
||||
chutes: {
|
||||
id: "chutes",
|
||||
alias: "chutes",
|
||||
@@ -3587,7 +3600,6 @@ const _REGISTRY_EAGER: Record<string, RegistryEntry> = {
|
||||
],
|
||||
},
|
||||
|
||||
|
||||
hackclub: {
|
||||
id: "hackclub",
|
||||
alias: "hc",
|
||||
|
||||
@@ -36,7 +36,7 @@ import { buildGitLabOAuthEndpoints, GITLAB_DUO_DEFAULT_BASE_URL } from "../gitla
|
||||
export const CLAUDE_CONFIG = {
|
||||
clientId: process.env.CLAUDE_OAUTH_CLIENT_ID || "9d1c250a-e61b-44d9-88ed-5944d1962f5e",
|
||||
authorizeUrl: "https://claude.ai/oauth/authorize",
|
||||
tokenUrl: "https://console.anthropic.com/v1/oauth/token",
|
||||
tokenUrl: "https://api.anthropic.com/v1/oauth/token",
|
||||
redirectUri:
|
||||
process.env.CLAUDE_CODE_REDIRECT_URI || "https://platform.claude.com/oauth/code/callback",
|
||||
scopes: [
|
||||
|
||||
52
tests/unit/claude-oauth-vps-endpoints.test.ts
Normal file
52
tests/unit/claude-oauth-vps-endpoints.test.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { test } from "node:test";
|
||||
import assert from "node:assert";
|
||||
import { CLAUDE_CONFIG } from "@/lib/oauth/constants/oauth.ts";
|
||||
import { OAUTH_ENDPOINTS } from "@omniroute/open-sse/config/constants.ts";
|
||||
import { REGISTRY } from "@omniroute/open-sse/config/providerRegistry.ts";
|
||||
|
||||
test("CLAUDE_CONFIG.tokenUrl uses api.anthropic.com (not console.anthropic.com)", () => {
|
||||
assert.ok(
|
||||
CLAUDE_CONFIG.tokenUrl.includes("api.anthropic.com"),
|
||||
`Expected api.anthropic.com but got ${CLAUDE_CONFIG.tokenUrl}`
|
||||
);
|
||||
assert.equal(CLAUDE_CONFIG.tokenUrl, "https://api.anthropic.com/v1/oauth/token");
|
||||
});
|
||||
|
||||
test("OAUTH_ENDPOINTS.anthropic uses api.anthropic.com for token and auth", () => {
|
||||
assert.ok(
|
||||
OAUTH_ENDPOINTS.anthropic.token.includes("api.anthropic.com"),
|
||||
`Expected api.anthropic.com but got ${OAUTH_ENDPOINTS.anthropic.token}`
|
||||
);
|
||||
assert.ok(
|
||||
OAUTH_ENDPOINTS.anthropic.auth.includes("api.anthropic.com"),
|
||||
`Expected api.anthropic.com but got ${OAUTH_ENDPOINTS.anthropic.auth}`
|
||||
);
|
||||
assert.equal(OAUTH_ENDPOINTS.anthropic.token, "https://api.anthropic.com/v1/oauth/token");
|
||||
assert.equal(OAUTH_ENDPOINTS.anthropic.auth, "https://api.anthropic.com/v1/oauth/authorize");
|
||||
});
|
||||
|
||||
test("Provider registry claude oauth.tokenUrl uses api.anthropic.com", () => {
|
||||
const claude = REGISTRY.claude;
|
||||
assert.ok(claude, "claude provider should exist in registry");
|
||||
assert.ok(
|
||||
claude.oauth?.tokenUrl?.includes("api.anthropic.com"),
|
||||
`Expected api.anthropic.com but got ${claude.oauth?.tokenUrl}`
|
||||
);
|
||||
assert.equal(claude.oauth.tokenUrl, "https://api.anthropic.com/v1/oauth/token");
|
||||
});
|
||||
|
||||
test("No console.anthropic.com remains in OAuth constants or registry", () => {
|
||||
const allUrls = [
|
||||
CLAUDE_CONFIG.tokenUrl,
|
||||
OAUTH_ENDPOINTS.anthropic.token,
|
||||
OAUTH_ENDPOINTS.anthropic.auth,
|
||||
REGISTRY.claude?.oauth?.tokenUrl,
|
||||
].filter(Boolean);
|
||||
for (const url of allUrls) {
|
||||
assert.equal(
|
||||
url.includes("console.anthropic.com"),
|
||||
false,
|
||||
`Found console.anthropic.com in ${url}`
|
||||
);
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user