Files
diegosouzapw--omniroute/tests/unit/token-refresh-service.test.ts
2026-07-13 13:39:12 +08:00

1353 lines
42 KiB
TypeScript

import test from "node:test";
import assert from "node:assert/strict";
const tokenRefresh = await import("../../open-sse/services/tokenRefresh.ts");
const { PROVIDERS, OAUTH_ENDPOINTS } = await import("../../open-sse/config/constants.ts");
const {
TOKEN_EXPIRY_BUFFER_MS,
refreshAccessToken,
refreshClineToken,
refreshKimiCodingToken,
refreshClaudeOAuthToken,
refreshGoogleToken,
refreshQwenToken,
refreshCodexToken,
refreshKiroToken,
refreshQoderToken,
refreshGitHubToken,
refreshCopilotToken,
supportsTokenRefresh,
isUnrecoverableRefreshError,
getAccessToken,
formatProviderCredentials,
getAllAccessTokens,
isProviderBlocked,
getCircuitBreakerStatus,
getConnectionRefreshMutexStatus,
refreshWithRetry,
} = tokenRefresh;
type LogLevel = "debug" | "info" | "warn" | "error";
type LogEntry = {
level: LogLevel;
scope: unknown;
message: unknown;
meta: unknown;
};
type MockLogger = {
entries: LogEntry[];
debug: (...args: [unknown?, unknown?, unknown?]) => void;
info: (...args: [unknown?, unknown?, unknown?]) => void;
warn: (...args: [unknown?, unknown?, unknown?]) => void;
error: (...args: [unknown?, unknown?, unknown?]) => void;
};
type TestFetch = typeof fetch;
type FastSetTimeout = typeof globalThis.setTimeout & {
__promisify__?: typeof globalThis.setTimeout.__promisify__;
};
function createLog(): MockLogger {
const entries: LogEntry[] = [];
const push = (level: LogLevel, args: [unknown?, unknown?, unknown?]) => {
const [scope, message, meta] = args;
entries.push({ level, scope, message, meta });
};
return {
entries,
debug: (...args) => push("debug", args),
info: (...args) => push("info", args),
warn: (...args) => push("warn", args),
error: (...args) => push("error", args),
};
}
function jsonResponse(body: any, status = 200) {
return new Response(JSON.stringify(body), {
status,
headers: { "content-type": "application/json" },
});
}
function textResponse(text: any, status = 400) {
return new Response(text, {
status,
headers: { "content-type": "text/plain" },
});
}
function bodyToString(body: BodyInit | null | undefined) {
if (typeof body === "string") return body;
if (body instanceof URLSearchParams) return body.toString();
return String(body ?? "");
}
async function withMockedFetch<TResult>(fetchImpl: TestFetch, fn: () => Promise<TResult>) {
const originalFetch = globalThis.fetch;
globalThis.fetch = fetchImpl;
try {
return await fn();
} finally {
globalThis.fetch = originalFetch;
}
}
async function withMockedNow<TResult>(now: number, fn: () => Promise<TResult>) {
const originalNow = Date.now;
Date.now = () => now;
try {
return await fn();
} finally {
Date.now = originalNow;
}
}
async function withPatchedProperties<TResult>(
target: object,
patch: Record<string, unknown>,
fn: () => Promise<TResult>
) {
const previous = new Map<string, unknown>();
const targetRecord = target as Record<string, unknown>;
for (const [key, value] of Object.entries(patch)) {
previous.set(
key,
Object.prototype.hasOwnProperty.call(targetRecord, key) ? targetRecord[key] : undefined
);
targetRecord[key] = value;
}
try {
return await fn();
} finally {
for (const [key] of Object.entries(patch)) {
const prior = previous.get(key);
if (prior === undefined) {
delete targetRecord[key];
} else {
targetRecord[key] = prior;
}
}
}
}
async function withFastRetryTimers<TResult>(fn: () => Promise<TResult>) {
const originalSetTimeout = globalThis.setTimeout as FastSetTimeout;
const fastSetTimeout: FastSetTimeout = Object.assign(
((callback: TimerHandler, delay = 0, ...args: unknown[]) =>
originalSetTimeout(callback, delay === 30_000 ? delay : 0, ...args)) as typeof setTimeout,
{ __promisify__: originalSetTimeout.__promisify__ }
);
globalThis.setTimeout = fastSetTimeout;
try {
return await fn();
} finally {
globalThis.setTimeout = originalSetTimeout;
}
}
test("TOKEN_EXPIRY_BUFFER_MS stays at five minutes", () => {
assert.equal(TOKEN_EXPIRY_BUFFER_MS, 5 * 60 * 1000);
});
test("refreshAccessToken returns null when no provider refresh endpoint exists", async () => {
const log = createLog();
const result = await refreshAccessToken("qoder", "refresh-token", {}, log);
assert.equal(result, null);
assert.equal(
log.entries.some((entry) => entry.level === "warn"),
true
);
});
test("refreshAccessToken returns null when refresh token is missing", async () => {
const log = createLog();
await withPatchedProperties(
PROVIDERS,
{
"custom-oauth-task-207": { tokenUrl: "https://auth.example.com/token" },
},
async () => {
const result = await refreshAccessToken("custom-oauth-task-207", null, {}, log);
assert.equal(result, null);
}
);
});
test("refreshAccessToken posts form data and returns rotated tokens", async () => {
const log = createLog();
const calls: any[] = [];
await withPatchedProperties(
PROVIDERS,
{
"custom-oauth-task-207": {
refreshUrl: "https://auth.example.com/token",
clientId: "client-id",
clientSecret: "client-secret",
},
},
async () => {
await withMockedFetch(
async (url, options = {}) => {
calls.push({ url, options });
return jsonResponse({
access_token: "new-access",
refresh_token: "new-refresh",
expires_in: 3600,
});
},
async () => {
const result = await refreshAccessToken("custom-oauth-task-207", "refresh-123", {}, log);
assert.deepEqual(result, {
accessToken: "new-access",
refreshToken: "new-refresh",
expiresIn: 3600,
});
}
);
}
);
assert.equal(calls[0].url, "https://auth.example.com/token");
assert.equal(
bodyToString(calls[0].options.body),
"grant_type=refresh_token&refresh_token=refresh-123&client_id=client-id&client_secret=client-secret"
);
});
test("refreshAccessToken returns null on upstream refresh failure", async () => {
const log = createLog();
await withPatchedProperties(
PROVIDERS,
{
"custom-oauth-task-207": { tokenUrl: "https://auth.example.com/token" },
},
async () => {
await withMockedFetch(
async () => textResponse("rate limited", 429),
async () => {
const result = await refreshAccessToken("custom-oauth-task-207", "refresh-123", {}, log);
assert.equal(result, null);
assert.equal(
log.entries.some((entry) => entry.level === "error"),
true
);
}
);
}
);
});
test("refreshClineToken handles nested payloads and computes expiresIn", async () => {
const log = createLog();
const calls: any[] = [];
await withMockedNow(1_700_000_000_000, async () => {
await withMockedFetch(
async (url, options = {}) => {
calls.push({ url, options });
return jsonResponse({
data: {
accessToken: "cline-access",
refreshToken: "cline-refresh",
expiresAt: new Date(Date.now() + 95_000).toISOString(),
},
});
},
async () => {
const result = await refreshClineToken("refresh-cline", log);
assert.equal(result?.accessToken, "cline-access");
assert.equal(result?.refreshToken, "cline-refresh");
assert.equal(result?.expiresIn, 95);
}
);
});
assert.equal(calls[0].url, PROVIDERS.cline.refreshUrl);
assert.deepEqual(JSON.parse(calls[0].options.body), {
refreshToken: "refresh-cline",
grantType: "refresh_token",
clientType: "extension",
});
});
test("refreshKimiCodingToken adds provider-specific headers and fields", async () => {
const log = createLog();
const calls: any[] = [];
await withMockedFetch(
async (url, options = {}) => {
calls.push({ url, options });
return jsonResponse({
access_token: "kimi-access",
refresh_token: "kimi-refresh-next",
expires_in: 7200,
token_type: "Bearer",
scope: "coding offline_access",
});
},
async () => {
// Pass providerSpecificData with a stable deviceId (second positional arg after signature change)
const result = await refreshKimiCodingToken(
"kimi-refresh",
{ deviceId: "test-stable-device" },
log
);
assert.deepEqual(result, {
accessToken: "kimi-access",
refreshToken: "kimi-refresh-next",
expiresIn: 7200,
tokenType: "Bearer",
scope: "coding offline_access",
});
}
);
assert.equal(calls[0].url, PROVIDERS["kimi-coding"].refreshUrl);
// Platform is now "kimi_cli" (matching the real Kimi CLI fingerprint)
assert.equal(calls[0].options.headers["X-Msh-Platform"], "kimi_cli");
// Version comes from KIMI_CLI_VERSION env or default "1.36.0"
assert.ok(calls[0].options.headers["X-Msh-Version"], "X-Msh-Version must be set");
// Device-Id must NOT be an ephemeral "kimi-refresh-<timestamp>" value
assert.ok(
calls[0].options.headers["X-Msh-Device-Id"] &&
!calls[0].options.headers["X-Msh-Device-Id"].startsWith("kimi-refresh-"),
"X-Msh-Device-Id must be stable (not ephemeral kimi-refresh-<timestamp>)"
);
// When providerSpecificData.deviceId is provided, it should be used directly
assert.equal(calls[0].options.headers["X-Msh-Device-Id"], "test-stable-device");
assert.match(bodyToString(calls[0].options.body), /grant_type=refresh_token/);
});
test("refreshClaudeOAuthToken posts the anthropic oauth refresh contract", async () => {
const log = createLog();
const calls: any[] = [];
await withMockedFetch(
async (url, options = {}) => {
calls.push({ url, options });
return jsonResponse({
access_token: "claude-access",
refresh_token: "claude-refresh-next",
expires_in: 1800,
});
},
async () => {
const result = await refreshClaudeOAuthToken("claude-refresh", log);
assert.deepEqual(result, {
accessToken: "claude-access",
refreshToken: "claude-refresh-next",
expiresIn: 1800,
});
}
);
assert.equal(calls[0].url, OAUTH_ENDPOINTS.anthropic.token);
assert.equal(calls[0].options.headers["anthropic-beta"], "oauth-2025-04-20");
assert.match(calls[0].options.body, /grant_type=refresh_token/);
assert.match(calls[0].options.body, /client_id=/);
});
test("refreshGoogleToken exchanges refresh tokens against the shared google endpoint", async () => {
const log = createLog();
const calls: any[] = [];
await withMockedFetch(
async (url, options = {}) => {
calls.push({ url, options });
return jsonResponse({
access_token: "google-access",
refresh_token: "google-refresh-next",
expires_in: 3600,
});
},
async () => {
const result = await refreshGoogleToken("google-refresh", "gid", "gsecret", log);
assert.deepEqual(result, {
accessToken: "google-access",
refreshToken: "google-refresh-next",
expiresIn: 3600,
});
}
);
assert.equal(calls[0].url, OAUTH_ENDPOINTS.google.token);
assert.equal(
bodyToString(calls[0].options.body),
"grant_type=refresh_token&refresh_token=google-refresh&client_id=gid&client_secret=gsecret"
);
});
test("refreshQwenToken maps resource_url into providerSpecificData", async () => {
const log = createLog();
await withMockedFetch(
async () =>
jsonResponse({
access_token: "qwen-access",
refresh_token: "qwen-refresh-next",
expires_in: 1500,
resource_url: "https://chat.qwen.ai/workspace/resource",
}),
async () => {
const result = await refreshQwenToken("qwen-refresh", log);
assert.deepEqual(result, {
accessToken: "qwen-access",
refreshToken: "qwen-refresh-next",
expiresIn: 1500,
providerSpecificData: {
resourceUrl: "https://chat.qwen.ai/workspace/resource",
},
});
}
);
});
test("refreshQwenToken surfaces invalid_request as unrecoverable", async () => {
const log = createLog();
await withMockedFetch(
async () => textResponse(JSON.stringify({ error: "invalid_request" }), 400),
async () => {
const result = await refreshQwenToken("qwen-refresh", log);
// Normalized to unrecoverable_refresh_error sentinel (Fix 4)
assert.deepEqual(result, { error: "unrecoverable_refresh_error", code: "invalid_request" });
}
);
});
test("refreshCodexToken recognizes refresh_token_reused responses", async () => {
const log = createLog();
await withMockedFetch(
async () => textResponse(JSON.stringify({ error: { code: "refresh_token_reused" } }), 400),
async () => {
const result = await refreshCodexToken("codex-refresh", log);
assert.deepEqual(result, {
error: "unrecoverable_refresh_error",
code: "refresh_token_reused",
});
}
);
});
// Port from decolua/9router#1821 (sacwooky): a 401 from OpenAI's OAuth token
// endpoint means the refresh credential itself was rejected (e.g. rotated away
// or a payload whose error code we do not yet recognize). Retrying with the
// same refresh token will never succeed — surface re-auth, do not loop.
test("refreshCodexToken treats any 401 from the token endpoint as unrecoverable", async () => {
const log = createLog();
await withMockedFetch(
async () =>
textResponse(
JSON.stringify({
error: {
// A payload variant whose code/type are NOT in the existing
// unrecoverable set — only the 401 status proves the token is dead.
message: "Could not validate your token. Please try signing in again.",
type: "invalid_request_error",
},
}),
401
),
async () => {
const result = await refreshCodexToken("codex-refresh", log);
assert.equal(
result?.error,
"unrecoverable_refresh_error",
"401 from OpenAI token endpoint must surface re-auth instead of returning null (which triggers retry)"
);
}
);
});
test("refreshKiroToken uses the AWS OIDC flow when client credentials are present", async () => {
const log = createLog();
const calls: any[] = [];
await withMockedFetch(
async (url, options = {}) => {
calls.push({ url, options });
return jsonResponse({
accessToken: "kiro-aws-access",
refreshToken: "kiro-aws-refresh-next",
expiresIn: 900,
});
},
async () => {
const result = await refreshKiroToken(
"kiro-refresh",
{
authMethod: "idc",
clientId: "aws-client",
clientSecret: "aws-secret",
region: "eu-west-1",
},
log
);
assert.deepEqual(result, {
accessToken: "kiro-aws-access",
refreshToken: "kiro-aws-refresh-next",
expiresIn: 900,
});
}
);
assert.equal(calls[0].url, "https://oidc.eu-west-1.amazonaws.com/token");
assert.deepEqual(JSON.parse(calls[0].options.body), {
clientId: "aws-client",
clientSecret: "aws-secret",
refreshToken: "kiro-refresh",
grantType: "refresh_token",
});
});
test("refreshKiroToken uses stored region for AWS OIDC refresh without authMethod", async () => {
const log = createLog();
const calls: any[] = [];
await withMockedFetch(
async (url, options = {}) => {
calls.push({ url, options });
return jsonResponse({
accessToken: "kiro-aws-access",
refreshToken: "kiro-aws-refresh-next",
expiresIn: 900,
});
},
async () => {
const result = await refreshKiroToken(
"kiro-refresh",
{
clientId: "aws-client",
clientSecret: "aws-secret",
region: "ap-southeast-1",
},
log
);
assert.deepEqual(result, {
accessToken: "kiro-aws-access",
refreshToken: "kiro-aws-refresh-next",
expiresIn: 900,
});
}
);
assert.equal(calls[0].url, "https://oidc.ap-southeast-1.amazonaws.com/token");
assert.deepEqual(JSON.parse(calls[0].options.body), {
clientId: "aws-client",
clientSecret: "aws-secret",
refreshToken: "kiro-refresh",
grantType: "refresh_token",
});
});
test("refreshKiroToken falls back to the social-auth refresh endpoint", async () => {
const log = createLog();
const calls: any[] = [];
await withMockedFetch(
async (url, options = {}) => {
calls.push({ url, options });
return jsonResponse({
accessToken: "kiro-social-access",
refreshToken: "kiro-social-refresh-next",
expiresIn: 1200,
});
},
async () => {
const result = await refreshKiroToken("kiro-refresh", null, log);
assert.deepEqual(result, {
accessToken: "kiro-social-access",
refreshToken: "kiro-social-refresh-next",
expiresIn: 1200,
});
}
);
assert.equal(calls[0].url, PROVIDERS.kiro.tokenUrl);
assert.deepEqual(JSON.parse(calls[0].options.body), {
refreshToken: "kiro-refresh",
});
});
// Issue #2328 — once a social-auth token has clientId/clientSecret stored
// (because it was imported after v3.8.0), refreshKiroToken must use the AWS OIDC
// endpoint, not the shared social-auth endpoint, even though authMethod is "google".
test("refreshKiroToken uses AWS OIDC path for social-auth token when clientId is present (#2328)", async () => {
const log = createLog();
const calls: any[] = [];
await withMockedFetch(
async (url, options = {}) => {
calls.push({ url, options });
return jsonResponse({
accessToken: "kiro-isolated-access",
refreshToken: "kiro-isolated-refresh-next",
expiresIn: 900,
});
},
async () => {
const result = await refreshKiroToken(
"kiro-social-refresh",
{
authMethod: "google",
clientId: "isolated-client-id",
clientSecret: "isolated-client-secret",
region: "us-east-1",
},
log
);
assert.deepEqual(result, {
accessToken: "kiro-isolated-access",
refreshToken: "kiro-isolated-refresh-next",
expiresIn: 900,
});
}
);
// Must call the AWS OIDC endpoint — not the shared social-auth tokenUrl
assert.ok(
calls[0].url.includes("oidc.us-east-1.amazonaws.com/token"),
`expected AWS OIDC endpoint but got ${calls[0].url}`
);
assert.notEqual(
calls[0].url,
PROVIDERS.kiro.tokenUrl,
"should not call the shared social-auth endpoint when clientId is set"
);
assert.deepEqual(JSON.parse(calls[0].options.body), {
clientId: "isolated-client-id",
clientSecret: "isolated-client-secret",
refreshToken: "kiro-social-refresh",
grantType: "refresh_token",
});
});
// Issue #2467 — an IMPORTED social token (authMethod === "imported") carries a
// freshly-registered clientId/clientSecret, but its refresh token is Kiro-social-issued
// and the isolated OIDC client cannot refresh it. It must use the social-auth endpoint,
// NOT AWS OIDC (which is what #2328 enabled for authMethod "google").
test("refreshKiroToken uses social-auth path for imported token even with clientId (#2467)", async () => {
const log = createLog();
const calls: any[] = [];
await withMockedFetch(
async (url, options = {}) => {
calls.push({ url, options });
return jsonResponse({
accessToken: "kiro-imported-access",
refreshToken: "kiro-imported-refresh-next",
expiresIn: 1100,
});
},
async () => {
const result = await refreshKiroToken(
"kiro-imported-refresh",
{
authMethod: "imported",
clientId: "isolated-client-id",
clientSecret: "isolated-client-secret",
region: "us-east-1",
},
log
);
assert.equal(result.accessToken, "kiro-imported-access");
}
);
// Must call the shared social-auth tokenUrl — NOT the AWS OIDC endpoint.
assert.equal(
calls[0].url,
PROVIDERS.kiro.tokenUrl,
`expected social-auth endpoint but got ${calls[0].url}`
);
assert.ok(!calls[0].url.includes("oidc."), "imported token must not use AWS OIDC");
});
test("refreshQoderToken uses basic auth once qoder oauth settings are configured", async () => {
const log = createLog();
const calls: any[] = [];
await withPatchedProperties(
PROVIDERS.qoder,
{
clientId: "qoder-client",
clientSecret: "qoder-secret",
},
async () => {
await withPatchedProperties(
OAUTH_ENDPOINTS.qoder,
{
token: "https://qoder.example.com/oauth/token",
},
async () => {
await withMockedFetch(
async (url, options = {}) => {
calls.push({ url, options });
return jsonResponse({
access_token: "qoder-access",
refresh_token: "qoder-refresh-next",
expires_in: 2400,
});
},
async () => {
const result = await refreshQoderToken("qoder-refresh", log);
assert.deepEqual(result, {
accessToken: "qoder-access",
refreshToken: "qoder-refresh-next",
expiresIn: 2400,
});
}
);
}
);
}
);
assert.equal(calls[0].url, "https://qoder.example.com/oauth/token");
assert.match(calls[0].options.headers.Authorization, /^Basic /);
});
test("refreshGitHubToken sends the real public github client_id and no client_secret (port from 9router#442)", async () => {
// GitHub Copilot's OAuth app is a public device-flow client: it has a client_id but
// NO client_secret. PROVIDERS.github.clientId must be populated from the embedded public
// cred so the refresh request actually carries a client_id — a missing one makes GitHub
// reject the refresh. The previous test patched a fake clientId/clientSecret onto
// PROVIDERS.github, masking the fact that the real config had neither. This uses the real
// config and asserts the real client_id is sent and no client_secret leaks out.
const log = createLog();
const calls: any[] = [];
await withMockedFetch(
async (url, options = {}) => {
calls.push({ url, options });
return jsonResponse({
access_token: "github-access",
refresh_token: "github-refresh-next",
expires_in: 3600,
});
},
async () => {
const result = await refreshGitHubToken("github-refresh", log);
assert.deepEqual(result, {
accessToken: "github-access",
refreshToken: "github-refresh-next",
expiresIn: 3600,
});
}
);
const body = bodyToString(calls[0].options.body);
assert.equal(calls[0].url, OAUTH_ENDPOINTS.github.token);
assert.ok(PROVIDERS.github.clientId, "PROVIDERS.github.clientId must be populated from the public cred");
assert.match(body, /client_id=Iv1\./, "the real public github client_id must be sent on refresh");
assert.ok(!body.includes("client_secret="), "no client_secret for the public github client");
});
test("refreshCopilotToken returns the short-lived copilot token", async () => {
const log = createLog();
const calls: any[] = [];
await withMockedFetch(
async (url, options = {}) => {
calls.push({ url, options });
return jsonResponse({
token: "copilot-session-token",
expires_at: "2026-01-01T00:00:00.000Z",
});
},
async () => {
const result = await refreshCopilotToken("github-access-token", log);
assert.deepEqual(result, {
token: "copilot-session-token",
expiresAt: "2026-01-01T00:00:00.000Z",
});
}
);
assert.equal(calls[0].url, "https://api.github.com/copilot_internal/v2/token");
assert.equal(calls[0].options.headers.Authorization, "token github-access-token");
});
test("supportsTokenRefresh, isUnrecoverableRefreshError and formatProviderCredentials cover provider helpers", async () => {
const log = createLog();
await withPatchedProperties(
PROVIDERS,
{
"custom-oauth-task-207": { tokenUrl: "https://auth.example.com/token" },
},
async () => {
assert.equal(supportsTokenRefresh("claude"), true);
assert.equal(supportsTokenRefresh("amazon-q"), true);
assert.equal(supportsTokenRefresh("custom-oauth-task-207"), true);
assert.equal(supportsTokenRefresh("missing-provider"), false);
}
);
assert.equal(isUnrecoverableRefreshError({ error: "refresh_token_reused" }), true);
assert.equal(isUnrecoverableRefreshError({ error: "invalid_request" }), true);
assert.equal(isUnrecoverableRefreshError({ error: "temporary_failure" }), false);
assert.deepEqual(
formatProviderCredentials(
"gemini",
{
apiKey: "gemini-key",
accessToken: "gemini-access",
projectId: "project-1",
refreshToken: "ignored",
},
log
),
{
apiKey: "gemini-key",
accessToken: "gemini-access",
projectId: "project-1",
}
);
assert.deepEqual(
formatProviderCredentials(
"antigravity",
{
accessToken: "google-access",
refreshToken: "google-refresh",
},
log
),
{
accessToken: "google-access",
refreshToken: "google-refresh",
}
);
assert.equal(formatProviderCredentials("missing-provider", {}, log), null);
});
test("getAccessToken deduplicates concurrent refreshes for the same provider and token", async () => {
const log = createLog();
let fetchCount = 0;
await withPatchedProperties(
PROVIDERS,
{
"custom-oauth-task-207": { tokenUrl: "https://auth.example.com/token" },
},
async () => {
await withMockedFetch(
async () => {
fetchCount += 1;
return jsonResponse({
access_token: "shared-access",
refresh_token: "shared-refresh-next",
expires_in: 600,
});
},
async () => {
const [first, second] = await Promise.all([
getAccessToken("custom-oauth-task-207", { refreshToken: "same-refresh" }, log),
getAccessToken("custom-oauth-task-207", { refreshToken: "same-refresh" }, log),
]);
assert.equal(fetchCount, 1);
assert.strictEqual(first, second);
assert.equal(first.accessToken, "shared-access");
}
);
}
);
});
test("getAccessToken cleans the in-flight cache after resolve and separates different tokens", async () => {
const log = createLog();
let fetchCount = 0;
await withPatchedProperties(
PROVIDERS,
{
"custom-oauth-task-207": { tokenUrl: "https://auth.example.com/token" },
},
async () => {
await withMockedFetch(
async (_url, options: RequestInit = {}) => {
fetchCount += 1;
const refreshToken = new URLSearchParams(bodyToString(options.body)).get("refresh_token");
return jsonResponse({
access_token: `access-${refreshToken}`,
refresh_token: `next-${refreshToken}`,
expires_in: 600,
});
},
async () => {
const first = await getAccessToken(
"custom-oauth-task-207",
{ refreshToken: "refresh-a" },
log
);
const second = await getAccessToken(
"custom-oauth-task-207",
{ refreshToken: "refresh-a" },
log
);
const third = await getAccessToken(
"custom-oauth-task-207",
{ refreshToken: "refresh-b" },
log
);
assert.equal(fetchCount, 3);
assert.equal(first.accessToken, "access-refresh-a");
assert.equal(second.accessToken, "access-refresh-a");
assert.equal(third.accessToken, "access-refresh-b");
}
);
}
);
});
test("getAccessToken returns null for invalid refresh token input", async () => {
const log = createLog();
const result = await getAccessToken("codex", { refreshToken: null }, log);
assert.equal(result, null);
});
test("getAllAccessTokens refreshes only active connections with providers", async () => {
const log = createLog();
let fetchCount = 0;
await withPatchedProperties(
PROVIDERS,
{
"custom-oauth-task-207": { tokenUrl: "https://auth.example.com/token" },
},
async () => {
await withMockedFetch(
async (_url, options: RequestInit = {}) => {
fetchCount += 1;
const refreshToken = new URLSearchParams(bodyToString(options.body)).get("refresh_token");
return jsonResponse({
access_token: `access-${refreshToken}`,
refresh_token: `next-${refreshToken}`,
expires_in: 900,
});
},
async () => {
const tokens = await getAllAccessTokens(
{
connections: [
{
provider: "custom-oauth-task-207",
refreshToken: "active-one",
isActive: true,
},
{
provider: "custom-oauth-task-207",
refreshToken: "inactive-one",
isActive: false,
},
{
provider: null,
refreshToken: "missing-provider",
isActive: true,
},
],
},
log
);
assert.equal(fetchCount, 1);
assert.deepEqual(tokens, {
"custom-oauth-task-207": {
accessToken: "access-active-one",
refreshToken: "next-active-one",
expiresIn: 900,
},
});
}
);
}
);
});
test("refreshWithRetry retries to success and clears prior circuit-breaker state", async () => {
const provider = `retry-success-${Date.now()}`;
const log = createLog();
await refreshWithRetry(async () => null, 1, log, provider);
assert.equal(getCircuitBreakerStatus()[provider].failures, 1);
await withFastRetryTimers(async () => {
let attempts = 0;
const result = await refreshWithRetry(
async () => {
attempts += 1;
return attempts === 2 ? { accessToken: "recovered" } : null;
},
3,
log,
provider
);
assert.deepEqual(result, { accessToken: "recovered" });
assert.equal(attempts, 2);
assert.equal(getCircuitBreakerStatus()[provider], undefined);
});
});
test("refreshWithRetry trips the circuit breaker after repeated failures and blocks new calls", async () => {
const provider = `retry-blocked-${Date.now()}`;
const log = createLog();
for (let attempt = 0; attempt < 5; attempt += 1) {
const result = await refreshWithRetry(async () => null, 1, log, provider);
assert.equal(result, null);
}
assert.equal(isProviderBlocked(provider), true);
assert.equal(getCircuitBreakerStatus()[provider].blocked, true);
let called = false;
const blockedResult = await refreshWithRetry(
async () => {
called = true;
return { accessToken: "should-not-run" };
},
1,
log,
provider
);
assert.equal(blockedResult, null);
assert.equal(called, false);
});
test("isProviderBlocked clears expired circuit-breaker entries once cooldown passes", async () => {
const provider = `retry-expiry-${Date.now()}`;
const log = createLog();
for (let attempt = 0; attempt < 5; attempt += 1) {
await refreshWithRetry(async () => null, 1, log, provider);
}
const blockedUntil = Date.parse(getCircuitBreakerStatus()[provider].blockedUntil as string);
await withMockedNow(blockedUntil + 1, async () => {
assert.equal(isProviderBlocked(provider), false);
assert.equal(getCircuitBreakerStatus()[provider], undefined);
});
});
// ─── Per-connection mutex tests ────────────────────────────────────────────────
test("getAccessToken per-connection mutex: 5 concurrent callers fire exactly one upstream call", async () => {
const log = createLog();
let upstreamCallCount = 0;
await withPatchedProperties(
PROVIDERS,
{ "custom-oauth-conn-mutex": { tokenUrl: "https://auth.example.com/token" } },
async () => {
await withMockedFetch(
async () => {
upstreamCallCount++;
// Simulate 50ms upstream latency so all 5 callers are concurrent
await new Promise((r) => setTimeout(r, 50));
return jsonResponse({
access_token: "new-access-token",
refresh_token: "new-refresh-token",
expires_in: 3600,
});
},
async () => {
const credentials = {
connectionId: "conn-abc-123",
refreshToken: "old-refresh-token",
};
const results = await Promise.all([
getAccessToken("custom-oauth-conn-mutex", { ...credentials }, log),
getAccessToken("custom-oauth-conn-mutex", { ...credentials }, log),
getAccessToken("custom-oauth-conn-mutex", { ...credentials }, log),
getAccessToken("custom-oauth-conn-mutex", { ...credentials }, log),
getAccessToken("custom-oauth-conn-mutex", { ...credentials }, log),
]);
assert.equal(upstreamCallCount, 1, "upstream called exactly once");
for (const result of results) {
assert.equal(result?.accessToken, "new-access-token", "all callers got same token");
assert.equal(result?.refreshToken, "new-refresh-token");
}
// All results are the same object reference (shared promise)
assert.strictEqual(results[0], results[1]);
assert.strictEqual(results[1], results[4]);
}
);
}
);
});
test("getAccessToken per-connection mutex: logs concurrent refresh with waiter count", async () => {
const log = createLog();
await withPatchedProperties(
PROVIDERS,
{ "custom-oauth-conn-mutex": { tokenUrl: "https://auth.example.com/token" } },
async () => {
await withMockedFetch(
async () => {
await new Promise((r) => setTimeout(r, 20));
return jsonResponse({ access_token: "tok", refresh_token: "rtok", expires_in: 600 });
},
async () => {
const credentials = { connectionId: "conn-log-test", refreshToken: "rt" };
await Promise.all([
getAccessToken("custom-oauth-conn-mutex", { ...credentials }, log),
getAccessToken("custom-oauth-conn-mutex", { ...credentials }, log),
getAccessToken("custom-oauth-conn-mutex", { ...credentials }, log),
]);
const concurrentLogs = log.entries.filter(
(e) =>
e.level === "info" &&
e.message === "Concurrent refresh detected — sharing in-flight refresh"
);
assert.ok(concurrentLogs.length >= 1, "logged at least one concurrent refresh event");
assert.ok(
concurrentLogs.some((e) => e.meta?.connectionId === "conn-log-test"),
"log includes connectionId"
);
assert.ok(
concurrentLogs.some((e) => typeof e.meta?.waiters === "number" && e.meta.waiters >= 1),
"log includes waiter count"
);
}
);
}
);
});
test("getAccessToken per-connection mutex: failed refresh propagates null to all waiters (idempotent error)", async () => {
const log = createLog();
await withPatchedProperties(
PROVIDERS,
{ "custom-oauth-conn-mutex": { tokenUrl: "https://auth.example.com/token" } },
async () => {
await withMockedFetch(
async () => {
await new Promise((r) => setTimeout(r, 20));
// 400 response causes refreshAccessToken to return null
return new Response("bad_request", { status: 400 });
},
async () => {
const credentials = { connectionId: "conn-fail-test", refreshToken: "expired-rt" };
const results = await Promise.all([
getAccessToken("custom-oauth-conn-mutex", { ...credentials }, log),
getAccessToken("custom-oauth-conn-mutex", { ...credentials }, log),
getAccessToken("custom-oauth-conn-mutex", { ...credentials }, log),
]);
for (const result of results) {
assert.equal(result, null, "failed refresh returns null to all waiters");
}
// Mutex cleaned up after failure
assert.equal(
getConnectionRefreshMutexStatus()["conn-fail-test"],
undefined,
"mutex entry removed after failure"
);
}
);
}
);
});
test("getAccessToken per-connection mutex: different connections run independently", async () => {
const log = createLog();
let upstreamCallCount = 0;
await withPatchedProperties(
PROVIDERS,
{ "custom-oauth-conn-mutex": { tokenUrl: "https://auth.example.com/token" } },
async () => {
await withMockedFetch(
async () => {
upstreamCallCount++;
await new Promise((r) => setTimeout(r, 20));
return jsonResponse({
access_token: `access-${upstreamCallCount}`,
refresh_token: `refresh-${upstreamCallCount}`,
expires_in: 600,
});
},
async () => {
const [groupA, groupB] = await Promise.all([
Promise.all([
getAccessToken(
"custom-oauth-conn-mutex",
{ connectionId: "conn-A", refreshToken: "rt-a" },
log
),
getAccessToken(
"custom-oauth-conn-mutex",
{ connectionId: "conn-A", refreshToken: "rt-a" },
log
),
]),
Promise.all([
getAccessToken(
"custom-oauth-conn-mutex",
{ connectionId: "conn-B", refreshToken: "rt-b" },
log
),
getAccessToken(
"custom-oauth-conn-mutex",
{ connectionId: "conn-B", refreshToken: "rt-b" },
log
),
]),
]);
assert.equal(upstreamCallCount, 2, "one upstream call per distinct connection");
assert.strictEqual(groupA[0], groupA[1], "conn-A callers share same result");
assert.strictEqual(groupB[0], groupB[1], "conn-B callers share same result");
assert.notStrictEqual(groupA[0], groupB[0], "conn-A and conn-B got different results");
}
);
}
);
});
test("getAccessToken per-connection mutex: mutex cleared after success, next call re-fires upstream", async () => {
const log = createLog();
let upstreamCallCount = 0;
// The rotation map (added for the codex-multi-auth pattern) is process-wide
// and intentionally redirects a stale-token caller to the cached rotated
// tokens. Clear it BEFORE and BETWEEN calls so this test exercises the
// lower-level mutex semantics it was designed for.
tokenRefresh._clearTokenRotationMap();
await withPatchedProperties(
PROVIDERS,
{ "custom-oauth-conn-mutex": { tokenUrl: "https://auth.example.com/token" } },
async () => {
await withMockedFetch(
async () => {
upstreamCallCount++;
return jsonResponse({
access_token: `access-${upstreamCallCount}`,
refresh_token: `refresh-${upstreamCallCount}`,
expires_in: 600,
});
},
async () => {
const credentials = { connectionId: "conn-refire", refreshToken: "rt" };
const first = await getAccessToken("custom-oauth-conn-mutex", { ...credentials }, log);
tokenRefresh._clearTokenRotationMap();
const second = await getAccessToken("custom-oauth-conn-mutex", { ...credentials }, log);
assert.equal(upstreamCallCount, 2, "each sequential call fires upstream once");
assert.equal(first?.accessToken, "access-1");
assert.equal(second?.accessToken, "access-2");
}
);
}
);
});
// ─── Unrecoverable error bail-out tests ──────────────────────────────────────
test("refreshWithRetry bails immediately on unrecoverable error without retrying", async () => {
const provider = `bail-unrecoverable-${Date.now()}`;
const log = createLog();
let callCount = 0;
const result = await refreshWithRetry(
async () => {
callCount++;
return { error: "unrecoverable_refresh_error", code: "http_400" };
},
3,
log,
provider
);
assert.equal(callCount, 1, "should only call refreshFn once (no retries)");
assert.deepEqual(result, { error: "unrecoverable_refresh_error", code: "http_400" });
const warnMessages = log.entries.filter((e) => e.level === "warn").map((e) => e.message);
assert.ok(
warnMessages.some((m) => String(m).includes("Unrecoverable")),
"should log an unrecoverable warning"
);
});
test("refreshWithRetry bails immediately on invalid_grant error without retrying", async () => {
const provider = `bail-invalid-grant-${Date.now()}`;
const log = createLog();
let callCount = 0;
const result = await refreshWithRetry(
async () => {
callCount++;
return { error: "invalid_grant", code: "http_400" };
},
3,
log,
provider
);
assert.equal(callCount, 1, "should only call refreshFn once (no retries)");
assert.deepEqual(result, { error: "invalid_grant", code: "http_400" });
});
test("refreshClaudeOAuthToken returns error object for invalid_grant (expired refresh token)", async () => {
const log = createLog();
await withMockedFetch(
async () =>
new Response(JSON.stringify({ error: "invalid_grant", error_description: "Token expired" }), {
status: 400,
headers: { "Content-Type": "application/json" },
}),
async () => {
const result = await refreshClaudeOAuthToken("expired-token", log);
assert.ok(result && typeof result === "object", "should return error object, not null");
// Normalized to unrecoverable_refresh_error sentinel (Fix 6)
assert.equal((result as any).error, "unrecoverable_refresh_error");
assert.equal((result as any).code, "invalid_grant");
assert.ok(isUnrecoverableRefreshError(result), "should be detected as unrecoverable");
}
);
});
test("refreshClaudeOAuthToken returns null for transient server errors (not unrecoverable)", async () => {
const log = createLog();
await withMockedFetch(
async () =>
new Response(JSON.stringify({ error: "server_error" }), {
status: 503,
headers: { "Content-Type": "application/json" },
}),
async () => {
const result = await refreshClaudeOAuthToken("some-token", log);
assert.equal(result, null, "transient server errors should return null (retryable)");
}
);
});