Optionally forward identity headers

This commit is contained in:
Owen
2026-10-20 09:50:02 -04:00
parent f363c07afa
commit 6aaaae3819
2 changed files with 67 additions and 6 deletions
+37 -6
View File
@@ -857,7 +857,10 @@ export async function verifyResourceSession(
return allowed( return allowed(
res, res,
whitelistEmail ? { email: whitelistEmail } : undefined, forwardedUserData(
resource,
whitelistEmail ? { email: whitelistEmail } : undefined
),
dontStripSession dontStripSession
); );
} }
@@ -899,7 +902,11 @@ export async function verifyResourceSession(
parsedBody.data parsedBody.data
); );
return allowed(res, userData, dontStripSession); return allowed(
res,
forwardedUserData(resource, userData),
dontStripSession
);
} }
logger.debug( logger.debug(
@@ -951,7 +958,10 @@ export async function verifyResourceSession(
return allowed( return allowed(
res, res,
{ ...allowedUserData, dontStripSession }, forwardedUserData(resource, {
...allowedUserData,
dontStripSession
}),
dontStripSession dontStripSession
); );
} }
@@ -1158,6 +1168,19 @@ async function notAllowed(
return response<VerifyUserResponse>(res, data); return response<VerifyUserResponse>(res, data);
} }
// Badger only sets the Remote-* identity headers for fields present in the
// response, so omitting the user data is enough to keep them off the upstream
// request. Some backends (e.g. embedded device web servers) reject requests
// carrying headers they don't recognize. Inference resources skip this since
// the AI gateway relies on those headers for attribution.
function forwardedUserData<T>(
resource: Resource,
userData: T | undefined
): T | undefined {
// Treat a missing value (older hybrid payloads) as enabled
return resource.forwardIdentityHeaders === false ? undefined : userData;
}
function allowed( function allowed(
res: Response, res: Response,
userData?: BasicUserData | EmailOnlyUserData, userData?: BasicUserData | EmailOnlyUserData,
@@ -1209,7 +1232,11 @@ async function allowAccessToken(
if (!tokenItem.persistSession) { if (!tokenItem.persistSession) {
logAccessTokenAccessAudit(tokenItem, resource, userData, auditBody); logAccessTokenAccessAudit(tokenItem, resource, userData, auditBody);
return allowed(res, userData, dontStripSession); return allowed(
res,
forwardedUserData(resource, userData),
dontStripSession
);
} }
const resourceSessionToken = extractResourceSessionToken( const resourceSessionToken = extractResourceSessionToken(
@@ -1241,7 +1268,11 @@ async function allowAccessToken(
logger.debug( logger.debug(
"Resource allowed because existing access token session is valid" "Resource allowed because existing access token session is valid"
); );
return allowed(res, userData, dontStripSession); return allowed(
res,
forwardedUserData(resource, userData),
dontStripSession
);
} }
} }
@@ -1274,7 +1305,7 @@ async function createAccessTokenSession(
); );
res.appendHeader("Set-Cookie", cookie); res.appendHeader("Set-Cookie", cookie);
logger.debug("Access token is valid, creating new session"); logger.debug("Access token is valid, creating new session");
return allowed(res, userData); return allowed(res, forwardedUserData(resource, userData));
} }
async function getAccessTokenUserData( async function getAccessTokenUserData(
@@ -127,6 +127,7 @@ function ProxyResourceHttpForm({
message: t("proxyErrorInvalidHeader") message: t("proxyErrorInvalidHeader")
} }
), ),
forwardIdentityHeaders: z.boolean(),
requestHeaders: z requestHeaders: z
.array(z.object({ name: z.string(), value: z.string() })) .array(z.object({ name: z.string(), value: z.string() }))
.nullable(), .nullable(),
@@ -143,6 +144,7 @@ function ProxyResourceHttpForm({
ssl: resource.ssl, ssl: resource.ssl,
tlsServerName: resource.tlsServerName || "", tlsServerName: resource.tlsServerName || "",
setHostHeader: resource.setHostHeader || "", setHostHeader: resource.setHostHeader || "",
forwardIdentityHeaders: resource.forwardIdentityHeaders,
requestHeaders: resource.requestHeaders, requestHeaders: resource.requestHeaders,
responseHeaders: resource.responseHeaders responseHeaders: resource.responseHeaders
}, },
@@ -184,6 +186,7 @@ function ProxyResourceHttpForm({
ssl: data.ssl, ssl: data.ssl,
tlsServerName: data.tlsServerName || null, tlsServerName: data.tlsServerName || null,
setHostHeader: data.setHostHeader || null, setHostHeader: data.setHostHeader || null,
forwardIdentityHeaders: data.forwardIdentityHeaders,
requestHeaders: data.requestHeaders || null, requestHeaders: data.requestHeaders || null,
responseHeaders: data.responseHeaders || null responseHeaders: data.responseHeaders || null
} }
@@ -207,6 +210,7 @@ function ProxyResourceHttpForm({
ssl: data.ssl, ssl: data.ssl,
tlsServerName: data.tlsServerName || null, tlsServerName: data.tlsServerName || null,
setHostHeader: data.setHostHeader || null, setHostHeader: data.setHostHeader || null,
forwardIdentityHeaders: data.forwardIdentityHeaders,
requestHeaders: data.requestHeaders || null, requestHeaders: data.requestHeaders || null,
responseHeaders: data.responseHeaders || null responseHeaders: data.responseHeaders || null
}); });
@@ -380,6 +384,32 @@ function ProxyResourceHttpForm({
/> />
</SettingsFormCell> </SettingsFormCell>
<SettingsFormCell span="full">
<FormField
control={form.control}
name="forwardIdentityHeaders"
render={({ field }) => (
<FormItem>
<FormControl>
<SwitchInput
id="forward-identity-headers-toggle"
label={t(
"proxyForwardIdentityHeaders"
)}
description={t(
"proxyForwardIdentityHeadersDescription"
)}
checked={field.value}
onCheckedChange={
field.onChange
}
/>
</FormControl>
</FormItem>
)}
/>
</SettingsFormCell>
<SettingsFormCell span="full"> <SettingsFormCell span="full">
<FormField <FormField
control={form.control} control={form.control}