5

Is there any ways to see the generated Password Reset Tokens in ASP.NET Identity 2 ?
My assumption is there should be a dictionary in TokenProviderwhich holds those tokens in memory.

Update:
Here is a similar question ResetPassword Token How and where is it stored?

"Tokens are generated using the SecurityStamp and validating against the SecurityStamp and not storing anywhere in database or local file storage."

But still the question is how does the framework know when a token is expired without storing it?

Community
  • 1
  • 1
Ehsan
  • 357
  • 5
  • 22
  • The token provider allows you to create the reset token. You will have to manually store it with the associated user. – Nkosi Jan 21 '16 at 19:36
  • @Nkosi I am able to generate the token and use it , however I need to see the already generated tokens in the memory for debugging purposes. I think it is automated as part of the library and I don't need to store it anywhere – Ehsan Jan 21 '16 at 19:57
  • The token providers do not persist the generated codes to memory. – Nkosi Jan 21 '16 at 20:00
  • @Nkosi Then, how it validates the token when I call the ResetPassword using the generated token? – Ehsan Jan 21 '16 at 20:07
  • 2
    Potential duplicate? According to the answer on this question the value is not stored anywhere. It is calculated off of the Security Stamp... http://stackoverflow.com/questions/27039953/resetpassword-token-how-and-where-is-it-stored – Sam Jan 21 '16 at 20:37
  • @Sam but how do we know when a token is expired if it isn't stored any where? – Ehsan Jan 21 '16 at 20:48
  • 1
    @EhsanEsl The token provider has a Timespan that is uses to calculate if the generated token has expired. – Nkosi Jan 21 '16 at 20:51
  • @Nkosi So if you generate two tokens for the same user, both of them are valid but they both expire at the same time, is that a right assumption? – Ehsan Jan 21 '16 at 20:56
  • @EhsanEsl it depends on the token purpose. I say this according to the . [source code on Github](https://github.com/aspnet/Identity/blob/dev/src/Microsoft.AspNet.Identity/TotpSecurityStampBasedTokenProvider.cs#L34) – Nkosi Jan 21 '16 at 21:04

2 Answers2

4

Token is not stored in Memory instead it is generated on the fly from the given SecurityStamp. Token is basically an Unicode encoded string.

You can see UserManager.cs's CreateSecurityTokenAsync method.

internal async Task<byte[]> CreateSecurityTokenAsync(TUser user)
{
    return Encoding.Unicode.GetBytes(await GetSecurityStampAsync(user));
}
Win
  • 61,100
  • 13
  • 102
  • 181
1

As mentioned by one of the commenters

"Tokens are generated using the SecurityStamp and validated against the SecurityStamp and not stored anywhere in database or local file storage."

which was quoted from ResetPassword Token How and where is it stored?

Here is how I was able to persist the tokens for later use.

I am assuming you are using the default project template.

In ApplicationUser create properties to store the tokens.

public class ApplicationUser : IdentityUser {
    public string EmailConfirmationToken { get; set; }
    public string ResetPasswordToken { get; set; }
}

Create the respective columns in the AspNetUsers table.

I then updated the ApplicationUserManager class as follows.

    public override async System.Threading.Tasks.Task<string> GenerateEmailConfirmationTokenAsync(string userId) {
        /* NOTE:
         * The default UserTokenProvider generates tokens based on the users's SecurityStamp, 
         * so until that changes(like when the user's password changes), the tokens will always be the same, and remain valid. 
         * So if you want to simply invalidate old tokens, just call manager.UpdateSecurityStampAsync().
         */
        //await base.UpdateSecurityStampAsync(userId);

        var token = await base.GenerateEmailConfirmationTokenAsync(userId);
        //associate the email token with the user account
        if (!string.IsNullOrEmpty(token)) {
            var x = await FindByIdAsync(userId);
            x.EmailConfirmationToken = token;
            x.EmailConfirmed = false;

            await UpdateAsync(x);
        }

        return token;
    }

    public override async System.Threading.Tasks.Task<string> GeneratePasswordResetTokenAsync(string userId) {
        var token = await base.GeneratePasswordResetTokenAsync(userId);
        if (!string.IsNullOrEmpty(token)) {
            var x = await FindByIdAsync(userId);
            x.ResetPasswordToken = token;
            await UpdateAsync(x);
        }
        return token;
    }

    public override async System.Threading.Tasks.Task<IdentityResult> ConfirmEmailAsync(string userId, string token) {
        var result = await base.ConfirmEmailAsync(userId, token);
        if (result.Succeeded) {
            var x = await FindByIdAsync(userId);
            x.EmailConfirmationToken = null;
            await UpdateAsync(x);
        }
        return result;
    }

    public override async System.Threading.Tasks.Task<IdentityResult> ResetPasswordAsync(string userId, string token, string newPassword) {
        var result = await base.ResetPasswordAsync(userId, token, newPassword);
        if (result.Succeeded) {
            var x = await FindByIdAsync(userId);
            x.ResetPasswordToken = null;
            await UpdateAsync(x);
        }
        return result;
    }

The above changes allowed me to persist the tokens between sessions.

Community
  • 1
  • 1
Nkosi
  • 235,767
  • 35
  • 427
  • 472
  • That's is not the answer because I already know how to use the library but I need to know how and where the TokenProvider stores the Tokens for PasswordReset – Ehsan Jan 21 '16 at 19:58
  • 2
    I find this helpful from extensibility stand point, we hate having to ask our users to enter email address (yet again) in order to "use" the reset link. So storing the last created code and looking up a user by it helps a lot. Note: we might lose some points at the hands of die-hard security tsars, but earn them from UX pros (a balancing act). – timmi4sa Feb 21 '18 at 03:08
  • But you don't need to ASK the user for their email - you just embed it in the link you have them click on. So you email someone the reset URL containing `?token=TOKEN&username=EMAIL`. And since that's the email you're sending it to then I think the czars would be ok with it. – Simon_Weaver Apr 26 '19 at 23:51