activescott / activescott/auth
Turnstile: sign-in forms can submit before the widget issues a token — silent auth failure
- Lenguaje dominante
- TypeScript
- Estrellas
- 0
- Forks
- 1
- Métricas de merge de PR
- Sin PR fusionados en 30 d
Descripción
## Bug
Production incident in fernfiles (activescott/fernfiles#6): a user submitted the email sign-in form before the Turnstile widget finished issuing its token (async challenge; slow on mobile). The POST reached `/auth/email/initiate` without a `cf-turnstile-response` field, `TurnstileBotCheck` rejected it (`turnstile:missing_token`), and no email was sent. From the user's perspective sign-in simply doesn't work — no email arrives and the error redirect is easy to miss. Every attempt on their device failed the same way.
Nothing in the library or its example prevents this: the react-router example renders the widget `div` inside the form, but the submit button is enabled the whole time. Any consumer following the example inherits the race.
## Ask
1. **Example + docs (minimum)**: the example's sign-in form should gate submission on the widget's `data-callback` (disable submit until the token exists, re-disable on `data-expired-callback`), with a doc note in `auth-botcheck-turnstile`'s README that the token is issued asynchronously and forms MUST wait. fernfiles' implementation (a small `useTurnstileReady` hook wiring named global callbacks) is in activescott/fernfiles#6 — free to lift.
2. **Shipped helper (better)**: an adapter-level `` component (or hook) that renders the script + widget and exposes `ready`, so consumers can't get the wiring wrong. It belongs next to `TurnstileBotCheck` since the server check is only half the contract.
3. **Error semantics**: `missing_token` deserves a distinct, user-facing error code from genuine bot rejection — 'the widget hadn't finished, try again' is recoverable and should say so (fernfiles currently special-cases `bot_check_failed` to a retry message, but it can't distinguish missing-token from failed-verification).
## Evidence
```
[auth] blocked initiate: reason=bot_check_failed detail=turnstile:missing_token provider=email ip=…
```
Two independent attempts, same device, both blocked; sign-in succeeded for other users whose widgets had completed. Dev/e2e never catch this because the abuse guard is disabled there — it is a prod-only failure mode, which is exactly why the library should make the safe pattern the default.
Guía de contribución
No hay ninguna guía de contribución indexada para este repositorio
Evaluación
Este issue todavía no se ha evaluado.