Set ALLOWED_ORIGIN to the exact page origin, including scheme and port. For local development with the template, serve the client from the same origin so no CORS is involved. Check that the preflight request is reaching the service at all.
The client refreshes the token automatically once on a 401. A persistent 401 usually means the refresh token is missing or expired. Call client.clearTokens() and sign in again. If this happens for every visitor, check that sessions and signing keys are persisted; an in-memory storage loses them on restart.
Confirm CAPTCHA_PROVIDER and the site key/secret match the widget. With none, the client sends an empty token and the server skips verification. After changing providers, redeploy the service so the public config endpoint updates.
forgot-password always returns success. Verify the sender address is verified in Resend, that RESEND_API_KEY is set in the deployment, and that the reset link points at a reachable page. During local development with email disabled, the flow exercises everything except delivery.
Sign-in attempts are rate limited per the configured window. Wait for the window to pass, or raise RATE_LIMIT_MAX_ATTEMPTS for non-production environments. Distributed deployments should use the Redis rate limiter so counters are shared.
Run storage.migrate() on start, as the hosted service and template do. It creates tables if they are missing. If you manage the schema yourself, mirror the auth schema exactly, including cascade rules.
Signing keys live in the database, so tokens survive restarts. If tokens break after a redeploy, the database likely is not shared between instances, or the signing-key table was wiped. See Storage.