mirror of
https://github.com/better-auth/better-auth.git
synced 2026-05-25 00:22:43 -05:00
fix(auth): respect trustedOrigins when baseURL is inferred (#6882)
This commit is contained in:
committed by
GitHub
parent
e9cd882f1f
commit
19d2b3a990
@@ -27,7 +27,11 @@ export const auth = betterAuth({
|
||||
})
|
||||
```
|
||||
|
||||
If not explicitly set, the system will check for the environment variable `process.env.BETTER_AUTH_URL`
|
||||
If not explicitly set, the system will check for the environment variable `BETTER_AUTH_URL`. If that's also not set, it will be inferred from the incoming request.
|
||||
|
||||
<Callout type="warn">
|
||||
Relying on request inference is not recommended. For security and stability, always set `baseURL` explicitly in your config or via the `BETTER_AUTH_URL` environment variable.
|
||||
</Callout>
|
||||
|
||||
## `basePath`
|
||||
|
||||
|
||||
@@ -247,8 +247,8 @@ describe("origin check middleware", async (it) => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("trustedOrigins regression tests", async (it) => {
|
||||
it("should respect trustedOrigins from config array through full request flow", async () => {
|
||||
describe("trusted origins with baseURL inferred from request", async (it) => {
|
||||
it("should respect trustedOrigins array when baseURL is NOT in config", async () => {
|
||||
const { customFetchImpl, testUser } = await getTestInstance({
|
||||
trustedOrigins: ["http://my-frontend.com"],
|
||||
emailAndPassword: {
|
||||
@@ -280,7 +280,7 @@ describe("trustedOrigins regression tests", async (it) => {
|
||||
expect(res.data?.user).toBeDefined();
|
||||
});
|
||||
|
||||
it("should reject origins not in trustedOrigins config", async () => {
|
||||
it("should reject untrusted origins even when baseURL is inferred", async () => {
|
||||
const { customFetchImpl, testUser } = await getTestInstance({
|
||||
trustedOrigins: ["http://my-frontend.com"],
|
||||
emailAndPassword: {
|
||||
@@ -311,7 +311,7 @@ describe("trustedOrigins regression tests", async (it) => {
|
||||
expect(res.error?.status).toBe(403);
|
||||
});
|
||||
|
||||
it("should respect BETTER_AUTH_TRUSTED_ORIGINS env variable through full request flow", async () => {
|
||||
it("should respect BETTER_AUTH_TRUSTED_ORIGINS env when baseURL is NOT in config", async () => {
|
||||
vi.stubEnv("BETTER_AUTH_TRUSTED_ORIGINS", "http://env-frontend.com");
|
||||
|
||||
try {
|
||||
@@ -347,4 +347,90 @@ describe("trustedOrigins regression tests", async (it) => {
|
||||
vi.unstubAllEnvs();
|
||||
}
|
||||
});
|
||||
|
||||
it("should allow requests from inferred baseURL origin", async () => {
|
||||
const { customFetchImpl, testUser } = await getTestInstance({
|
||||
emailAndPassword: {
|
||||
enabled: true,
|
||||
},
|
||||
advanced: {
|
||||
disableCSRFCheck: false,
|
||||
disableOriginCheck: false,
|
||||
},
|
||||
});
|
||||
|
||||
const client = createAuthClient({
|
||||
baseURL: "http://localhost:3000",
|
||||
fetchOptions: {
|
||||
customFetchImpl,
|
||||
headers: {
|
||||
origin: "http://localhost:3000",
|
||||
cookie: "session=test",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const res = await client.signIn.email({
|
||||
email: testUser.email,
|
||||
password: testUser.password,
|
||||
callbackURL: "http://localhost:3000/dashboard",
|
||||
});
|
||||
|
||||
expect(res.data?.user).toBeDefined();
|
||||
});
|
||||
|
||||
it("should support both config array and env var together when baseURL is inferred", async () => {
|
||||
vi.stubEnv("BETTER_AUTH_TRUSTED_ORIGINS", "http://env-origin.com");
|
||||
|
||||
try {
|
||||
const { customFetchImpl, testUser } = await getTestInstance({
|
||||
trustedOrigins: ["http://config-origin.com"],
|
||||
emailAndPassword: {
|
||||
enabled: true,
|
||||
},
|
||||
advanced: {
|
||||
disableCSRFCheck: false,
|
||||
disableOriginCheck: false,
|
||||
},
|
||||
});
|
||||
|
||||
const client = createAuthClient({
|
||||
baseURL: "http://localhost:3000",
|
||||
fetchOptions: {
|
||||
customFetchImpl,
|
||||
headers: {
|
||||
origin: "http://config-origin.com",
|
||||
cookie: "session=test",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const res1 = await client.signIn.email({
|
||||
email: testUser.email,
|
||||
password: testUser.password,
|
||||
callbackURL: "http://config-origin.com/dashboard",
|
||||
});
|
||||
expect(res1.data?.user).toBeDefined();
|
||||
|
||||
const client2 = createAuthClient({
|
||||
baseURL: "http://localhost:3000",
|
||||
fetchOptions: {
|
||||
customFetchImpl,
|
||||
headers: {
|
||||
origin: "http://env-origin.com",
|
||||
cookie: "session=test",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const res2 = await client2.signIn.email({
|
||||
email: testUser.email,
|
||||
password: testUser.password,
|
||||
callbackURL: "http://env-origin.com/dashboard",
|
||||
});
|
||||
expect(res2.data?.user).toBeDefined();
|
||||
} finally {
|
||||
vi.unstubAllEnvs();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { AuthContext, BetterAuthOptions } from "@better-auth/core";
|
||||
import { runWithAdapter } from "@better-auth/core/context";
|
||||
import { BASE_ERROR_CODES, BetterAuthError } from "@better-auth/core/error";
|
||||
import { getEndpoints, router } from "../api";
|
||||
import { getTrustedOrigins } from "../context/helpers";
|
||||
import type { Auth } from "../types";
|
||||
import { getBaseURL, getOrigin } from "../utils/url";
|
||||
|
||||
@@ -37,13 +38,13 @@ export const createBetterAuth = <Options extends BetterAuthOptions>(
|
||||
if (baseURL) {
|
||||
ctx.baseURL = baseURL;
|
||||
ctx.options.baseURL = getOrigin(ctx.baseURL) || undefined;
|
||||
ctx.trustedOrigins = getTrustedOrigins(ctx.options);
|
||||
} else {
|
||||
throw new BetterAuthError(
|
||||
"Could not get base URL from request. Please provide a valid base URL.",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof options.trustedOrigins === "function") {
|
||||
ctx.trustedOrigins = [
|
||||
...ctx.trustedOrigins,
|
||||
|
||||
Reference in New Issue
Block a user