I'm implementing a user email confirmation feature. I would like any and all feedback on my approach so far.
I've added a verify_token and a verified field to my users table.
I've signed up to Mandrill and I've tested that I can send emails.
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.
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.
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.
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?
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 ).
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.