Email Change

Email changes go through a confirmation flow similar to password resets. The user's current email stays active until the new one is confirmed via a link sent to the new address.

Flow

  1. The authenticated user sends their new email to POST /account/email
  2. A confirmation link is sent to the new address with a token
  3. The client handles the link click and sends the token to POST /auth/change-email
  4. The email is swapped and email_verified_at is set to the current timestamp

The old email remains unchanged until step 4 completes. The user stays fully functional throughout.

Why a separate token table?

This could have reused the existing verification system, but verification is multi-channel by design (email, SMS) and uses short codes. Email changes are inherently email-only and work better with signed links. The email_change_tokens table keeps these concerns separate and mirrors the password_reset_tokens pattern already in the project.

Token behavior

Tokens are hashed before storage, single-use, and scoped to one user. Requesting a new token deletes any previous one. Expiry and throttle are controlled by auth.email_change.expire and auth.email_change.throttle in config/auth.php, defaulting to 60 minutes and 60 seconds respectively.

Uniqueness

The new email is validated against the users table on request. A race condition where someone else claims the email between request and confirmation is effectively impossible since the confirmation requires inbox access, and the unique constraint on users.email catches it at write time regardless.

The confirmation endpoint is unauthenticated

Same reasoning as password resets. The token is sent to the new inbox, so clicking the link proves ownership. Requiring an active session would break if the user opens the email on a different device or browser.

© Websanova 2026 About Privacy