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:
co-authored by
Claude Sonnet 5
parent
5e09330001
commit
cafd513019
@@ -15,6 +15,7 @@ describe("Core identity configuration", () => {
|
||||
|
||||
expect(config).toEqual({
|
||||
baseUrl: validBaseUrl,
|
||||
publicSignupEnabled: false,
|
||||
secret: validSecret,
|
||||
trustedOrigins: validTrustedOrigins,
|
||||
});
|
||||
@@ -167,6 +168,41 @@ describe("Core identity configuration", () => {
|
||||
expect(config.baseUrl).toBe("http://localhost:3000");
|
||||
});
|
||||
|
||||
it("defaults publicSignupEnabled to false when entirely omitted", () => {
|
||||
const config = createIdentityConfig(validInput(), { environment: "production" });
|
||||
expect(config.publicSignupEnabled).toBe(false);
|
||||
});
|
||||
|
||||
it("accepts an explicit boolean publicSignupEnabled", () => {
|
||||
expect(
|
||||
createIdentityConfig({ ...validInput(), publicSignupEnabled: true }).publicSignupEnabled,
|
||||
).toBe(true);
|
||||
expect(
|
||||
createIdentityConfig({ ...validInput(), publicSignupEnabled: false }).publicSignupEnabled,
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it('accepts the exact strings "true"/"false" (environment variables are always strings)', () => {
|
||||
expect(
|
||||
createIdentityConfig({ ...validInput(), publicSignupEnabled: "true" }).publicSignupEnabled,
|
||||
).toBe(true);
|
||||
expect(
|
||||
createIdentityConfig({ ...validInput(), publicSignupEnabled: "false" }).publicSignupEnabled,
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it("rejects a misspelled, loosely-truthy, or malformed publicSignupEnabled rather than silently defaulting", () => {
|
||||
for (const publicSignupEnabled of ["1", "0", "yes", "no", "TRUE", "True", " true", "", 1, 0]) {
|
||||
let captured: unknown;
|
||||
try {
|
||||
createIdentityConfig({ ...validInput(), publicSignupEnabled });
|
||||
} catch (error) {
|
||||
captured = error;
|
||||
}
|
||||
expect(captured).toMatchObject({ code: "PUBLIC_SIGNUP_ENABLED_INVALID" });
|
||||
}
|
||||
});
|
||||
|
||||
it("never leaks the secret, base URL, or trusted origins through a thrown error", () => {
|
||||
const hostileSecret = "s3cr3t-do-not-log-0123456789abcdef";
|
||||
let captured: unknown;
|
||||
|
||||
Reference in New Issue
Block a user