fix: fail closed on production signup
Production configuration accepted publicSignupEnabled: true and started a working Identity/API runtime, even though Ekklesia has no verified email, invitation requirement, CAPTCHA, or durable shared/edge limiter — Doctor merely reported "Public signup is explicitly ENABLED" as a pass. validatePublicSignupEnabled now takes the same IdentityEnvironment the rest of this config validates against and throws the stable PUBLIC_SIGNUP_UNSUPPORTED_IN_PRODUCTION whenever the parsed value is true in production. Parsing stays separate from the policy, so a malformed value still reports PUBLIC_SIGNUP_ENABLED_INVALID everywhere. Because the executable entry point validates configuration before startApi, such a deployment fails before any pool, Better Auth instance, module runtime, or listener exists, and Doctor reports NOT READY via its existing IdentityConfigError path. Development and test composition still enable it explicitly through the identical typed path. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5
parent
d3c6bd73d3
commit
1161f5a076
@@ -418,6 +418,44 @@ describe("Ekklesia API shutdown lifecycle", () => {
|
||||
expect(serveMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("refuses a production configuration that enables public signup before any pool, Better Auth instance, module runtime, or server exists", async () => {
|
||||
const poolEnd = vi.fn().mockResolvedValue(undefined);
|
||||
poolConstructor.mockImplementation(function fakePool() {
|
||||
return { end: poolEnd, on: vi.fn() };
|
||||
});
|
||||
betterAuthMock.mockReturnValue({ api: { getSession: vi.fn() }, handler: vi.fn() });
|
||||
mockSuccessfulServe();
|
||||
|
||||
// The rejection happens in `createApiRuntimeConfig` itself — the same boundary the executable
|
||||
// entry point calls before `startApi` — so `startApi` is never even reachable with such a
|
||||
// configuration, and no runtime resource is ever constructed to leak.
|
||||
let captured: unknown;
|
||||
try {
|
||||
createApiRuntimeConfig(
|
||||
{
|
||||
databaseUrl: "postgresql://ekklesia:[email protected]:5432/ekklesia",
|
||||
host: "127.0.0.1",
|
||||
identity: {
|
||||
baseUrl: "https://identity.example.test",
|
||||
publicSignupEnabled: true,
|
||||
secret: "correct-horse-battery-staple-0123456789",
|
||||
trustedOrigins: ["https://identity.example.test"],
|
||||
},
|
||||
port: 0,
|
||||
},
|
||||
{ environment: "production" },
|
||||
);
|
||||
} catch (error) {
|
||||
captured = error;
|
||||
}
|
||||
|
||||
expect(captured).toMatchObject({ code: "PUBLIC_SIGNUP_UNSUPPORTED_IN_PRODUCTION" });
|
||||
expect(poolConstructor).not.toHaveBeenCalled();
|
||||
expect(betterAuthMock).not.toHaveBeenCalled();
|
||||
expect(serveMock).not.toHaveBeenCalled();
|
||||
expect(poolEnd).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("closes an already-created database pool when the server fails to bind its listen address", async () => {
|
||||
const poolEnd = vi.fn().mockResolvedValue(undefined);
|
||||
poolConstructor.mockImplementation(function fakePool() {
|
||||
|
||||
Reference in New Issue
Block a user