2

I'm trying to create a function that will reset the users password when requested in asp .net core 3.1 using Identity

So far whenever the function GeneratePasswordResetTokenAsync is called it returns the token as expected

// Generate Reset Token.
var token = await userManager.GeneratePasswordResetTokenAsync(appIdentityUser);

CfDJ8OHfQcgoimpKvwzwyqjhuwNuJOOwPXPw2F9wg5t7HNMc+YZbnJn1n8cVwBmq/yYV4edV8wl+p6QHSOv/gtW6yat7iuD9v9dBqTmw+Lie2UY9MDLsMEu+GQWaRlUWEH70FoyGqUUcU1/Tzk6tmBvz8cRPlx2KTnJfVc73e1XMZg69pUk58XRuKzRTgwyw/70aSSy6oh1LgDj4g1OqPRSqsgKaPh1vUnMThYb0GwqovqGZoU37N5COem4RmYFn4uVIEQ==

For testing purposes I am now calling the ResetPasswordAsync within the same method to see if the password will reset

NOTE user is found.

// Find User.
AppIdentityUser user = await userManager.FindByEmailAsync(appIdentityUser.Email);

// Attempt To Reset The Password To someRealL0ngP@ssW0rd
IdentityResult resetPassword = await userManager.ResetPasswordAsync(user, token, "someRealL0ngP@ssW0rd");

Unfortunately I receive this error Failed : InvalidToken

Here is AddIdentity that is in the startup file along with the DbContext.

...
// Create The DbContext.
services.AddDbContext<AppIdentityDbContext>(options =>
{
    options.UseSqlServer(Configuration.GetConnectionString("AussieFoods2ULocal"));
});

// Identity User. Plus Password Complexity For Easy Testing.
services.AddIdentity<AppIdentityUser, IdentityRole>(options =>
{
    options.Password.RequireDigit = false;
    options.Password.RequiredLength = 5;
    options.Password.RequireNonAlphanumeric = false;
    options.Password.RequiredUniqueChars = 0;
    options.Password.RequireUppercase = false;
    options.User.RequireUniqueEmail = true;
})
.AddEntityFrameworkStores<AppIdentityDbContext>()
.AddDefaultTokenProviders();

 // Paths For The Identity
 services.ConfigureApplicationCookie(options =>
 {
     options.LoginPath = "/Security/SignIn";
     options.AccessDeniedPath = "/Security/AccessDenided";
 });
    
 services.Configure<DataProtectionTokenProviderOptions>(options =>
 {
      options.TokenLifespan = TimeSpan.FromHours(2);
 });

 services.AddAuthentication();
 ...

I'm not sure if this has anything to do with it, but I did create a custom class that inherits from IdentityUser

public class AppIdentityUser : IdentityUser
{
    [Required]
    public string FirstName { get; set; }
    [Required]
    public string LastName { get; set; }
    
    [EmailAddress]
    [Required]
    public override string Email { get => base.Email; set => base.Email = value; }

    public string Address1 { get; set; }
    public string Address2 { get; set; }
    public string Suburb { get; set; }
    public string City { get; set; }
    public string PostCode { get; set; }
}
2
  • Why are you using FindByEmailAsync when you already have appIdentityUser? Aren't user and appIdentityUser the same? Commented Jun 26, 2020 at 9:34
  • I’m calling it because the userid is missing for when you call the ResetPasswordAsync Commented Jun 26, 2020 at 9:37

4 Answers 4

4

If you're sending an email with something like Sendgrid and using HtmlContent to link the token, I found that all instances of "+" are replaced with " " for whatever reason.

The simple solution to this is to do a string.Replace.

var decodedToken = token.Replace(" ", "+");
Sign up to request clarification or add additional context in comments.

Comments

2

To test your Startup.cs config, I quickly made a new project. The ConfigureServices was the same as yours with services.AddControllersWithViews(); at the end and Configure looks liked below.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
        app.UseDeveloperExceptionPage();
    else
    {
        app.UseExceptionHandler("/Home/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseRouting();
    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllerRoute(
            name: "default",
            pattern: "{controller=Home}/{action=Index}/{id?}");
    });
}

I then created an action to create a user and reset the password. Below is the action to reset the password.

public async Task ResetPassword()
{
    var user = await UserManager.FindByEmailAsync("[email protected]");
    var token = await UserManager.GeneratePasswordResetTokenAsync(user);
    var result = await UserManager.ResetPasswordAsync(user, token, Guid.NewGuid().ToString());
}

This returns a successful result.

Comments

1

You can create an custom token provider to generate PasswordResetToken like this

public class ResetPasswordTokenProvider<TUser> : DataProtectorTokenProvider<TUser> where TUser : class
{
    public ResetPasswordTokenProvider(IDataProtectionProvider dataProtectionProvider,
        IOptions<ResetPasswordTokenProviderOptions> options)
        : base(dataProtectionProvider, options)
    {
    }
}
public class ResetPasswordTokenProviderOptions : DataProtectionTokenProviderOptions
{
    public ResetPasswordTokenProviderOptions()
    {
        Name = "ResetPasswordDataProtectorTokenProvider";
        TokenLifespan = TimeSpan.FromDays(1);
    }
}

Then register custom token provider in AddIdentity and services

services.AddIdentity<AppIdentityUser, IdentityRole>(options =>
{
    options.Password.RequireDigit = false;
    options.Password.RequiredLength = 5;
    options.Password.RequireNonAlphanumeric = false;
    options.Password.RequiredUniqueChars = 0;
    options.Password.RequireUppercase = false;
    options.User.RequireUniqueEmail = true;
    //NOTE THIS
    options.Tokens.ProviderMap.Add("ResetPassword", new TokenProviderDescriptor(typeof(ResetPasswordTokenProvider<AppIdentityUser>)));
    options.Tokens.PasswordResetTokenProvider = "ResetPassword";

})
.AddTokenProvider<ResetPasswordTokenProvider<AppUserIdentity>>("ResetPassword")//<--NOTE THIS
.AddEntityFrameworkStores<AppIdentityDbContext>();

Comments

0

I encountered the issue while configuring MFA during first login, where I offered users the choice of authentication methods: Email, SMS, or Authenticator App. The issue occurred only when the Authenticator App was selected.

After investigation, we found that configuring the Authenticator App resets the SecurityStamp for the user in the AspNetUsers table. So, any tokens generated before this reset are rendered invalid.

To resolve the issue, we adjusted our flow as follows:

Check if the Authenticator App is configured: Before proceeding with actions like password reset, confirm whether the user has completed MFA setup with the Authenticator App.

Generate a new token: After the Authenticator App is set up, create a fresh token and validate the user with this token.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.