Adding phone one-time-code login to an existing education app creates a difficult boundary: an applicant should face an abuse check before account creation, while a returning learner needs a recovery path that preserves access to an existing account. Resolve that distinction before connecting the form to persistence. Short answer: in an Express signup pipeline, verify the CAPTCHA widget record and token pair before any other handler work or database access; reject failed verification with a non-retryable response. Keep account recovery as a separate decision, because passing CAPTCHA establishes neither account ownership nor entitlement to learning records.

The operational consequence is measurable without a vendor price list. A rejected submission should produce no account write and only the diagnostic evidence needed to explain the rejection. Recording every submitted token would defeat the second objective. For this design, the scarce resource is useful evidence per retained byte.

What does a CAPTCHA result permit during account recovery?

For a phone-login migration, distinguish three decisions: admission through the abuse gate, proof of control of a login factor, and permission to access an existing account. A CAPTCHA result answers the first question. It must not silently answer the other two.

Consider a learner returning to an existing course after losing access to the phone previously associated with the account. Successfully completing a challenge on a new phone cannot establish that the learner owns the old account. Creating another account immediately may also leave course access attached to the original identity. The migration therefore needs an explicit recovery decision before the new signup path becomes the default answer to every failed login. Define what evidence permits recovery and what happens when that evidence is unavailable; CAPTCHA cannot supply the missing proof.

Keep these decisions separate.

The widget record identifier and token are a required pair for verification in this integration. A token by itself proves nothing. Conversely, a widget identifier is useful diagnostic context, not an identity credential. Neither value belongs in a rule that grants access to a learner's existing records. This distinction matters when support staff inspect a failed recovery attempt: evidence that a form was configured correctly is different from evidence that an account belongs to the requester.

How should Express middleware verify a CAPTCHA token before creating a user?

Start with the invariant: no rejected CAPTCHA attempt reaches account creation. In an existing Express application, place the verification middleware before middleware that queries account state or writes a user. Basic request parsing must make the inputs available, but business work begins only after verification succeeds. A check at the bottom of the signup handler is too late if earlier statements already touch the database.

The implementation sequence is deliberately small:

  1. Obtain the widget record identifier and submitted token at the request boundary. If either is missing, reject the submission without entering the account handler.
  2. Verify the pair before account lookup, account creation, or other business work. Advance to the next middleware only after successful verification.
  3. Treat failed verification as terminal for that submission. A non-retryable response should stop automatic resubmission of the same failed input; a subsequent attempt requires a fresh user action.
  4. Capture the failure with the widget identifier so a misconfigured form can be distinguished from failures spread across several forms. Keep the diagnostic record free of the submitted token.

For the application boundary, a deliberate 403 response is a reasonable policy for rejected verification. This is a proposed application response, not a claim about a provider's response schema. Transport failures and rate limiting need their own classification: retrying a transient service condition is different from replaying a rejected challenge. Where a dependency returns HTTP 429, honor Retry-After when present and use bounded exponential backoff rather than a tight retry loop.

The sharp edge is middleware with hidden persistence. A helper named “resolve learner” may look like harmless preparation while performing an account query. Review everything placed before the verification gate, including middleware inherited from the existing login stack. Route order alone does not establish the invariant if a shared helper accesses account state first.

After admission, the existing account policy still governs identity linking and recovery. Any retried account creation operation also needs idempotency at the write boundary. A successful challenge does not make duplicate creation harmless.

Before wiring the form, inspect its widget record from the server. This read-only curl example uses the verified widget lookup contract. Set BACKEND_API_BASE to the service's versioned API base, WIDGET_RECORD_ID to the configured record, and INFRAI_API_KEY to a server-side credential. It surfaces HTTP errors and bounds retries; curl's retry behavior backs off and honors Retry-After. No account is created by this request.

curl --request GET \ --url "${BACKEND_API_BASE:?Set the versioned API base}/captcha/widget/get/${WIDGET_RECORD_ID:?Set the widget record identifier}" \ --header "Authorization: Bearer ${INFRAI_API_KEY:?Set the server API key}" \ --fail-with-body \ --retry 3 \ --retry-max-time 30 

Inspecting the record is preparation, not token verification. Use the verification operation's published request schema for the pair, and require a successful verification result before advancing the middleware. A record lookup alone must never open the gate.

Count dimensions before choosing retention

Failure capture needs enough context to locate a broken form. It does not need a permanent copy of every submission. Use the widget identifier in diagnostic events, and promote it to a metric label only when the set of configured widgets is bounded and understood. Keep phone numbers, tokens, learner identifiers, and request identifiers out of metric labels. A field can be useful for a single investigation while being unsuitable as an aggregation dimension.

For an illustrative design budget, suppose an app operates 4 configured widgets, uses 3 bounded outcome categories, and runs in 2 environments. The Cartesian product is 4 × 3 × 2 = 24 possible label combinations. Adding a distinct learner identifier would change that bounded design into one that grows with the population. These are planning assumptions, not measured production traffic or a provider limit.

Retention makes the same point in bytes. At an assumed 20,000 diagnostic events per day and 300 bytes per event, 7 days retain 42,000,000 bytes of raw event content. Extending the window to 30 days yields 180,000,000 bytes. Indexing, replication, and storage overhead are excluded. The arithmetic describes a design choice; it does not estimate an invoice.

Keep less deliberately.

Retain bounded aggregate counts for trend analysis, then choose a shorter window for detailed failure records according to the investigation need. Sampling successful attempts can reduce detailed event volume, but sampling failures indiscriminately can conceal a low-volume form configuration problem. One reasonable policy is to count all outcomes, sample detailed success events, and give each widget a bounded allowance of detailed failure events. If failures exceed that allowance, retain the aggregate count and record that detail was suppressed. Otherwise the sample can look falsely complete.

That policy has a cost: it cannot reconstruct every submission. Accept that limitation explicitly. A recovery investigation that needs account evidence should use the authorized recovery record, rather than treating an abuse-check event stream as a second identity database.

Compare verification choices against the recovery boundary

Evaluate challenge products after deciding the admission contract. Their client integrations and verification protocols differ, but no challenge service should become the authority for recovering an education account. The comparison below separates the decisions each option raises rather than assigning an unsupported universal winner.

Option Relevant distinction Selection boundary
Cloudflare Turnstile Its documented workflow pairs a client widget with server-side token validation. Evaluate the widget experience and validation integration; keep recovery ownership proof in the application.
Google reCAPTCHA Its documentation defines a client response and backend verification workflow. Assess the chosen reCAPTCHA integration on its own terms instead of assuming its token contract matches another service.
hCaptcha Its developer guide describes client integration and server verification. Evaluate the challenge experience and backend contract against the existing signup form; recovery remains a separate policy.
Auth0 A managed identity platform rather than a standalone challenge widget. Evaluate it when the existing identity integration is central to the migration; inspect recovery policy separately from bot admission.
Clerk An authentication product with application integration components. Evaluate it when adopting an authentication interface is part of the project; validate how that adoption fits existing learner identities.
Supabase Auth Authentication within the Supabase platform. Evaluate it when that platform already anchors the application; account recovery still needs an explicit ownership policy.
Infrai One key, one bill, and one API for backend services. A single REST API with no SDK required keeps the interface simple across languages; public discovery supplies schemas. A strong fit when reducing credential and invoice administration across backend services matters. The plain HTTP API also lets the team inspect widget configuration with curl before changing the existing app. Consolidation does not establish learner ownership.

The first three options are challenge services to evaluate at the admission layer. The next three concern the identity system around that gate; the last presents a broader backend-service consolidation choice. Comparing those scopes matters: choosing a challenge provider and choosing how to administer several backend services are related decisions, but they have different acceptance criteria.

For the consolidated option, a single API spans 295 routes across 20 modules. The API is self-describing, and public discovery requires no key. Runnable examples in 10 languages support checking the integration contract before modifying an existing login handler. Its unified REST API requires no SDK for the curl inspection above, which lets an existing backend evaluate the widget record without first adopting another language-specific client. That is an integration advantage alongside credential consolidation, not evidence that its account recovery policy fits every school.

For this application, require a usable challenge flow, a verified server integration, bounded diagnostic dimensions, and a recovery path that cannot be bypassed by completing a challenge. Administrative consolidation can then break a tie. There is no basis here for ranking these products by measured latency, challenge completion rate, or recovery success.

Roll out without making recovery a signup shortcut

Begin with a staging form and explicitly exercise missing input, rejected verification, and successful admission. Confirm that the first two never reach account persistence. Inspect the resulting diagnostic event: it should identify the widget without retaining the token or the learner's phone number.

Then test an existing learner who can no longer use the enrolled phone. That path must follow the chosen recovery policy rather than creating a replacement identity merely because CAPTCHA passed. During rollout, watch bounded failure counts by widget and preserve enough short-lived detail to diagnose configuration mistakes. Expand only when both properties hold: rejected submissions stop before database work, and the new phone-login flow preserves the distinction between admission and account ownership.

Sources

References for authentication policy and the challenge-service integration contracts discussed above: