add no auth and passthrough auth

This commit is contained in:
miloschwartz
2026-08-05 15:39:08 -04:00
parent 2e9bd50172
commit bcf6b86b84
10 changed files with 323 additions and 205 deletions
+2
View File
@@ -1651,6 +1651,8 @@ export const aiProviders = pgTable("aiProviders", {
| "x-goog-api-key"
| "hec"
| "cf-aig-authorization"
| "none"
| "passthrough"
>()
.notNull(),
routingMode: varchar("routingMode")
+2
View File
@@ -1633,6 +1633,8 @@ export const aiProviders = sqliteTable("aiProviders", {
| "x-goog-api-key"
| "hec"
| "cf-aig-authorization"
| "none"
| "passthrough"
>()
.notNull(),
routingMode: text("routingMode")
+25 -5
View File
@@ -14,7 +14,9 @@ export const AI_PROVIDER_AUTH_TYPES = [
"x-api-key",
"x-goog-api-key",
"hec",
"cf-aig-authorization"
"cf-aig-authorization",
"none",
"passthrough"
] as const;
export type AiProviderAuthType = (typeof AI_PROVIDER_AUTH_TYPES)[number];
@@ -71,6 +73,10 @@ const CONFLICTING_AUTH_HEADERS = [
"cf-aig-authorization"
] as const;
export function authTypeRequiresApiKey(authType: AiProviderAuthType): boolean {
return authType !== "none" && authType !== "passthrough";
}
export function providerRequiresUpstreamUrl(
type: AiProviderType,
routingMode: AiProviderRoutingMode = "url"
@@ -116,20 +122,26 @@ export function resolveAiProviderCreateFields(input: {
const defaults = AI_PROVIDER_DEFAULTS[input.type];
return {
upstreamUrl: input.upstreamUrl ?? defaults.upstreamUrl,
authType: defaults.authType,
authType: input.authType ?? defaults.authType,
routingMode
};
}
/**
* Strip inbound client auth headers, then set the provider auth header
* for the given authType.
* Apply provider auth to upstream headers.
* - Injected modes: strip client auth headers, then set the provider key.
* - none: strip client auth headers, send no auth.
* - passthrough: leave client auth headers as-is.
*/
export function applyAiProviderAuthHeaders(
headers: Record<string, string>,
authType: AiProviderAuthType,
apiKey: string
apiKey: string | null
): void {
if (authType === "passthrough") {
return;
}
for (const name of CONFLICTING_AUTH_HEADERS) {
for (const key of Object.keys(headers)) {
if (key.toLowerCase() === name) {
@@ -138,6 +150,14 @@ export function applyAiProviderAuthHeaders(
}
}
if (authType === "none") {
return;
}
if (!apiKey) {
throw new Error(`API key required for authType ${authType}`);
}
switch (authType) {
case "bearer":
headers["Authorization"] = `Bearer ${apiKey}`;
+15 -10
View File
@@ -19,7 +19,8 @@ import config from "@server/lib/config";
import { decrypt } from "@server/lib/crypto";
import {
AiProviderAuthType,
applyAiProviderAuthHeaders
applyAiProviderAuthHeaders,
authTypeRequiresApiKey
} from "@server/lib/aiProviderDefaults";
import {
SESSION_COOKIE_NAME,
@@ -488,15 +489,6 @@ export async function chatCompletions(
const { provider } = selection;
if (!provider.apiKey) {
return res.status(HttpCode.INTERNAL_SERVER_ERROR).json({
error: { message: "AI provider has no API key configured" }
});
}
const secret = config.getRawConfig().server.secret!;
const apiKey = decrypt(provider.apiKey, secret);
const upstreamUrl = provider.upstreamUrl;
const authType = provider.authType as AiProviderAuthType;
@@ -508,6 +500,19 @@ export async function chatCompletions(
});
}
let apiKey: string | null = null;
if (authTypeRequiresApiKey(authType)) {
if (!provider.apiKey) {
return res.status(HttpCode.INTERNAL_SERVER_ERROR).json({
error: {
message: "AI provider has no API key configured"
}
});
}
const secret = config.getRawConfig().server.secret!;
apiKey = decrypt(provider.apiKey, secret);
}
const targetUrl = `${upstreamUrl.replace(/\/$/, "")}`;
// Drop hop-by-hop / proxy-only headers. Forwarding Host especially
-8
View File
@@ -52,12 +52,4 @@ export function refineProviderUpstreamFields(
path: ["upstreamUrl"]
});
}
if (data.type === "custom" && !data.authType) {
ctx.addIssue({
code: "custom",
message: "authType is required for custom providers",
path: ["authType"]
});
}
}