Reputation: 1656
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; }
}
Upvotes: 2
Views: 3398
Reputation: 11
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.
Upvotes: 0
Reputation: 97
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(" ", "+");
Upvotes: 4
Reputation: 5861
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>();
Upvotes: 1
Reputation: 4839
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.
Upvotes: 2