Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

mstnorris's avatar

Best practice for email confirmation

I'm implementing a user email confirmation feature. I would like any and all feedback on my approach so far.

  1. I've added a verify_token and a verified field to my users table.

  2. I've signed up to Mandrill and I've tested that I can send emails.

  3. In the email I have provided a link to @example.com&token=AH37H8DN87DNK8Y9" target="_blank">http://mydomain.com/verify?email=myemail@example.com&token=AH37H8DN87DNK8Y9 <- a random 16 character string.

Should I include the email in the above URL, or is the fact that a 16 character random string is enough to distinguish and identify an email. I must note that I am only using the characters HMN34P67R9TWCXYF as per this thread.

I started this thread with more questions/ideas in my head but I can't think of them right now. I will update it as I go.

I haven't implemented the actual logic to handle the verification route yet. Any pointers to accomplish this will be greatly appreciated. If I have overlooked anything or you'd do it differently, please do say.

0 likes
8 replies
RachidLaasri's avatar

I would personally exclude the email, and then listen for a GET request on the verify route.

Do a quick SQL search for a user with active = 0 and verify_token equal to request->get('token'), if it does exist i'll update the active field to 1 and display a success message, if not either redirect to home or show an error message.

RokSiEu's avatar

Hi, this is my first post here on Laracasts forum and I hope that it will be helpful.

Routes.php

Route::get( 'user/confirm/{code}',         'UserController@confirm');

UserController@confirm

$user = User::where('verify_token','=',$code)->first();
if($user)
{
    $user->verified = true;
    $user->save();
}

What I would recommend is that every verify_token you generate has expiration date/time setted up, this way we can eliminate never ending brute force attempts.

1 like
mstnorris's avatar

So I have written this method in my UsersController

It responds to the route /verify/{verify_token} and it works!

    public function verify($verify_token)
    {
        $user = User::where('verify_token', $verify_token)->whereVerified(0)->first();
        if ( $user ) {
            $user->verified = 1;
            $user->save();
            return view('emails.verified_email');    
        } else {
            // user has either been verified or token doesn't exist
        }
    }

But, as the comment in the code above suggests, I would like to acknowledge whether or not a user has already been verified. Should I remove the verify key? Or should I keep it and say that they're already verified?

@RokSiEu thank you. How would you and @RachidLaasri suggest I implement an expiration date on the verify token? What would I need to consider if it did expire, how would I go about generating a new one and preventing that from being abused?

RachidLaasri's avatar

Just set the verify_token to null too.

public function verify($verify_token)
{
    $user = User::where('verify_token', $verify_token)->whereVerified(0)->first();
    if ( $user ) {
        $user->verified = 1;
        $user->verify_token = null;
        $user->save();
        return view('emails.verified_email');    
    } 

    // do something else
}

Then if the first "if statement" isn't true, you can show an error message saying that code doesn't exist or it's expired. But, if you wanna be more explicit, you can create a table :

  • users_verification
    • id
    • email
    • verify_token
    • expiration_date

Then, you will check if the token provided in the GET request exist in the database and the expiration date is >= to now, if yes update the active field of the account that has the same email. if not, show a message depending on the error ( either token doesn't exist or is expired ).

2 likes
JeffreyWay's avatar

I'll do a video on email verification. Thought we had one, but guess no.

15 likes
jekinney's avatar

I set the verification code to null. I then utilize it again for password resets via email. You could also then check if a code exists on login to notify the user a password reset attempt was made and either change password or confirm it was them but remembered later. Either way set the code back to null.

tundeopaleye's avatar

@JeffreyWay Could you also please do a video that integrates Socialite in addition to the email confirmation of regular email signup?

tgif's avatar

@JeffreyWay yes thank you Jeffrey. That would make a great addition to an already great library of vids.

Please or to participate in this conversation.