DEV Community

Rui Figueiredo
Rui Figueiredo

Posted on • Edited on • Originally published at blinkingcaret.com

Rethinking email confirmation

We are very accustomed to how user registration works online. Enter your email, possibly pick a username, enter password, re-enter the password, get a confirmation email, click on a link in that email and voilà , account created.

Image of an envelope with a tick

However, there a are a few things that can go wrong.

Imagine this scenario. You want to create a new account with an hypothetical website named TheWebsite. You go through all the normal process but you mistype your email. Instead of john.smith@email.com you type jon.smith@email.com. But not all is bad, you typed your username correctly: johnsmith.

You wait for the confirmation email, but it doesn't arrive. It's 6pm, time to go home. You'll check it tomorrow, surely it will have arrived by then.

Tomorrow comes, and you forget that you didn't confirm your email, so you try to login, johnsmith + password. And it works, happy days.

Years pass, and you've been using TheWebsite happily. All is well until one day try to log in and you get an invalid login error. You try again, and again you get an error.

This can't be right you think. I always use johnsmith and the same password. You click the reset password link, but no reset email ever arrives.

This is a tragedy, all those pictures, and other stuff you've uploaded to TheWebsite. You can't access them anymore.

What happened?

Well, turns out that the person that actually owned the jon.smith@email.com was a kid that was curious and clicked the email from TheService asking him to verify his email address.

He himself forgot about this until a couple of years later when he heard about TheWebsite from some friends, and decided to try it. He tried to create an account and got an "account already exists" error. He used the password reset functionality and that's that. He now owns the original John Smith's account.

Although this story might seem convoluted it does happen. Brock Allen when talking about ASP.NET Identity Core in NDC described a situation where this had happened to someone from his family.

So what can be done?

A possible solution would be to ask for the password again when clicking the email confirmation link. This way Jon Smith wouldn't have been able to confirm John Smith's email, because he wouldn't know the password.

This introduces a new problem though. What about if between choosing a password an receiving the confirmation email you forget the password?

Well, then bye bye ability to create an account with that email. And goodbye cool username you managed to pick, won't be able to use those again.

Is there no hope?

There is. Here's a radical idea: don't let the user pick anything until s/he confirms the email address.

The process would be:

  • Click Sign Up
  • Enter email
  • Tell user to check email and click on the email confirmation link
  • User clicks on email confirmation link and is taken to a page where he enters
    • username (if required, some websites only require an email)
    • password
    • retype password
  • Done

It's like saying I'll only talk to you when you confirm you own that email address. After that you can tell me what you want.

Trying to create an actual implementation

Last week I wrote a step by step guide on how to create a web application from scratch that is able to deal with user registration using email confirmation.

It was while researching ASP.NET Identity Core that I saw that NDC video with Brock Allen and learned about this problem.

I decided to try to implement this approach using ASP.NET Identity, but unfortunately couldn't get to a reliable implementation.

A scenario where the user picks an invalid password or a username that is already taken will leave the account for the user's email in limbo.

Here's a snippet that demonstrates the problem when using ASP.NET Identity:

[HttpPost]
public async Task<IActionResult> VerifyEmail(string id, string token, string username, string password, string repassword)
{
    var user = await _userManager.FindByIdAsync(id);

    var confirmEmailResult = await _userManager.ConfirmEmailAsync(user, token);
    if (!confirmEmailResult.Succeeded)
        throw new InvalidOperationException("Invalid token");      

    var addPasswordResult = await _userManager.AddPasswordAsync(user, password);
    if (!addPasswordResult.Succeeded)
    {
        addPasswordResult.Errors.ToList().ForEach(error => ModelState.AddModelError(string.Empty, error.Description));
        return View(); //goes back to the form where the user chooses the username and password
    } 
    //...
Enter fullscreen mode Exit fullscreen mode

If the user mistypes the passwords the email gets confirmed but the user has no password set (it is possible to create users without password in ASP.NET Identity). The token for confirming the email cannot be used again, so the user won't ever be able to set the password.

One could think that we should only confirm the email token after setting the user's password, unfortunately that does not work because the token becomes invalid (won't match the user) if you change any of the user's properties.

Also, we can't validate the email and then redirect the user to another page to pick a username and password. That other page could be abused by another party to take over the account simply by navigating to it before the user did.

So it seems there's no reliable way of doing this with ASP.NET Identity, at least without creating custom email confirmation tokens that are still valid if we set the user's password.

The idea is still valid though, however it's hard not to feel that we are fighting the framework while trying to do it.

Top comments (4)

Collapse
 
nektro profile image
Meghan (she/her)

Would this approach not be a perfect storm for spam/dos? Imagine a URL that ingests these email addresses, say https://thewebsite.com/api/signup_step1?email={insert}. What's stopping someone from downloading a list of 306 million email addresses and sending them all to that URL?

Collapse
 
dimpiax profile image
Dmytro Pylypenko

Hello 2005 approach.

Collapse
 
adelowo profile image
Lanre Adelowo

Just use Oauth2 please

Collapse
 
reeder29 profile image
Doug Reeder

Mozilla Pesona was am authentication system which addressed related concerns. It appears to have worked, but not achieved traction.