mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-14 03:02:14 +03:00
* fix(api): enforce image generation API key auth * fix(api): align image route auth guard with clientApiPolicy The route-level guard added for image generation was stricter than the authz middleware that already fronts /api/v1/* (src/proxy.ts → clientApiPolicy), so requests the pipeline admits were 401'd by the handler: - A cookie-authenticated dashboard session was rejected under REQUIRE_API_KEY=true. The dashboard Media page (dashboard/cache/media) and the Playground call these routes with a session and no Bearer — the same mismatch already fixed for /api/playground/presets. - A presented invalid key was rejected even with REQUIRE_API_KEY=false, where clientApiPolicy (#2257) and the sibling /v1/embeddings and /v1/web/fetch routes degrade a stale CLI key to anonymous instead. Extract the shared guard into shared/utils/clientApiRouteAuth so both image routes (and future /v1 handlers) mirror the middleware contract instead of re-deriving it, and drop the now-dead auth imports. Also switch the call-log attribution fallback back to `||`: with `??`, an empty-string apiKeyId/apiKeyName would be persisted verbatim and would block the request-scoped context, which the previous `entry.apiKeyId || null` never did. Tests: cover the dashboard-session and keyless-mode-invalid-key branches, and split the auth/attribution cases into image-generation-route-auth.test.ts to stay under the 800-line new-test-file cap. Co-authored-by: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: alexey.nazarov@softmg.ru <alexey.nazarov@softmg.ru> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
51 lines
2.0 KiB
TypeScript
51 lines
2.0 KiB
TypeScript
/**
|
|
* Route-level authentication guard for CLIENT_API (`/api/v1/*`) handlers.
|
|
*
|
|
* Defence-in-depth companion to `clientApiPolicy`
|
|
* (src/server/authz/policies/clientApi.ts), which already fronts these routes
|
|
* through `src/proxy.ts`. The handler-level check must never be *stricter*
|
|
* than the middleware, otherwise callers the pipeline admits get a 401 from
|
|
* the route instead:
|
|
*
|
|
* - a presented key must be valid, but is only rejected while
|
|
* `REQUIRE_API_KEY=true`; with enforcement off a stale CLI key degrades to
|
|
* anonymous instead of failing the whole request (#2257);
|
|
* - a cookie-authenticated dashboard session is accepted in place of a key —
|
|
* the dashboard Media page and Playground call these routes with a session
|
|
* only (same fix as the preset auth mismatch in
|
|
* `src/app/api/playground/presets/route.ts`);
|
|
* - anonymous traffic stays allowed while `REQUIRE_API_KEY=false`.
|
|
*
|
|
* @module shared/utils/clientApiRouteAuth
|
|
*/
|
|
|
|
import { errorResponse } from "@omniroute/open-sse/utils/error.ts";
|
|
import { HTTP_STATUS } from "@omniroute/open-sse/config/constants.ts";
|
|
import { extractApiKey, isValidApiKey } from "@/sse/services/auth";
|
|
import { isRequireApiKeyEnabled } from "@/shared/utils/featureFlags";
|
|
import { isDashboardSessionAuthenticated } from "@/shared/utils/apiAuth";
|
|
|
|
/**
|
|
* Authenticate a client-API request at the route level.
|
|
*
|
|
* @param request - The incoming request.
|
|
* @returns A 401 `Response` the handler must return, or `null` when the
|
|
* request may proceed to policy enforcement.
|
|
*/
|
|
export async function enforceClientApiRouteAuth(request: Request): Promise<Response | null> {
|
|
const apiKeyRaw = extractApiKey(request);
|
|
|
|
if (apiKeyRaw) {
|
|
if (await isValidApiKey(apiKeyRaw)) return null;
|
|
return isRequireApiKeyEnabled()
|
|
? errorResponse(HTTP_STATUS.UNAUTHORIZED, "Invalid API key")
|
|
: null;
|
|
}
|
|
|
|
if (await isDashboardSessionAuthenticated(request)) return null;
|
|
|
|
return isRequireApiKeyEnabled()
|
|
? errorResponse(HTTP_STATUS.UNAUTHORIZED, "Authentication required")
|
|
: null;
|
|
}
|