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
@@ -346,6 +346,15 @@ async function startConfiguredApi(
|
||||
url: string,
|
||||
options: StartConfiguredApiOptions = {},
|
||||
): Promise<RunningApi> {
|
||||
const publicSignupEnabled = options.publicSignupEnabled ?? true;
|
||||
// Production configuration refuses to enable public signup at all until durable abuse controls
|
||||
// exist (`PUBLIC_SIGNUP_UNSUPPORTED_IN_PRODUCTION`, `core/src/identity/config.ts`), so a scenario
|
||||
// that creates accounts over HTTP must validate its configuration as development — the explicit,
|
||||
// intended way to opt in. This changes only Ekklesia's own config-validation strictness:
|
||||
// `environment` never reaches Better Auth, and this harness keeps the identical https,
|
||||
// non-loopback base URL and trusted origins either way, so the production-equivalent origin/CSRF
|
||||
// behavior ADR 0012 established is exercised exactly the same in both cases.
|
||||
const environment = publicSignupEnabled ? "development" : "production";
|
||||
const config = createApiRuntimeConfig(
|
||||
{
|
||||
...(options.clientAddress === undefined ? {} : { clientAddress: options.clientAddress }),
|
||||
@@ -353,13 +362,13 @@ async function startConfiguredApi(
|
||||
host: "127.0.0.1",
|
||||
identity: {
|
||||
baseUrl: identityBaseUrl,
|
||||
publicSignupEnabled: options.publicSignupEnabled ?? true,
|
||||
publicSignupEnabled,
|
||||
secret: "integration-test-secret-0123456789abcdefgh",
|
||||
trustedOrigins: [identityBaseUrl, dashboardOrigin],
|
||||
},
|
||||
port: 0,
|
||||
},
|
||||
{ environment: "production" },
|
||||
{ environment },
|
||||
);
|
||||
return startApi(config, {
|
||||
...("rateLimitOverride" in options
|
||||
@@ -902,7 +911,9 @@ describe.sequential("live API — startup and migration guarantees", () => {
|
||||
},
|
||||
port: 0,
|
||||
},
|
||||
{ environment: "production" },
|
||||
// Development validation, for the same reason `startConfiguredApi` documents: this scenario
|
||||
// drives a real sign-up request, which production configuration refuses to enable at all.
|
||||
{ environment: "development" },
|
||||
);
|
||||
const running = await startApi(config, { rateLimitOverride: { max: 1000, window: 1 } });
|
||||
const origin = apiOrigin(running);
|
||||
|
||||
Reference in New Issue
Block a user