Skip to content

Require a verified email address before the app opens - #18

Merged
SourovCodes merged 1 commit into
mainfrom
feat/require-a-verified-email
Sep 20, 2026
Merged

SourovCodes merged 1 commit into
mainfrom
feat/require-a-verified-email

Conversation

@SourovCodes

Copy link
Copy Markdown
Member

MustVerifyEmail was commented out on the User model, so Laravel never sent a verification mail and hasVerifiedEmail() was always true. That is why the verified middleware was dead weight and got removed — the contract behind it was never implemented.

Implementing it is the half that was missing. Everything else was already here and already tested: Fortify's three verification routes, the verify-email page, VerifyEmailResponse, an unverified() factory state, EmailVerificationTest and VerificationNotificationTest. This turns the flow on rather than building it.

What changed

The middleware goes back on, and this time it bites. Registration signs the account in, sends the mail, and holds it at the prompt until the link is clicked.

Accepting an invitation is gated too, which it was not before. An invitation is addressed to an email; joining an organization on the strength of one should require proving the address is yours, or anyone could type a colleague's address at registration and walk in. The profile page stays reachable without verification, so an address typed wrong can still be corrected.

/dashboard is new, and is needed now. config('fortify.home') is the fixed string /dashboard, which Fortify redirects to on its own when an already verified user opens the verification prompt or asks for another mail. Every real dashboard lives under an organization, so that path was a 404 waiting for the first person to click a stale link. It now forwards to the current organization, or to onboarding.

.env.example pointed at MAIL_MAILER=log on port 2525. Nothing listens on 2525 here and mailpit is on 1025, so a fresh checkout would have buried the verification link in a log file at the exact moment the link became mandatory.

Verification

New VerifiedAccessTest covers each gated surface, the profile escape hatch, that registration sends the mail and leaves the account unverified, and both /dashboard outcomes. Dropping the contract while keeping the middleware turns 8 of its 12 cases red, so the guard is real rather than a no-op.

The browser tests now follow the actual signed link from the mail rather than writing email_verified_at directly.

Checked end to end against Herd and mailpit: register → prompt → mail arrives over SMTP → the link in it lands on onboarding. 330 tests, PHPStan, Pint, tsc and vp check all green.

🤖 Generated with Claude Code

MustVerifyEmail was commented out on the User model, so Laravel never
sent a verification mail and hasVerifiedEmail() was always true. That is
why the `verified` middleware was dead weight and got removed: the
contract behind it was never implemented.

Implementing it is the half that was missing. Everything else was
already here and already tested -- Fortify's three verification routes,
the verify-email page, VerifyEmailResponse, an unverified() factory
state, EmailVerificationTest and VerificationNotificationTest -- so this
turns the flow on rather than building it.

The middleware goes back on, and this time it bites. Registration signs
the account in, sends the mail and holds it at the prompt until the link
is clicked.

Accepting an invitation is gated too, which it was not before. An
invitation is addressed to an email; joining an organization on the
strength of one should require proving the address is yours, or anyone
could type a colleague's address at registration and walk in. The
profile page stays reachable without verification, so an address typed
wrong can still be corrected.

/dashboard is new, and is needed now. config('fortify.home') is the
fixed string '/dashboard', which Fortify redirects to on its own when an
already verified user opens the verification prompt or asks for another
mail. Every real dashboard lives under an organization, so that path was
a 404 waiting for the first person to click a stale link. It now
forwards to the current organization, or to onboarding.

.env.example pointed at MAIL_MAILER=log on port 2525. Nothing listens on
2525 here and mailpit is on 1025, so a fresh checkout would have buried
the verification link in a log file at the exact moment the link became
mandatory.

Verified end to end against Herd and mailpit: register, mail arrives over
SMTP, the link in it lands on onboarding.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@SourovCodes
SourovCodes merged commit 0397daf into main Sep 20, 2026
1 check passed
@SourovCodes
SourovCodes deleted the feat/require-a-verified-email branch September 20, 2026 11:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant