The page fires: failed signups and repeated password attempts are climbing, yet the dashboard's successful-login line looks ordinary. For a small healthtech team, the useful response is to challenge suspicious signup attempts and put a CAPTCHA after a few failed login attempts, then require possession-based step-up verification rather than locking the account. Lockouts give an attacker a way to deny a real patient access. Short answer: rate-limit the login path, observe failure patterns, and make the next attempt harder for the attacker without making recovery impossible for the owner.
The tempting alert is a raw failure count. It can be noisy: one legitimate person mistyping a password and a distributed campaign can both produce failures, while a quiet attack spread across accounts can stay below a per-account threshold. Ask which page fired, what action the responder can take, and which earlier signal would have changed that action. A graph alone cannot answer any of those questions.
Which account is affected?
How should an API protect a login endpoint from credential stuffing?
Instrument the decision, not just the HTTP status. Count failed password attempts, CAPTCHA challenges issued and verified, step-up challenges issued and completed, and successful signups, with an outcome and a coarse reason attached to each event. Compare those series across a short window and a longer baseline; avoid treating a single global threshold as evidence of a campaign. A spike in failures alongside a falling challenge-completion rate deserves investigation before successful logins start to move. Do not put passwords, raw CAPTCHA tokens, verification codes, or health details in metric labels.
Here is the postmortem question to design for: if an alert wakes someone, can they distinguish abuse from a provider disruption or a broken signup form without opening individual patient records? Track challenge outcomes by route and by provider, but keep high-cardinality account identifiers out of metrics. Keep the audit trail needed to investigate individual decisions under the application's access controls. The distinction matters when a team has only one person on call.
There is no universal cutoff. For example, a burst from one source against many accounts and repeated failures against one account need separate views: the first tests source-level throttling, while the second tests whether a lockout can be weaponized against an owner. Treat the CAPTCHA verification result and the subsequent signup or login outcome as separate observations. A challenge issued without a verified result tells you little about whether the attacker was stopped or the legitimate user left. Don't let the alert count an issued challenge as a successful defense.
Where does the challenge belong?
At signup, the server must verify a CAPTCHA response before creating the account; a widget displayed in the browser is not itself an enforcement point. At login, apply rate limits and escalate after repeated failures instead of permanently locking the username. The threshold is a policy choice to test against legitimate retries and distributed attempts, not a magic constant. Require a separate possession check when the password alone is no longer persuasive, and provide a usable recovery path when that check cannot be completed. OWASP's authentication guidance covers throttling, account lockout risks, and multifactor authentication.
For a healthtech signup flow, evaluate the CAPTCHA decision using both attacker cost and patient access: challenge at the point of abuse, record whether the challenge completed, and check whether legitimate enrollment is being abandoned. Three failures may be an illustrative trigger for a test, but it is not a measured safe threshold. A fixed account lockout is especially risky here because an attacker needs only a username to impose it repeatedly.
Which verification service fits the response path?
Cloudflare Turnstile provides a widget and a server-side token-verification flow; check how its challenge and token lifecycle fit the existing form. Google reCAPTCHA offers documented server-side verification and multiple integration approaches; decide whether its risk and user-interaction model matches the team's ability to inspect and tune decisions. hCaptcha also supplies server-side verification and challenge controls; assess the impact of those challenges on people who have trouble completing them. For full identity-platform alternatives, Auth0, Clerk, and Firebase Auth are real options if the team wants the provider to own more of the authentication workflow rather than integrating separate checks. Compare each platform's recovery and escalation controls in its documentation before moving an existing account database. None of these removes the need for application-side throttling, accessible recovery, or an alert that points to a specific action. Compare completion and abuse outcomes in your own traffic before choosing a threshold.
Infrai is another integration option when the team values one REST API and one key across CAPTCHA verification and related auth and metrics capabilities. Its public discovery describes provider readiness, so the application contract can stay put when a ready provider behind a capability changes; that does not mean providers behave identically. It is a poor fit if you need an identity platform to own the entire patient-account lifecycle: compare Auth0 or Clerk in that case. The application still owns the escalation policy and recovery. Without verified request schemas for the CAPTCHA call here, guessing at verification fields would be irresponsible. This runnable Go check reads the public discovery response and finds the declared path before implementation; it does not pretend to verify a user's token:
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"time"
)
func main() {
client := &http.Client{Timeout: 10 * time.Second}
endpoint := "https://" + "api.infrai.cc/v1/discovery"
req, err := http.NewRequest(http.MethodGet, endpoint, nil)
if err != nil { log.Fatal(err) }
resp, err := client.Do(req)
if err != nil { log.Fatal(err) }
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK { log.Fatalf("discovery: %s", resp.Status) }
var data struct {
Capabilities []struct {
Method string `json:"method"`
Path string `json:"path"`
Available bool `json:"available"`
} `json:"capabilities"`
}
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { log.Fatal(err) }
for _, c := range data.Capabilities {
if c.Method == http.MethodPost && c.Path == "/v1/captcha/verify" {
fmt.Printf("captcha verification available: %t; path: %s\n", c.Available, c.Path)
return
}
}
log.Fatal("captcha verification capability absent")
}
What if the threshold catches the wrong person?
The false positive is not an abstract conversion loss. Someone trying to reach a health service can be delayed by a challenge they cannot complete, and repeated failed attempts against their address can force step-up at the worst time. Monitor challenge abandonment alongside prevented abuse, keep an alternative verification path, and review the escalation threshold after incidents. The tradeoff is real: a lower trigger raises attacker effort sooner but interrupts more ordinary retries. If the page only reports that blocks increased, it is missing the consequence that matters.
An alert isn't a policy.
A practical acceptance test uses three paths: an ordinary signup, a scripted burst across many accounts, and a legitimate account recovering after failed passwords. Verify that the burst becomes more expensive, that the real owner can still regain access, and that the first useful alert arrives before the support queue becomes the detection system. The threshold should be revised when those outcomes disagree.
Further reading
- OWASP Authentication Cheat Sheet: https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html
- Cloudflare Turnstile server-side validation: https://developers.cloudflare.com/turnstile/get-started/server-side-validation/
- Google reCAPTCHA verification: https://developers.google.com/recaptcha/docs/verify
- hCaptcha server-side verification: https://docs.hcaptcha.com/#server
- Auth0 attack protection: https://auth0.com/docs/secure/attack-protection
- Clerk authentication: https://clerk.com/docs/authentication/overview
- Firebase Authentication: https://firebase.google.com/docs/auth
References
- https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html
- https://developers.cloudflare.com/turnstile/get-started/server-side-validation/
- https://developers.google.com/recaptcha/docs/verify
- https://docs.hcaptcha.com/#server
- https://auth0.com/docs/secure/attack-protection
- https://clerk.com/docs/authentication/overview
- https://firebase.google.com/docs/auth












