aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-08-25 22:30:35 -0700
committerAdam Soltys <[email protected]>2026-08-25 22:30:35 -0700
commitbe1f54473273a97d5201948433a53216da26398f (patch)
tree69efeb04022c3c1e8ddcadd3a79e48fd2faef542
parent13d906b4239e7520359df76943dece656e10e240 (diff)
downloadcoinos-server-be1f54473273a97d5201948433a53216da26398f.tar.gz
coinos-server-be1f54473273a97d5201948433a53216da26398f.zip
login: fail-open recaptcha verify on infrastructure errors
verifyRecaptcha failed closed on ANY error from the Google siteverify call, so a transient coinos<->Google network blip (or a Google slowdown) turned every non-admin login into a "failed captcha" 401 until connectivity recovered — a self-inflicted login outage from an external dependency. Distinguish a real verdict from an infrastructure failure: fail CLOSED only on a genuine success:false from Google; on a thrown error (network, timeout, 5xx) fail OPEN and log it. Reaching Google is not part of authentication — the password and the per-IP login rate limits still gate the request, and captcha is only bot-mitigation, so allowing a login through when the verifier is unreachable is the safer failure mode. Also bound the call with a 6s timeout so a slow verifier can't hang the login. Co-Authored-By: Claude Opus 4.8 <[email protected]> Claude-Session: https://claude.ai/code/session_01XiwgmQKwKnRxUmVDd4xSf7
-rw-r--r--routes/users.ts19
1 files changed, 17 insertions, 2 deletions
diff --git a/routes/users.ts b/routes/users.ts
index 20afc5d8..438452b2 100644
--- a/routes/users.ts
+++ b/routes/users.ts
@@ -68,11 +68,26 @@ const verifyRecaptcha = async (response, req?) => {
response,
remoteip: ip,
},
+ // Bound the wait so a slow/unreachable Google can't hang the login.
+ timeout: { request: 6000 },
})
.json();
+ // A real verdict from Google: fail CLOSED only when the captcha itself is
+ // invalid (success === false).
return success || (!!config.adminpass && response === config.adminpass);
- } catch {
- return false;
+ } catch (e) {
+ // Fail OPEN on an INFRASTRUCTURE error (network blip, timeout, Google 5xx).
+ // Reaching Google is not part of authentication; failing closed here turned
+ // a transient coinos<->Google connectivity hiccup into a login outage —
+ // users got "failed captcha" until it cleared (2026-08-25 incident). The
+ // password and the per-IP login rate limits (ipCount>30 / ipFailures>20 ->
+ // 429) still gate the request; captcha is only bot-mitigation, so allowing
+ // it through when the verifier is unreachable is the safer failure mode.
+ warn(
+ "recaptcha verify unreachable — allowing login (fail-open)",
+ (e as any)?.code ?? (e as any)?.message ?? String(e),
+ );
+ return true;
}
};