Skip to content

Email verification

Email verification uses one-time tokens stored in the token_adapter. It requires:

  • a TokenAdapter (e.g. adapter.token),
  • an EmailTransport if you actually want the email delivered.

Registration does NOT send a verification email. Calling POST /auth/register creates the user with email_verified=False and immediately issues tokens. The user must then explicitly request a verification email.

Request a verification email

curl -X POST http://localhost:8000/auth/request-verify-email \
  -H "Authorization: Bearer $ACCESS"
# -> {"message":"Verification email sent"}

Implementation (api/auth.py): generates a one-time token of type "verification", hashes it, stores it in the token adapter with a 24-hour expiry, and calls email_dispatcher.send_verification_email(user, token, expires_in_minutes=1440). If no email_transport is configured, the email is silently not sent — but the token is still stored, so you can complete verification via the API directly.

Verify the email

Two equivalent endpoints:

# GET — convenient for links in emails
curl "http://localhost:8000/auth/verify-email?token=..."

# POST — convenient for SPAs
curl -X POST http://localhost:8000/auth/verify-email \
  -H "Content-Type: application/json" \
  -d '{"token":"..."}'

Both call _verify_email, which atomically consumes the hashed token. On success, the user's email_verified is set to True and on_email_verify is fired.

Edge cases

  • /auth/register does NOT send a verification email. Documented again here because it is the #1 source of confusion.
  • /auth/login ignores email_verified. Unverified users can still log in — gating on verification is your application's responsibility. A common pattern is allow_signin hook + email_verified check, but note the hook reach below.
  • allow_signin is not called by /auth/login (the credentials route). It is called by OAuth callback, magic-link callback, and passkey authenticate-complete. If you want to gate credentials login on email_verified, do it in your own route wrapper or open an issue (see Maintainer clarification).
  • OAuth providers that report email_verified=True propagate that to the new user on first OAuth sign-in (core.oauth.complete_oauth_flow).

Common mistakes

  • Calling /auth/verify-email with the raw email token but the endpoint returns Invalid or expired — make sure the value sent is the raw token from the email, not the hash.
  • No token_adapter configured/auth/request-verify-email returns 400 Token adapter is not configured.