fix: secure authentication request identity

Replace Better Auth's default trust of a caller-controlled X-Forwarded-For
with a platform-owned client-address resolution boundary: direct mode (the
safe default) trusts only the real socket peer, and an explicit trusted-proxy
mode requires a header name, a concrete trusted-proxy list, and only honors a
forwarded chain from an actual trusted peer. This closes a rate-limit bypass
where rotating a spoofed header let a caller choose a fresh bucket on every
request. Public sign-up is also disabled by default and requires an explicit
typed opt-in, using Better Auth's own disableSignUp gate so no email-existence
leak or second signup path is introduced.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
ekklesia
2026-07-26 12:20:51 -05:00
co-authored by Claude Sonnet 5
parent 5e09330001
commit cafd513019
22 changed files with 1772 additions and 38 deletions
+30
View File
@@ -21,10 +21,12 @@ describe("API runtime configuration", () => {
const config = createApiRuntimeConfig(validInput(), { environment: "production" });
expect(config).toEqual({
clientAddress: { mode: "direct" },
databaseUrl: validInput().databaseUrl,
host: "127.0.0.1",
identity: {
baseUrl: "https://identity.example.test",
publicSignupEnabled: false,
secret: validSecret,
trustedOrigins: ["https://identity.example.test"],
},
@@ -131,6 +133,34 @@ describe("API runtime configuration", () => {
expect(config.identity.baseUrl).toBe("http://localhost:3000");
});
it("defaults public signup to disabled and threads an explicit enablement through to identity config", () => {
expect(createApiRuntimeConfig(validInput()).identity.publicSignupEnabled).toBe(false);
expect(
createApiRuntimeConfig({
...validInput(),
identity: { ...validInput().identity, publicSignupEnabled: true },
}).identity.publicSignupEnabled,
).toBe(true);
});
it("defaults client-address configuration to direct mode and threads explicit trusted-proxy configuration through", () => {
expect(createApiRuntimeConfig(validInput()).clientAddress).toEqual({ mode: "direct" });
const config = createApiRuntimeConfig({
...validInput(),
clientAddress: {
header: "x-forwarded-for",
mode: "trusted-proxy",
trustedProxies: ["10.0.0.1"],
},
});
expect(config.clientAddress).toEqual({
header: "x-forwarded-for",
mode: "trusted-proxy",
trustedProxies: ["10.0.0.1"],
});
});
it("never leaks the database URL, secret, or trusted origins through a thrown error", () => {
const hostileUrl = "postgresql://admin:[email protected]:5432/ekklesia";
let captured: unknown;