fix(auth): drop invalid auth profiles from order

This commit is contained in:
Peter Steinberger
2026-01-13 07:42:41 +00:00
parent 2887376646
commit 9007920695
2 changed files with 159 additions and 2 deletions

View File

@@ -59,6 +59,134 @@ describe("resolveAuthProfileOrder", () => {
expect(order).toContain("anthropic:default"); expect(order).toContain("anthropic:default");
}); });
it("drops explicit order entries that are missing from the store", () => {
const order = resolveAuthProfileOrder({
cfg: {
auth: {
order: {
minimax: ["minimax:default", "minimax:prod"],
},
},
},
store: {
version: 1,
profiles: {
"minimax:prod": {
type: "api_key",
provider: "minimax",
key: "sk-prod",
},
},
},
provider: "minimax",
});
expect(order).toEqual(["minimax:prod"]);
});
it("drops explicit order entries that belong to another provider", () => {
const order = resolveAuthProfileOrder({
cfg: {
auth: {
order: {
minimax: ["openai:default", "minimax:prod"],
},
},
},
store: {
version: 1,
profiles: {
"openai:default": {
type: "api_key",
provider: "openai",
key: "sk-openai",
},
"minimax:prod": {
type: "api_key",
provider: "minimax",
key: "sk-mini",
},
},
},
provider: "minimax",
});
expect(order).toEqual(["minimax:prod"]);
});
it("drops token profiles with empty credentials", () => {
const order = resolveAuthProfileOrder({
cfg: {
auth: {
order: {
minimax: ["minimax:default"],
},
},
},
store: {
version: 1,
profiles: {
"minimax:default": {
type: "token",
provider: "minimax",
token: " ",
},
},
},
provider: "minimax",
});
expect(order).toEqual([]);
});
it("drops token profiles that are already expired", () => {
const order = resolveAuthProfileOrder({
cfg: {
auth: {
order: {
minimax: ["minimax:default"],
},
},
},
store: {
version: 1,
profiles: {
"minimax:default": {
type: "token",
provider: "minimax",
token: "sk-minimax",
expires: Date.now() - 1000,
},
},
},
provider: "minimax",
});
expect(order).toEqual([]);
});
it("keeps oauth profiles that can refresh", () => {
const order = resolveAuthProfileOrder({
cfg: {
auth: {
order: {
anthropic: ["anthropic:oauth"],
},
},
},
store: {
version: 1,
profiles: {
"anthropic:oauth": {
type: "oauth",
provider: "anthropic",
access: "",
refresh: "refresh-token",
expires: Date.now() - 1000,
},
},
},
provider: "anthropic",
});
expect(order).toEqual(["anthropic:oauth"]);
});
it("does not prioritize lastGood over round-robin ordering", () => { it("does not prioritize lastGood over round-robin ordering", () => {
const order = resolveAuthProfileOrder({ const order = resolveAuthProfileOrder({
cfg, cfg,

View File

@@ -1042,6 +1042,7 @@ export function resolveAuthProfileOrder(params: {
}): string[] { }): string[] {
const { cfg, store, provider, preferredProfile } = params; const { cfg, store, provider, preferredProfile } = params;
const providerKey = normalizeProviderId(provider); const providerKey = normalizeProviderId(provider);
const now = Date.now();
const storedOrder = (() => { const storedOrder = (() => {
const order = store.order; const order = store.order;
if (!order) return undefined; if (!order) return undefined;
@@ -1076,7 +1077,36 @@ export function resolveAuthProfileOrder(params: {
const filtered = baseOrder.filter((profileId) => { const filtered = baseOrder.filter((profileId) => {
const cred = store.profiles[profileId]; const cred = store.profiles[profileId];
return cred ? normalizeProviderId(cred.provider) === providerKey : true; if (!cred) return false;
if (normalizeProviderId(cred.provider) !== providerKey) return false;
const profileConfig = cfg?.auth?.profiles?.[profileId];
if (profileConfig) {
if (normalizeProviderId(profileConfig.provider) !== providerKey) {
return false;
}
if (profileConfig.mode !== cred.type) {
const oauthCompatible =
profileConfig.mode === "oauth" && cred.type === "token";
if (!oauthCompatible) return false;
}
}
if (cred.type === "api_key") return Boolean(cred.key?.trim());
if (cred.type === "token") {
if (!cred.token?.trim()) return false;
if (
typeof cred.expires === "number" &&
Number.isFinite(cred.expires) &&
cred.expires > 0 &&
now >= cred.expires
) {
return false;
}
return true;
}
if (cred.type === "oauth") {
return Boolean(cred.access?.trim() || cred.refresh?.trim());
}
return false;
}); });
const deduped: string[] = []; const deduped: string[] = [];
for (const entry of filtered) { for (const entry of filtered) {
@@ -1089,7 +1119,6 @@ export function resolveAuthProfileOrder(params: {
if (explicitOrder && explicitOrder.length > 0) { if (explicitOrder && explicitOrder.length > 0) {
// ...but still respect cooldown tracking to avoid repeatedly selecting a // ...but still respect cooldown tracking to avoid repeatedly selecting a
// known-bad/rate-limited key as the first candidate. // known-bad/rate-limited key as the first candidate.
const now = Date.now();
const available: string[] = []; const available: string[] = [];
const inCooldown: Array<{ profileId: string; cooldownUntil: number }> = []; const inCooldown: Array<{ profileId: string; cooldownUntil: number }> = [];