fix(auth): drop invalid auth profiles from order
This commit is contained in:
@@ -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,
|
||||||
|
|||||||
@@ -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 }> = [];
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user