Add a CLIENT_IP_HEADERS escape hatch and stop the auth handler hanging
Two changes, both from working out how to verify the client-IP configuration on the Plesk vhost before deploying. CLIENT_IP_HEADERS=none now trusts no header at all. This covers the one case where the wrong setting is worse than no setting: if the proxy does not overwrite the header we trust, any client can send it and mint itself an unlimited brute-force budget against /sign-in. Falling back to the shared rate-limit bucket is a nuisance - one noisy client can lock everyone out for ten seconds at a time - but it fails closed, and it can be reverted from the environment without a deploy. An empty or unset value still means "use the default": a stray blank line in a .env must not silently change how requests are bucketed, so only the explicit word does that. An empty array is what better-auth reads as "no headers"; it falls back to its own default only when the option is absent, and [] is truthy. The TRUSTED_PROXY_IPS boot warning was overstating the risk. It now says that a single-value header needs no trusted proxies, so seeing it on a plain single-proxy setup is expected rather than a problem to chase. Separately: a rejected promise from better-auth's handler used to escape as an unhandled rejection, leaving the request hanging forever with no response while the process logged an uncaughtException. Express 4 does not await an async handler, and nearly every better-auth route touches the admin database, so any database blip would have done this. Found by pointing ADMIN_DB at a database the user cannot open while testing the hatch. The handler now answers 503, so the caller learns and the other domains keep serving; verified as a 24ms response instead of a hang, with / and /feedback/admin/me unaffected. test/admin/admin.config.test.ts is new: it pins both branches of the hatch and the rule that an unset NODE_ENV counts as production. It stubs dotenv, because admin.config would otherwise read the repo's own .env and quietly reintroduce NODE_ENV=development - the exact value several of those cases exist to remove. 157 unit tests and 41 integration tests pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -52,6 +52,12 @@ ADMIN_BOOTSTRAP_EMAIL=
|
||||
# request shares ONE rate-limit bucket (/sign-in/* allows 3 per 10 seconds, so
|
||||
# one noisy client locks everyone out). Check with:
|
||||
# SELECT `key` FROM rateLimit; -- a "no-trusted-ip" row means it is happening.
|
||||
# The header the reverse proxy puts the real client IP in. Must be one the proxy
|
||||
# actually overwrites - trusting a header it does not set lets any client send its
|
||||
# own value and bypass the sign-in rate limit entirely.
|
||||
# Set to "none" to trust no header at all: every request then shares one rate-limit
|
||||
# bucket, which is the safe fallback if the check below fails. Verify after deploy
|
||||
# with: SELECT ipAddress FROM session ORDER BY createdAt DESC LIMIT 3;
|
||||
CLIENT_IP_HEADERS=x-real-ip
|
||||
TRUSTED_PROXY_IPS=
|
||||
|
||||
|
||||
Reference in New Issue
Block a user