mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-20 14:12:59 +03:00
Co-authored-by: Markus Hartung <mail@hartmark.se>
This commit is contained in:
committed by
GitHub
parent
b755dd5e74
commit
d6c4fec2ee
@@ -0,0 +1 @@
|
||||
- fix(providers): register a real Firefly auth probe under both the `firefly` alias and the `adobe-firefly` canonical id, and normalize the provider id before the generic web-cookie fallback, so a Firefly connection stops always reporting "Provider validation not supported" (#10522)
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
isOpenAICompatibleProvider,
|
||||
isSelfHostedChatProvider,
|
||||
providerAllowsOptionalApiKey,
|
||||
resolveProviderId,
|
||||
WEB_COOKIE_PROVIDERS,
|
||||
} from "@/shared/constants/providers";
|
||||
import { MODAL_DEFAULT_VALIDATION_MODEL_ID } from "@/shared/constants/modal";
|
||||
@@ -107,6 +108,7 @@ import {
|
||||
validateBytezProvider,
|
||||
} from "./validation/webCookie";
|
||||
import { validateAiHordeProvider } from "./validation/aihorde";
|
||||
import { validateAdobeFireflyProvider } from "./validation/adobeFirefly";
|
||||
import {
|
||||
validateV0VercelProvider,
|
||||
validateAuggieProvider,
|
||||
@@ -184,6 +186,11 @@ export async function validateProviderApiKey({ provider, apiKey, providerSpecifi
|
||||
devin: validateDevinCloudAgentProvider,
|
||||
auggie: validateAuggieProvider,
|
||||
aihorde: validateAiHordeProvider,
|
||||
// #10522: registered under both the canonical id and the short alias — Firefly
|
||||
// connections are commonly stored as "firefly" (same prefix as firefly/<model>
|
||||
// routing ids), not the canonical "adobe-firefly" WEB_COOKIE_PROVIDERS key.
|
||||
"adobe-firefly": validateAdobeFireflyProvider,
|
||||
firefly: validateAdobeFireflyProvider,
|
||||
qoder: validateQoderProvider,
|
||||
kiro: validateKiroProvider,
|
||||
"command-code": validateCommandCodeProvider,
|
||||
@@ -328,9 +335,14 @@ export async function validateProviderApiKey({ provider, apiKey, providerSpecifi
|
||||
// per-provider validator (grok-web, chatgpt-web, claude-web, …) are handled by
|
||||
// SPECIALTY_VALIDATORS first and must not be shadowed by this generic probe (issue: the
|
||||
// #4023 dispatch was placed too early and intercepted every web-cookie provider).
|
||||
if (WEB_COOKIE_PROVIDERS[provider]) {
|
||||
const canonicalProvider = resolveProviderId(provider);
|
||||
if (WEB_COOKIE_PROVIDERS[canonicalProvider]) {
|
||||
try {
|
||||
return await validateWebCookieProvider({ provider, apiKey, providerSpecificData });
|
||||
return await validateWebCookieProvider({
|
||||
provider: canonicalProvider,
|
||||
apiKey,
|
||||
providerSpecificData,
|
||||
});
|
||||
} catch (error: any) {
|
||||
return toValidationErrorResult(error);
|
||||
}
|
||||
|
||||
38
src/lib/providers/validation/adobeFirefly.ts
Normal file
38
src/lib/providers/validation/adobeFirefly.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Adobe Firefly key/cookie validation (#10522).
|
||||
*
|
||||
* validateProviderApiKey() previously had no SPECIALTY_VALIDATORS entry for
|
||||
* "firefly"/"adobe-firefly" and the generic WEB_COOKIE_PROVIDERS fallback used a raw,
|
||||
* unaliased lookup — so a Firefly connection always fell through to the "Provider
|
||||
* validation not supported" branch regardless of whether the pasted cookie/JWT was
|
||||
* valid. This reuses the already-working credits/balance probe
|
||||
* (getAdobeFireflyUsage, used today by the Limits/quota page) as a real auth check:
|
||||
* a successful balance fetch means the token is valid, a `{ message }` result means
|
||||
* it was rejected (expired/guest/invalid), and thrown transport errors are mapped
|
||||
* through the shared toValidationErrorResult() helper.
|
||||
*/
|
||||
import { getAdobeFireflyUsage } from "@omniroute/open-sse/services/usage/adobeFirefly.ts";
|
||||
import { toValidationErrorResult } from "./transport";
|
||||
|
||||
export async function validateAdobeFireflyProvider({
|
||||
apiKey,
|
||||
providerSpecificData,
|
||||
fetchImpl = fetch,
|
||||
}: {
|
||||
apiKey?: unknown;
|
||||
providerSpecificData?: Record<string, unknown> | null;
|
||||
fetchImpl?: typeof fetch;
|
||||
}) {
|
||||
try {
|
||||
const key = typeof apiKey === "string" ? apiKey : undefined;
|
||||
const result = await getAdobeFireflyUsage(key, undefined, providerSpecificData, fetchImpl);
|
||||
|
||||
if ("message" in result) {
|
||||
return { valid: false, error: result.message };
|
||||
}
|
||||
|
||||
return { valid: true, error: null };
|
||||
} catch (error) {
|
||||
return toValidationErrorResult(error);
|
||||
}
|
||||
}
|
||||
90
tests/unit/firefly-cookie-validation-10522.test.ts
Normal file
90
tests/unit/firefly-cookie-validation-10522.test.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
import { test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import { validateProviderApiKey } from "../../src/lib/providers/validation.ts";
|
||||
import { validateAdobeFireflyProvider } from "../../src/lib/providers/validation/adobeFirefly.ts";
|
||||
import { resolveProviderId } from "../../src/shared/constants/providers.ts";
|
||||
import { ADOBE_FIREFLY_CREDITS_BALANCE_URL } from "../../open-sse/services/adobeFireflyClient.ts";
|
||||
|
||||
// A well-formed Adobe IMS *user* access token (3-segment JWT, non-guest payload, long
|
||||
// enough to satisfy looksLikeAdobeJwt). Only used as a routing/shape fixture — never a
|
||||
// real credential.
|
||||
const userJwt =
|
||||
`eyJhbGciOiJSUzI1NiJ9.` +
|
||||
Buffer.from(
|
||||
JSON.stringify({
|
||||
user_id: "0EB@AdobeID",
|
||||
type: "access_token",
|
||||
client_id: "clio-playground-web",
|
||||
})
|
||||
).toString("base64url") +
|
||||
`.` +
|
||||
"x".repeat(60);
|
||||
|
||||
function jsonResponse(status: number, body: unknown) {
|
||||
return new Response(JSON.stringify(body), {
|
||||
status,
|
||||
headers: { "content-type": "application/json" },
|
||||
});
|
||||
}
|
||||
|
||||
test("#10522: resolveProviderId('firefly') stays stable (SPECIALTY_VALIDATORS dual-key registration depends on it)", () => {
|
||||
assert.equal(resolveProviderId("firefly"), "adobe-firefly");
|
||||
});
|
||||
|
||||
test("#10522: firefly alias dispatches to the Firefly validator, not the generic unsupported fallback", async () => {
|
||||
const result = await validateProviderApiKey({
|
||||
provider: "firefly",
|
||||
apiKey: "not-a-real-token",
|
||||
providerSpecificData: {},
|
||||
});
|
||||
assert.notEqual(result.unsupported, true);
|
||||
});
|
||||
|
||||
test("#10522: adobe-firefly canonical id dispatches to the Firefly validator, not the generic unsupported fallback", async () => {
|
||||
const result = await validateProviderApiKey({
|
||||
provider: "adobe-firefly",
|
||||
apiKey: "not-a-real-token",
|
||||
providerSpecificData: {},
|
||||
});
|
||||
assert.notEqual(result.unsupported, true);
|
||||
});
|
||||
|
||||
test("#10522: expired/invalid token reports valid:false with a real error message (not 'not supported')", async () => {
|
||||
const fetchImpl = async (url: string | URL) => {
|
||||
assert.equal(String(url), ADOBE_FIREFLY_CREDITS_BALANCE_URL);
|
||||
return jsonResponse(401, { error: "invalid_token" });
|
||||
};
|
||||
|
||||
const result = await validateAdobeFireflyProvider({
|
||||
apiKey: userJwt,
|
||||
providerSpecificData: {},
|
||||
fetchImpl: fetchImpl as typeof fetch,
|
||||
});
|
||||
|
||||
assert.equal(result.valid, false);
|
||||
assert.notEqual((result as { unsupported?: boolean }).unsupported, true);
|
||||
assert.ok(result.error && result.error.length > 0);
|
||||
});
|
||||
|
||||
test("#10522: a genuine credits balance payload reports valid:true", async () => {
|
||||
const fetchImpl = async (url: string | URL) => {
|
||||
assert.equal(String(url), ADOBE_FIREFLY_CREDITS_BALANCE_URL);
|
||||
return jsonResponse(200, {
|
||||
total: { quota: { total: 100, used: 10, available: 90 } },
|
||||
credits: {
|
||||
firefly_free_credit: { quota: { total: 50, used: 5, available: 45 } },
|
||||
firefly_plan_credit: { quota: { total: 50, used: 5, available: 45 } },
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const result = await validateAdobeFireflyProvider({
|
||||
apiKey: userJwt,
|
||||
providerSpecificData: {},
|
||||
fetchImpl: fetchImpl as typeof fetch,
|
||||
});
|
||||
|
||||
assert.equal(result.valid, true);
|
||||
assert.equal(result.error, null);
|
||||
});
|
||||
Reference in New Issue
Block a user