From be1f54473273a97d5201948433a53216da26398f Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Tue, 25 Aug 2026 22:30:35 -0700 Subject: login: fail-open recaptcha verify on infrastructure errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Claude-Session: https://claude.ai/code/session_01XiwgmQKwKnRxUmVDd4xSf7 --- routes/users.ts | 19 +++++++++++++++++-- 1 file 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; } }; -- cgit v1.2.3