fix(auth): drop invalid auth profiles from order
This commit is contained in:
@@ -59,6 +59,134 @@ describe("resolveAuthProfileOrder", () => {
|
||||
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", () => {
|
||||
const order = resolveAuthProfileOrder({
|
||||
cfg,
|
||||
|
||||
@@ -1042,6 +1042,7 @@ export function resolveAuthProfileOrder(params: {
|
||||
}): string[] {
|
||||
const { cfg, store, provider, preferredProfile } = params;
|
||||
const providerKey = normalizeProviderId(provider);
|
||||
const now = Date.now();
|
||||
const storedOrder = (() => {
|
||||
const order = store.order;
|
||||
if (!order) return undefined;
|
||||
@@ -1076,7 +1077,36 @@ export function resolveAuthProfileOrder(params: {
|
||||
|
||||
const filtered = baseOrder.filter((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[] = [];
|
||||
for (const entry of filtered) {
|
||||
@@ -1089,7 +1119,6 @@ export function resolveAuthProfileOrder(params: {
|
||||
if (explicitOrder && explicitOrder.length > 0) {
|
||||
// ...but still respect cooldown tracking to avoid repeatedly selecting a
|
||||
// known-bad/rate-limited key as the first candidate.
|
||||
const now = Date.now();
|
||||
const available: string[] = [];
|
||||
const inCooldown: Array<{ profileId: string; cooldownUntil: number }> = [];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user