L LAB

Email Verification

Signed-URL email verification with optional login enforcement and automatic verification on OTP login.

How it works

  1. Registration fires Registered, which triggers sendEmailVerificationNotification().
  2. The user receives an email containing a signed verify URL.
  3. Clicking it hits GET /auth/email/verify/{id}/{hash}.
  4. The address is marked verified and the Verified event fires.

OTP verification also marks the email verified — the user just proved they own the address.

Endpoints

MethodPathAuthDescription
GET/api/v1/auth/email/verify/{id}/{hash}Signed URL onlyVerify the address.
POST/api/v1/auth/email/verification-notificationRequiredResend the email.

Verify is intentionally unauthenticated so the link works from any device. Security comes from the signed URL plus a sha1(email) hash segment.

Configuration

'auth' => [
    'email_verification' => [
        'enabled' => env('AUTH_EMAIL_VERIFICATION_ENABLED', true),
        'required_for_login' => env('AUTH_EMAIL_VERIFICATION_REQUIRED_FOR_LOGIN', false),
        'expire_minutes' => env('AUTH_EMAIL_VERIFICATION_EXPIRE_MINUTES', 60),
        'redirect_url' => env('AUTH_EMAIL_VERIFICATION_REDIRECT_URL'),
    ],
],
KeyEffect
enabledWhen false, no verification emails are sent on registration.
required_for_loginWhen true, password login refuses unverified accounts with 403. OTP login is unaffected (it auto-verifies).
expire_minutesValidity window of the signed link.
redirect_urlWhen set, verify redirects to <url>?status=success (or ?status=failure&reason=invalid) instead of returning JSON.

Behaviour summary

ScenarioResult
Valid signed link clickedEmail verified, Verified dispatched.
Tampered query string403 (signed middleware rejects).
Wrong hash segment404 “Verification link is invalid.”
Expired link403.
Resend by verified user200 “Email already verified.” (no email).
Resend without auth401.
Resend over the limit429 (auth-email_verify_resend).
Password login, unverified, required_for_login=true403.
OTP verify (any user)Email marked verified.

Frontend redirect

With AUTH_EMAIL_VERIFICATION_REDIRECT_URL set, after a click the user lands on …?status=success or …?status=failure&reason=invalid. Your frontend reads the query string and renders the right state. Without a redirect URL the endpoint returns JSON — fine for API-only setups, poor UX for browser clicks.

Customising the email

The mailable is App\Mail\VerifyEmail with view resources/views/emails/verify-email.blade.php. Edit the Blade for branding, or swap the mailable for a Notification class.