mirror of
https://github.com/better-auth/better-auth.git
synced 2026-05-25 00:22:43 -05:00
feat: add support for trusted proxy headers in base URL inference (#6285)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -1,6 +1,11 @@
|
||||
import { createAuthEndpoint } from "@better-auth/core/api";
|
||||
import {
|
||||
createAuthEndpoint,
|
||||
createAuthMiddleware,
|
||||
} from "@better-auth/core/api";
|
||||
import type { router } from "better-auth/api";
|
||||
import { describe, expectTypeOf, test } from "vitest";
|
||||
import { describe, expect, expectTypeOf, test } from "vitest";
|
||||
import { createAuthClient } from "../client";
|
||||
import { getTestInstance } from "../test-utils";
|
||||
import type { Auth } from "../types";
|
||||
import { betterAuth } from "./auth";
|
||||
|
||||
@@ -62,3 +67,60 @@ describe("auth type", () => {
|
||||
expectTypeOf<R>().toEqualTypeOf<{ data: { message: string } }>();
|
||||
});
|
||||
});
|
||||
|
||||
describe("auth with trusted proxy headers", () => {
|
||||
test("shouldn't infer base url from proxy headers if trusted", async () => {
|
||||
let baseURL: string | undefined;
|
||||
const { auth, customFetchImpl } = await getTestInstance({
|
||||
baseURL: undefined,
|
||||
advanced: {
|
||||
trustedProxyHeaders: true,
|
||||
},
|
||||
hooks: {
|
||||
before: createAuthMiddleware(async (ctx) => {
|
||||
baseURL = ctx.context.baseURL;
|
||||
}),
|
||||
},
|
||||
});
|
||||
const client = createAuthClient({
|
||||
fetchOptions: {
|
||||
customFetchImpl,
|
||||
},
|
||||
baseURL: "http://localhost:3000",
|
||||
});
|
||||
const res = await client.$fetch("/ok", {
|
||||
headers: {
|
||||
"x-forwarded-host": "localhost:3001",
|
||||
"x-forwarded-proto": "http",
|
||||
},
|
||||
});
|
||||
expect(baseURL).toBe("http://localhost:3001/api/auth");
|
||||
});
|
||||
test("shouldn't infer base url from proxy headers if not trusted", async () => {
|
||||
let baseURL: string | undefined;
|
||||
const { customFetchImpl } = await getTestInstance({
|
||||
baseURL: undefined,
|
||||
advanced: {
|
||||
trustedProxyHeaders: false,
|
||||
},
|
||||
hooks: {
|
||||
before: createAuthMiddleware(async (ctx) => {
|
||||
baseURL = ctx.context.baseURL;
|
||||
}),
|
||||
},
|
||||
});
|
||||
const client = createAuthClient({
|
||||
fetchOptions: {
|
||||
customFetchImpl,
|
||||
},
|
||||
baseURL: "http://localhost:3000",
|
||||
});
|
||||
const res = await client.$fetch("/ok", {
|
||||
headers: {
|
||||
"x-forwarded-host": "localhost:3001",
|
||||
"x-forwarded-proto": "http",
|
||||
},
|
||||
});
|
||||
expect(baseURL).toBe("http://localhost:3000/api/auth");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -30,7 +30,13 @@ export const createBetterAuth = <Options extends BetterAuthOptions>(
|
||||
const ctx = await authContext;
|
||||
const basePath = ctx.options.basePath || "/api/auth";
|
||||
if (!ctx.options.baseURL) {
|
||||
const baseURL = getBaseURL(undefined, basePath, request);
|
||||
const baseURL = getBaseURL(
|
||||
undefined,
|
||||
basePath,
|
||||
request,
|
||||
undefined,
|
||||
ctx.options.advanced?.trustedProxyHeaders,
|
||||
);
|
||||
if (baseURL) {
|
||||
ctx.baseURL = baseURL;
|
||||
ctx.options.baseURL = getOrigin(ctx.baseURL) || undefined;
|
||||
@@ -40,6 +46,7 @@ export const createBetterAuth = <Options extends BetterAuthOptions>(
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ctx.trustedOrigins = [
|
||||
...(options.trustedOrigins
|
||||
? Array.isArray(options.trustedOrigins)
|
||||
|
||||
@@ -55,6 +55,7 @@ export function getBaseURL(
|
||||
path?: string,
|
||||
request?: Request,
|
||||
loadEnv?: boolean,
|
||||
trustedProxyHeaders?: boolean | undefined,
|
||||
) {
|
||||
if (url) {
|
||||
return withPath(url, path);
|
||||
@@ -76,7 +77,7 @@ export function getBaseURL(
|
||||
|
||||
const fromRequest = request?.headers.get("x-forwarded-host");
|
||||
const fromRequestProto = request?.headers.get("x-forwarded-proto");
|
||||
if (fromRequest && fromRequestProto) {
|
||||
if (fromRequest && fromRequestProto && trustedProxyHeaders) {
|
||||
return withPath(`${fromRequestProto}://${fromRequest}`, path);
|
||||
}
|
||||
|
||||
|
||||
@@ -257,6 +257,20 @@ export type BetterAuthAdvancedOptions = {
|
||||
generateId?: GenerateIdFn | false | "serial" | "uuid";
|
||||
}
|
||||
| undefined;
|
||||
/**
|
||||
* Trusted proxy headers
|
||||
*
|
||||
|
||||
* - `x-forwarded-host`
|
||||
* - `x-forwarded-proto`
|
||||
*
|
||||
* If set to `true` and no `baseURL` option is provided, we will use the headers to infer the
|
||||
* base URL.
|
||||
*
|
||||
* ⚠︎ This may expose your application to security vulnerabilities if not
|
||||
* used correctly. Please use this with caution.
|
||||
*/
|
||||
trustedProxyHeaders?: boolean | undefined;
|
||||
};
|
||||
|
||||
export type BetterAuthOptions = {
|
||||
|
||||
Reference in New Issue
Block a user