0

I am designing a REST API that allows users to register and authenticate with an email address and a password. Before being able to authenticate, I want to ensure that the user owns the email address they are using, so I send them a verification email with a token. My issue is that the token can expire, so I need a way to resend the verification email, and this is where I'm having problems.

Basic Setup

I have two models: EmailAddress and EmailConfirmation. EmailAddress stores the address, the user who owns it, and a boolean indicating if it's verified. EmailConfirmation contains a key used to verify a specific email address.


Approach 1

When a user registers, I create a new user and an EmailAddress instance owned by that user. A confirmation is sent to the email. If the confirmation is expired, the user can request a new confirmation.

The issue with this approach is multiple users can sign up with the same email address until someone verifies that address. If there are multiple EmailAddress instances with the same address, how do I determine which of them requested the verification to be resent?

Approach 2

When a user registers, we create an EmailAddress instance for the address, but don't assign a user to it. If additional users register with the same address, we don't create new instances. Instead, the EmailConfirmation model has an attached user and we set ownership of the address based on which key is used to verify it.

The issue with this approach is how to determine which user requested a new confirmation email.


Further Notes

  • User's shouldn't be able to prevent others from taking an email address. For example: If Fred registers with bob@example.com but can't verify it, Bob should still be able to register with bob@example.com and verify it.

I am happy to take a different approach than the ones described above, but those are the only ones I can think of. Any help would be greatly appreciated.

Edit: I believe the original title of my question was a little misleading. I'm more interested in solving the issues with resending a confirmation email. However, if this involves restructuring my approach to the whole system I'm happy to do that.

Edit 2: To further clarify my question, I'm talking about the case where a user registers and gets sent a verification email. For whatever reason, they don't use the token they've received in time and it expires, or they simply lose the email. They then request a new confirmation email be sent by submitting their email address.

I believe the problem boils down to determining which user requested a new email confirmation without any knowledge of the initial verification token.

  • 1
    Another approach is to defer this to an OAuth 2 or OpenID provider. – Erik Eidt Sep 21 '17 at 22:51
  • Why is a user requesting another registration email because their first one expired any different from a new user attempting to register with that email address? – Jack Sep 22 '17 at 4:58
  • 1
    It is not totally clear what your problem is. Are you asking how to resend a mail with a token and validate this token, or are just just asking how to represent the email and confirmation in a REST API resource model? Because these are really separate issues. If you didn't have a REST API you would still need to send the mail and verify the token. – JacquesB Sep 22 '17 at 6:03
  • 1
    There a crap ton of obvious ways to do this, @ChathanDriehuys. Quit dangling the carrot; my answer is deleted. – Brandon Arnold Sep 22 '17 at 21:09
  • @BrandonArnold I'm sorry you feel that way. I genuinely had no idea if what I wanted was possible with the amount of information provided. I see now that it is not, and I appreciate you taking the time to try to help. – Chathan Driehuys Sep 25 '17 at 13:59
2

For either approach, the easiest way to resolve this is to embed user information into the token. That way the token contains enough information on its own to validate the user and their email address.

An overly simplistic version of this might be to attach the user id to the nonce you send. For example, if Bob is user number 1234, the email might contain the token 1234000574628, where 574628 is the nonce you use to confirm that the user actually read the email.

  • I appreciate the response. As I said in my comment on the other answer, how does this solve the problems I mentioned with resending a confirmation? – Chathan Driehuys Sep 22 '17 at 1:08
  • @ChathanDriehuys: It solves the problem by uniquely identifying which user is requesting a resend. – Robert Harvey Sep 22 '17 at 2:16
  • @ChathanDriehuys Your issue is with determining which user account was being authenticated. In both of your examples, the user is known at the time of sending the email, and by encoding the user id into the confirmation token, that userID can be retrieved at authentication time. – Cort Ammon Sep 22 '17 at 3:06
  • @CortAmmon I've further clarified my question. I still don't understand how to associate the email address with the correct user without any knowledge of the original token. My understanding of your answer is that we use the information in the token to associate the correct user, but the second generated token has no knowledge of the first one. – Chathan Driehuys Sep 22 '17 at 4:36
  • 2
    @ChathanDriehuys Ahh I see the issue. It easy to solve. When they say they lost the token, have them enter a username instead of an email (or enter both). Any user that is trying to log in knows their username. Have them provide it to you. Then you can put that userid into the token. – Cort Ammon Sep 22 '17 at 5:46
1

I understand the question is purely about the REST API modelling, and not about how to actually generate the token, right?

Each time you generate a confirmation mail, you generate a confirmation record identifying a user, an email address, an expiration time and a unique unguessable token. When an confirmation is verified by clicking the token URL, the email address is verified for this user (and you have to check no other user verified the same email address in the meantime!).

It is not clear from you description if the same user can change email address. If the email address cannot be changed, then it is a property of the user, not the confirmation, since all confirmation for the same use would be for the same address.

As for modelling this in REST, I think you answer it yourself. The Confirmation need to be associated to a user. So you have something like /users/1234/confirmation/567 representing a confirmation mail, and you verify this by posting the token to it.

But note that you shouldn't expose this resource URL directly in the confirmation mail. The token and URL should be opaque and not reflect the resource URL of anything, since it should not be possible to guess or reverse-engineer verification URL's.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.