6

I've successfully build simple registration system using ASP.NET Identity 2.2.1.
I only needed API so I've build simple controller that allows creating account using route account/create, when account is created user receives SMS message with token needed to confirm his phone, same time I'm generating link that is needed to verify email address and I'm sending it to user email address. Then user need to input token from SMS (do request to account/confirm_phone) and click link in email he receives.
These two actions are needed to activate account, this part works fine.

I have requirement to change email link to email token, similar to one used to confirm phone number, so instead of clicking link user will have to input that token (request to account/confirm_email)

Method GenerateEmailConfirmationTokenAsync returns very long code that becomes part of link send in email, I'd like it to return 6 digit token.

I can't reuse GeneratePhoneConfirmationTokenAsync because this will generate same token as send via SMS.

I've searched over the internet and I wasn't able to find any information how to customize token generated by GenerateEmailConfirmationTokenAsync method.

Is it possible to configure that token generator so it will return 6 (or any configurable length) digit code that I can use to confirm user email address.

I'm aware that link send to user is better option, but as I wrote this is requirement I got and I can't change that.

10
  • So you are sending confirmation to both email and a mobile? Commented Oct 26, 2016 at 13:57
  • @trailmax yes, but these shoul be different codes. One for confirming phone number, second for email address. Commented Oct 26, 2016 at 14:13
  • I see. In one of the systems I've done similar thing. But I've stored a confirmation code with expiry time. And then compared user-provided to the stored one. You can do the same - store both codes with expiry timestamp and generate the codes yourself (not with Identity). Commented Oct 26, 2016 at 14:16
  • @trailmax thanks for hint. I was thinking about similar thing, but I've looked at source code of Identity and I found code responsible for generating phone number tokens. I was thinking about using similar thing but with email as modifier instead of phone number. I' like to avoid those two fields in database and generate that token from salt and email address, but I don't know how to set lifetime of that nee token. Commented Oct 26, 2016 at 14:23
  • 1
    You are on the good track then. I recall I've seen lifetime property somewhere in token generation code. Keep digging. I can have a look on that tonight if you still need it Commented Oct 26, 2016 at 14:36

2 Answers 2

2

I've had a good careful look on this and I'm afraid you are out of luck.

ASP.Net Identity framework (v2.2.x) has 2 token providers: DataProtectorTokenProvider and EmailTokenProvider.

DataProtectorTokenProvider gives you a very long token that is no good for manual copy-paste. But it allows to set a timespan: DataProtectorTokenProvider.TokenLifespan. By default it is set to 1 day - see constractor of that class. Token generated by this provider contains current UTC time, userId, security stamp and additional string purpose which can be anything, but on validation this must be the same as on generation. This is usually is "email" or "phone". Then all that data is encrypted by IDataProtector. Hence you get very long and nasty looking string.

You can have variable life span for different tokens, but you'll have to do a magic dance around setting correct value in DataProtectorTokenProvider.TokenLifespan before validating every particular token.

Another option EmailTokenProvider - this class is inherited from TotpSecurityStampBasedTokenProvider. This token provider generates One Time Password (OTP) based on Rfc6238. This token is time-sensitive and becomes invalid once the time-window is passed. See section 5.2 for more details. Default time-window for identity is set to be 3 minutes. See line:

private static readonly TimeSpan _timestep = TimeSpan.FromMinutes(3); 

in Rfc6238AuthenticationService. But Rfc6238 says that implementation must allow for more than 1 time-window. Given implementation allows total of 6 minutes token lifespan. And there is no way to change that without implementing your own Rfc6238.

So Identity does not provide you the means to implement your requirement - you'll have to generate tokens yourself and store them with the timestamp. Perhaps only one of them - short token with longer lifespan. The default SMS implementation is already short-lived and short for manual entry.

Sign up to request clarification or add additional context in comments.

11 Comments

Thank You for Your investigation. I was thinking about using similar approach as GenerateChangePhoneNumberTokenAsync and VerifyChangePhoneNumberTokenAsync in UserManager, but this would give me 3 minute tokens, so I'll probably copy Rfc6238AuthenticationService code and modify it to be able to specify timestep. One think I'm not sure is if I change _timespan from 3 minutes to lets say 4 yours token will be valid 4 hours from now or 8 hours? It's bit confusing in Your answer. Could You please clarify that a bit?
I've modified that rfc6238 authentication service you mentioned. I've added parameter for specifying time step, but because this all is quite new to me I'm not sure if I did everything correctly. Could You please take a look at it? gist.github.com/Misiu/c2822de165e347ede71750ab20b6c12a
@Misiu Sorry, I don't know enough about this algorithm. Here it is actually validated, but I've no idea what happens there with -2..+2. The only way to check would be to write unit tests and replace DateTime.UtcNow here with a parameter that you can change to simulate changed time.
@Misiu I've used code from this answer to do unit tests where DateTime.UtcNow was used. Part of my project - for reference. And this is how it is used in tests.
Thank You for Your time and help. I've tested my token implementation. Problem is algorithm - it use time windows, not specific time as expiration date. If I generate token just couple of minutes after midnight and set it validity as 24 hours it will be valid 2 days (till end of current day and next), same with 1 hour tokens, they are valid till end of current hour + one hour. I'll probably will edit my user store and add 3 extra fields, one for token hash and second for token expiration date and third for number to ties. This way I'll be sure expiration time is correct.
|
0

This may be not so easy, but the way to do it would be to write your own IUserTokenProvider implementation.

public class CustomTokenProvider : IUserTokenProvider<ApplicationUser, string> { public Task<string> GenerateAsync(string purpose, UserManager<ApplicationUser, string> manager, ApplicationUser user) { ??? } public Task<bool> IsValidProviderForUserAsync(UserManager<ApplicationUser, string> manager, ApplicationUser user) { ??? } public Task NotifyAsync(string token, UserManager<ApplicationUser, string> manager, ApplicationUser user) { ??? } public Task<bool> ValidateAsync(string purpose, string token, UserManager<ApplicationUser, string> manager, ApplicationUser user) { ??? } } 

and then in your ApplicationUserManager's Create method:

manager.UserTokenProvider = new CustomTokenProvider<ApplicationUser, string>(); 

1 Comment

Thank You for answer, but I don't want to change UserTokenProvider, because I'd like to leave reset password functionality as it is. I only need other way of email confirmation - via token.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.