Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ResetPassword Token How and where is it stored?

I've been trying to understand how the reset password & account confirmation works in ASP.NET Identity. I'd just like to know if the Tokens are being stored and if so, where?

The links I receive when I'm using the password reset feature look something like this

http://localhost:1470/Account/ResetPassword?userId=a8b1389c-df93-4dfc-b463-541507c1a4bc&code=yhUegXIM9SZBpPVbBtv22kg7NO7F96B8MJi9MryAadUY5XYjz8srVkS5UL8Lx%2BLPYTU6a6jhqOrzMUkkMyPbEHPY3Ul6%2B%2F0s0qQvtM%2FLLII3s29FgkcK0OnjX46Bmj9JlFCUx53rOH%2FXMacwnKDzoJ1rbrUyypZiJXloIE50Q6iPuMTUHbX9O%2B3JMZtCVXjhhsHLkTOn9IVoN6uVAOMWNQ%3D%3D

My guess is that the tokens are stored in the link itself since I cannot find any trace of it anywhere else. Maybe someone knows for sure?

like image 585
AndreasElffors Avatar asked Nov 20 '14 12:11

AndreasElffors


People also ask

What is a token when resetting a password?

For security reasons, passwords are never sent out across the Internet. Instead a token will be sent to your email instead. A token is a one-time generated link that contains numbers and letters that'll allow you to reset your password. It cannot be reused and is only valid for seven days.

How long should a password reset token last?

By default, password reset tokens expire after one hour. You may change this via the password reset expire option in your config/auth. php file. The default expire is 60 minutes.


2 Answers

As I mentioned in the comment

"Tokens are generated using the SecurityStamp and validating against the SecurityStamp and not storing anywhere in database or local file storage. If you update the SecurityStamp, then previous tokens are no longer valid."

like image 161
DSR Avatar answered Oct 22 '22 21:10

DSR


@DSR is correct but I would like to add some information to this as well.

If you have set up a Web project with Individual User Accounts go to:

App_Start -> IdentityConfig.cs

There you will see code like this:

var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
    manager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"));
}

The description for DataProtectorTokenProvider<TUser, TKey> gives the information:

Represents a token provider that uses an IDataProtector to generate encrypted tokens based off of the security stamp.

https://learn.microsoft.com/en-us/previous-versions/aspnet/dn613280(v%3dvs.108)

We can however try to dig a bit deeper how it really works. The token verification will fail if different Application Pool Identities are used for creating and validating a token on a single server. This points to that the actual protection mechanism would look something like this:

System.Security.Cryptography.ProtectedData.Protect(userData, entropy, DataProtectionScope.CurrentUser);

Given that it works if all sites use the same Application Pool Identity points to this as well. Could also be DataProtectionProvider with protectionDescriptor "LOCAL=user". It should have worked with different Application Pool Identities if LOCAL=machine was set.

new DataProtectionProvider("LOCAL=user")

https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.dataprotector?view=netframework-4.7.2

https://learn.microsoft.com/en-us/uwp/api/windows.security.cryptography.dataprotection.dataprotectionprovider

dataProtectionProvider is of type IDataProtectionProvider.

It is injected in Startup.Auth.cs like this:

app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);

CreatePerOwinContext is located in the assembly Microsoft.AspNet.Identity.Owin -> AppBuilderExtensions.cs. Both ASP.NET Identity and ASP.NET Core Identity are open source and can be viewed at GitHub.

public static IAppBuilder CreatePerOwinContext<T>(this IAppBuilder app,
    Func<IdentityFactoryOptions<T>, IOwinContext, T> createCallback,
    Action<IdentityFactoryOptions<T>, T> disposeCallback) where T : class, IDisposable
{
    if (app == null)
    {
        throw new ArgumentNullException("app");
    }
    if (createCallback == null)
    {
        throw new ArgumentNullException("createCallback");
    }
    if (disposeCallback == null)
    {
        throw new ArgumentNullException("disposeCallback");
    }

    app.Use(typeof (IdentityFactoryMiddleware<T, IdentityFactoryOptions<T>>),
        new IdentityFactoryOptions<T>
        {
            DataProtectionProvider = app.GetDataProtectionProvider(),
            Provider = new IdentityFactoryProvider<T>
            {
                OnCreate = createCallback,
                OnDispose = disposeCallback
            }
        });
    return app;
}

https://github.com/aspnet/AspNetIdentity/blob/master/src/Microsoft.AspNet.Identity.Owin/Extensions/AppBuilderExtensions.cs

https://archive.codeplex.com/?p=aspnetidentity#src/Microsoft.AspNet.Identity.Owin/Extensions/AppBuilderExtensions.cs

app.GetDataProtectionProvider() is in turn located in assembly Microsoft.Owin.Security that is also Open Source.

public static IDataProtectionProvider GetDataProtectionProvider(this IAppBuilder app)
{
    if (app == null)
    {
        throw new ArgumentNullException("app");
    }
    object value;
    if (app.Properties.TryGetValue("security.DataProtectionProvider", out value))
    {
        var del = value as DataProtectionProviderDelegate;
        if (del != null)
        {
            return new CallDataProtectionProvider(del);
        }
    }
    return null;
}

https://github.com/aspnet/AspNetKatana/blob/release/src/Microsoft.Owin.Security/DataProtection/AppBuilderExtensions.cs

We can also see that CreateDataProtector has a fallback to the implementation DpapiDataProtectionProvider.

private static IDataProtectionProvider FallbackDataProtectionProvider(IAppBuilder app)
{
    return new DpapiDataProtectionProvider(GetAppName(app));
}

When reading about DpapiDataProtectionProvider(DPAPI stands for Data Protection Application Programming Interface) the description says:

Used to provide the data protection services that are derived from the Data Protection API. It is the best choice of data protection when you application is not hosted by ASP.NET and all processes are running as the same domain identity.

The Create method purposes are described as:

Additional entropy used to ensure protected data may only be unprotected for the correct purposes.

The protector class itself then looks like this:

using System.Security.Cryptography;

namespace Microsoft.Owin.Security.DataProtection
{
    internal class DpapiDataProtector : IDataProtector
    {
        private readonly System.Security.Cryptography.DpapiDataProtector _protector;

        public DpapiDataProtector(string appName, string[] purposes)
        {
            _protector = new System.Security.Cryptography.DpapiDataProtector(appName, "Microsoft.Owin.Security.IDataProtector", purposes)
            {
                Scope = DataProtectionScope.CurrentUser
            };
        }

        public byte[] Protect(byte[] userData)
        {
            return _protector.Protect(userData);
        }

        public byte[] Unprotect(byte[] protectedData)
        {
            return _protector.Unprotect(protectedData);
        }
    }
}

https://learn.microsoft.com/en-us/previous-versions/aspnet/dn253784(v%3dvs.113)

like image 8
Ogglas Avatar answered Oct 22 '22 22:10

Ogglas