Skip to content

Email verification should work across devices #117

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

tnylea
Copy link
Contributor

@tnylea tnylea commented Apr 18, 2025

The current Verify Email functionality utilizes the EmailVerificationRequest here: https://github.com/laravel/framework/blob/12.x/src/Illuminate/Foundation/Auth/EmailVerificationRequest.php, which fails when a user clicks the verification email from a different device or browser.

This update removes the need for the user to be logged in when verifying their email. Instead, we check the id and hash directly from the signed URL. We use hasValidSignature() to make sure the link hasn’t been tampered with or expired, and then compare the hash to a SHA-1 version of the user’s email to confirm it’s legit.

With this update, the email verification flow is still secure and has a better user experience.

@taylorotwell, let me know if this looks good. I have PR's for this in the other starter kits but wanted to get your thoughts. Should I instead update the EmailVerificationRequest from inside the Laravel framework to work this way (https://github.com/laravel/framework/blob/12.x/src/Illuminate/Foundation/Auth/EmailVerificationRequest.php), or is it fine to add it in each starter kit?

Let me know 👍 Thanks!

@tnylea tnylea added the Approved Approved for merge label Apr 18, 2025
@tnylea tnylea requested a review from taylorotwell April 18, 2025 14:10

$user = User::findOrFail($id);

if (! hash_equals($hash, sha1($user->getEmailForVerification()))) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be calling sha1 directly? Isn't there a Laravel abstraction for this? Also bearing this in mind.

event(new Verified($user));
}

// Always log the user in, regardless of verification status
Auth::login($user);
Copy link

@Synchro Synchro Apr 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes verification links a little dangerous – anyone who gets hold of one is instantly logged in with no other auth required. Usual practice after verification is to require a login, which solves that problem, though at a mild UX expense.
/cc @valorin

Note that this additional login doesn't really apply for OAuth, since verification and login are essentially the same thing there.

@pablo-gonzalez-helpwan
Copy link

pablo-gonzalez-helpwan commented Apr 23, 2025

Hello!

Have you considered that many email servers now (and have for many years) click links in emails to prevent phishing? (Example: https://old.reddit.com/r/webdev/comments/1i861ej/ms_and_other_antivirus_now_click_on_links_in/ but if you search around there are many other articles).

This would cause users to be verified automatically without user action. Which may be ok if you only care that the email exists, but otherwise I'm not sure it should be the default action.

@Synchro
Copy link

Synchro commented Apr 23, 2025

To be fair, that's all we are actually interested in when verifying, but it does also suggest that logging the user in in response to a verification link is a bad idea, as I mentioned in a review comment. This is the same problem that faced email subscription headers, where one-click unsubscribe links were automatically opened by mail filters, automatically unsubscribing everyone, leading to the introduction of the list-unsubscribe-post header.

@valorin
Copy link

valorin commented Apr 24, 2025

I need to have a deeper look into this, but I wanted to quickly respond to this:

This would cause users to be verified automatically without user action. Which may be ok if you only care that the email exists, but otherwise I'm not sure it should be the default action.

You shouldn't blindly accept email verification if the link is clicked because then you're only verifying if the email exists, rather than if the owner of the email address signed up for the service. This means you could sign up for a service, submit the email address of someone whose email provider automatically clicks link, and then you've got a verified account with their email address.

You need to ensure the email verification click is intentional, to prevent abuse of automated link clicking.

@Synchro
Copy link

Synchro commented Apr 24, 2025

You need to ensure the email verification click is intentional, to prevent abuse of automated link clicking.

That's true. In that case it means that opening the verification link by any means is insufficient; we need to display a page with a verify button on it which must be clicked to verify the account. Of course this means that we are still subject to very stupid mail filters (which sadly exist) that also click every link on a destination page to check it for malware, but there's only so much we can do!

Even so, that verification step doesn't obviate the need to require a post-verify login. While this UX may seem a little clunky, it's an extremely common pattern, and not as user-hostile as many auth flows (e.g. AWS and eBay).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Approved Approved for merge
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants