Aspnet Core Identity Role Update Error - .net-core

I am trying update exist role but i am getting error.
Code
private readonly RoleManager<IdentityRole> _roleManager;
public EditModel(RoleManager<IdentityRole> roleManager)
{
_roleManager = roleManager;
}
[BindProperty]
public IdentityRole IdentityRole { get; set; }
public async Task<IActionResult> OnGetAsync(string id)
{
if (id == null)
{
return NotFound();
}
IdentityRole = await _roleManager.FindByIdAsync(id);
if (IdentityRole == null)
{
return NotFound();
}
return Page();
}
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
try
{
await _roleManager.UpdateAsync(IdentityRole);//Error is occuring here.
}
catch (DbUpdateConcurrencyException)
{
}
return RedirectToPage("./Index");
}
Error
InvalidOperationException: The instance of entity type 'IdentityRole' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values.
Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap.Add(TKey key, InternalEntityEntry entry)

I changed codes like that and its work but that is a weird and when i change name that changes automatic IdentityRole normalized name column.
var role = await _roleManager.FindByIdAsync(IdentityRole.Id);
role.Name = IdentityRole.Name;
await _roleManager.UpdateAsync(role);
I found there enter link description here

Related

RoleManager in asp.net core web app working with different tables

I have Four tables (Student, AspNetUsers, AspNetUserRoles and AspNetRoles).
I have three roles (user, Teacher, Admin).
The teacher/Admin creates a user account(fields ie Email, phone, address, role) with the user role and this is saved in the student table. The teacher gives an Email to the student.
The student creates an account with an Email(Created by teacher/Admin) and password and this is saved in the AspNetUsers table.
My Question Is: How to assign a role to the student that is given by the Teacher/Admin in AspNetUserRoles table (UserID and UserRoleId). UserId is in AspNetUsers table and UserRoleId is in student table
public class RolesAdminController : Controller
{
private ApplicationUserManager _userManager;
public ApplicationUserManager UserManager;
private ApplicationUserManager _userManager;
public RolesAdminController(ApplicationUserManager userManager,
ApplicationRoleManager roleManager, ApplicationSignInManager signInManager)
{
UserManager = userManager;
RoleManager = roleManager;
SignInManager = signInManager;
}
[HttpPost]
public async Task<IActionResult> Register(string returnUrl = null)
{
returnUrl = returnUrl ?? Url.Content("~/");
if(ModelState.IsValid){
var user = new IdentityUser{Username = Input.Name, Email = Input.Email};
var result = await _UserManager.CreateAsync(user,Input.Password);
return RedirectToAction("Index");
}
}
I have no idea.. What should I add in my code to do it? Assign a role to the student that should be done while the student Register the account.
Need Help
Here is hoping that you have your startup code wired up correctly to include the AddRoleManager method like so
services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
Then your code is tweeked a bit since it seems as though you are tring to make the call from an api endpoint instead of an actual web page, hence issues such as redirecting to a return url will be handled by the caller when they assess whether the response from the api call they made succeeded or not. Note also that I use IdentityUser instead of ApplicationUser, but you can swap it out if you are using the later. Anyway, the code should give you a gist of how to implement it in your own project
public class RolesAdminController : ControllerBase
{
private UserManager<IdentityUser> userManager;
private readonly RoleManager<IdentityRole> roleManager;
public RolesAdminController(UserManager<IdentityUser> userManager,
RoleManager<IdentityRole> roleManager)
{
this.userManager = userManager;
this.roleManager = roleManager;
}
[HttpPost]
public async Task<IActionResult> Register(InputDto inputDto)
{
inputDto ??= new InputDto(); // if input is null, create new input
List<string> errors = inputDto.Errors();
if (errors.Any())
{
return BadRequest(errors);
}
var user = new IdentityUser(){ UserName = inputDto.Name, Email = inputDto.Email };
var result = await userManager.CreateAsync(user, inputDto.Password);
if (!result.Succeeded)
{
return BadRequest(result.Errors); //You can turn that errors object into a list of string if you like
}
string roleName = "";
switch (inputDto.RoleType)
{
case RoleType.User:
roleName = "user";
break;
case RoleType.Teacher:
roleName = "Teacher";
break;
case RoleType.Admin:
roleName = "Admin";
break;
default:
break;
}
//Checking if role is in not system
if(!(await roleManager.RoleExistsAsync(roleName)))
{
await roleManager.CreateAsync(new IdentityRole(roleName));
}
await userManager.AddToRoleAsync(user, roleName);
return Ok(); //Or you can return Created(uriToTheCreatedResource);
}
}
public class InputDto
{
public string Name { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public RoleType RoleType { get; set; }
public List<string> Errors()
{
List<string> vtr = new List<string>();
if (String.IsNullOrWhiteSpace(Name))
{
vtr.Add("Name Cannot be null");
}
if (String.IsNullOrWhiteSpace(Email))
{
vtr.Add("Email Cannot be null");
}
//Do the rest of your validation
return vtr;
}
}
public enum RoleType
{
User, Teacher, Admin
}

Edit action is not doing anything on automapper

I´m using Visual Studio 2019, .net core 3.1 and automapper. My Edit action dont edit the record. I´ve seen tutorials but all are just of one action and I need to do a crud. Taking as example a usual edit action I´ve made this:
public class CustomerCountriesController : Controller
{
private readonly ApplicationDbContext _context;
private readonly IMapper _mapper;
public CustomerCountriesController(ApplicationDbContext context, IMapper mapper)
{
_context = context;
_mapper = mapper;
}
// GET: CustomerCountries
public async Task<IActionResult> Index()
{
//CustomerCountries customerCountry = new CustomerCountries();
var customerCountry = await _context.CustomerCountries.ToListAsync();
List<CustomerCountriesDto> countries = _mapper.Map<List<CustomerCountries>,
List<CustomerCountriesDto>>(await _context.CustomerCountries.ToListAsync());
return View(countries);
}
public async Task<IActionResult> Edit(string id)
{
if (id == null)
{
return NotFound();
}
var customerCountries = await _context.CustomerCountries.FindAsync(id);
var model = _mapper.Map<CustomerCountries, CustomerCountriesDto>(customerCountries);
if (customerCountries == null)
{
return NotFound();
}
return View(model);
//return View(customerCountries);
}
// POST: CustomerCountries/Edit/5
// To protect from overposting attacks, enable the specific properties you want to bind to, for
// more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
//public async Task<IActionResult> Edit(string id, [Bind("CustomerCountry")] CustomerCountries customerCountries)
public async Task<IActionResult> Edit(string customerCountry, CustomerCountriesDto customerCountriesDto)
{
if (customerCountry != customerCountriesDto.CustomerCountry)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
var CustomerCountries = _mapper.Map<CustomerCountriesDto, CustomerCountries>(customerCountriesDto);
_context.Update(CustomerCountries);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!CustomerCountriesExists(customerCountriesDto.CustomerCountry))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(customerCountriesDto);
}
public class AutoMapping : Profile
{
public AutoMapping()
{
CreateMap<CustomerCountries, CustomerCountriesDto>();
CreateMap<CustomerCountriesDto, CustomerCountries>();
}
}
public class CustomerCountries
{
[StringLength(50, ErrorMessage = "Longitud máxima para el país: 50")]
public string CustomerCountry { get; set; }
public ICollection<CustomerRegions> CustomerRegions { get; set; }
}
public class CustomerCountriesDto
{
public string CustomerCountry { get; set; }
}
On startup
services.AddAutoMapper(typeof(Startup));
The id of the table is CustomerCounty
Can you tell me the correct way?
I´ve found the solution thanks to an experienced developer that lead me through the issue and corrected my code from time to time (That is acctually really helping guys). Turns out that I was using a field as a PK: CustomerCountry... I wasn´t using and Id, when I changed the model, the update happened
var CustomerCountries = _mapper.Map<CustomerCountriesDto, CustomerCountries>(customerCountriesDto);
var country = _context.CustomerCountries.FirstOrDefault(c => c.Id == CustomerCountries.Id);
country.CustomerCountry = customerCountriesDto.CustomerCountry;
_context.Update(country);
await _context.SaveChangesAsync();

Asp.Net IdentityUser add custom List Property

I am fairly new to coding with asp.net so there might be an obvious answere to my question but I haven't found one yet.
So currently I am developing a site for project management and I want the users to get notified when an event happens, eg. they were added to a new project, a project has been updated etc.
For that I have expanded the IdentityUser Model with a new property List
public class CojectUser : IdentityUser
{
public List<Notification> Notifications { get; set; }
}
public class Notification
{
public int NotificationID { get; set; }
public string Message { get; set; }
public bool Seen { get; set; }
}
When an event happens I add them to the user's notification list and update the user via the userManager.
public class EventBroker<T> : IEventBroker<T>
{
private readonly UserManager<CojectUser> userManager;
public EventBroker(UserManager<CojectUser> userMgr, IUserValidator<CojectUser> userValid)
{
userManager = userMgr;
}
public async Task NotifyAsync(Message<T> message, List<UserRole> recipients)
{
foreach (var user in recipients)
{
var cojectUser = await userManager.FindByNameAsync(user.Name);
if (cojectUser != null)
{
if (cojectUser.Notifications == null)
{
cojectUser.Notifications = new List<Notification>();
}
cojectUser.Notifications.Add(new Notification
{
Message = message.Information,
Seen = false
});
IdentityResult result = await userManager.UpdateAsync(cojectUser);
if (!result.Succeeded)
{
throw new UserUpdateFailException();
}
}
}
}
}
}
I am able to save the custom data to the database, but I am unable to load it again from database.
When I want to display the user's notifications userManager retrieves an user object with null as notification list. Even though the data is stored in database.
public async Task<IActionResult> Index()
{
CojectUser user = await userManager.GetUserAsync(User);
if(user.Notifications == null)
{
user.Notifications = new List<Notification>();
}
return View(user);
}
Data in database:
Can anybody tell me what I am doing wrong?
UserManager don't eager load properties by default.
You should use DatabaseContext directly.
var user = _context.Users.Include(c => c.Notifications).Where(u => u.Id == user.Id).ToList();

ASP.NET Unable to resolve service for type, while attempting to activate controller

While I understand there were other questions on this very topic I was having a difficult time understanding the answers and was hoping someone could walk me through how DbContext's work as I feel I might have created a second context when I shouldn't have.
So, as I'm teaching myself more about .NET Core I'm working on turning an old school project into a .NET project which is a simple Dentist office web app where users can sign up for appointments, view their appointments, etc. I was following along with this tutorial to add additional user attributes instead of just username & e-mail as I was trying to grab the current user when creating an appointment.
Before I added this custom attributes using the default IdentityUI I had my project working where a user could register and login, create a basic appointment with their 'username' pick a date and time and once created would display their appointments in a basic table format. My next step was to add the custom user attributes so it would display based on their real-name and not their username which is defaulted to their email.
Following the tutorial I'm not sure if I misunderstood but I created a new Context and IdentityUser which all worked but it has broken my 'Appointments' page giving me the:
InvalidOperationException: Unable to resolve service for type 'Microsoft.AspNetCore.Identity.UserManager`1[Microsoft.AspNetCore.Identity.IdentityUser]' while attempting to activate 'WelchDentistry.Controllers.AppointmentsController'.** error.
Here is my ConfigureServices method as I believe it's an issue in regards to registering the 2 different Contexts.
public void ConfigureServices(IServiceCollection services)
{
/*
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
*/
/*
services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
*/
services.AddControllersWithViews();
services.AddRazorPages();
services.AddMvc();
}
Here is the original context
namespace WelchDentistry.Data
{
public class ApplicationDbContext : IdentityDbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public DbSet<WelchDentistry.Models.Appointment> Appointment { get; set; }
}
}
Here is my controller for my appointments
namespace WelchDentistry.Controllers
{
public class AppointmentsController : Controller
{
private readonly ApplicationDbContext _context;
private readonly UserManager<IdentityUser> _userManager;
public AppointmentsController(ApplicationDbContext context, UserManager<IdentityUser> userManager)
{
_context = context;
_userManager = userManager;
}
// GET: Appointments
public async Task<IActionResult> Index()
{
var user = await _userManager.GetUserAsync(HttpContext.User);
return View(await _context.Appointment.ToListAsync());
}
// GET: Appointments/Details/5
public async Task<IActionResult> Details(int? id)
{
if (id == null)
{
return NotFound();
}
var appointment = await _context.Appointment
.FirstOrDefaultAsync(m => m.ID == id);
if (appointment == null)
{
return NotFound();
}
return View(appointment);
}
// GET: Appointments/Create
public IActionResult Create()
{
return View();
}
// POST: Appointments/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("ID, CustomerName, AppointmentTime,CustomerDoctor")] Appointment appointment)
{
if (ModelState.IsValid)
{
_context.Add(appointment);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(appointment);
}
// GET: Appointments/Edit/5
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var appointment = await _context.Appointment.FindAsync(id);
if (appointment == null)
{
return NotFound();
}
return View(appointment);
}
// POST: Appointments/Edit/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("ID,CustomerName,AppointmentTime,CustomerDoctor")] Appointment appointment)
{
if (id != appointment.ID)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(appointment);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!AppointmentExists(appointment.ID))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(appointment);
}
// GET: Appointments/Delete/5
public async Task<IActionResult> Delete(int? id)
{
if (id == null)
{
return NotFound();
}
var appointment = await _context.Appointment
.FirstOrDefaultAsync(m => m.ID == id);
if (appointment == null)
{
return NotFound();
}
return View(appointment);
}
// POST: Appointments/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteConfirmed(int id)
{
var appointment = await _context.Appointment.FindAsync(id);
_context.Appointment.Remove(appointment);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
private bool AppointmentExists(int id)
{
return _context.Appointment.Any(e => e.ID == id);
}
}
}
If more code is needed please ask or you can view on my Github
I appreciate all the help and bare with my as I'm still lost on most of this but slowly learning.
First of all remove your IdentityHostingStartup file in your Identity Area.
Then change your databasecontext to this ( You should introduce your User class ) :
public class ApplicationDbContext : IdentityDbContext<WelchDentistryUser, IdentityRole, string>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public DbSet<Models.Appointment> Appointment { get; set; }
}
And add this codes in your startup file .
services.AddIdentity<WelchDentistryUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
And finally you should use your custom User class in controller.
public class AppointmentsController : Controller
{
private readonly ApplicationDbContext _context;
private readonly UserManager<WelchDentistryUser> _userManager;
public AppointmentsController(ApplicationDbContext context, UserManager<WelchDentistryUser> userManager)
{
_context = context;
_userManager = userManager;
}
}

Identity Framework test if confirm email token is expired

Is it possible to test whether a confirm email token is expired using Identity Framework's UserManager? No matter what the error is, from the following:
var result = await UserManager.ConfirmEmailAsync(userId, code);
I get a generic "Invalid Token" error.
I found a way to parse the token for the date issued, which you can then check to see if is within the allowed timespan (default of 24hours if not specified).
Identity.cs
ApplicationUserManager
public IDataProtector Protector { get; set; }
public TimeSpan TokenLifespan { get; set; }
ApplicationUserManager Create()
// Explicitly set token expiration to 24 hours.
manager.TokenLifespan = TimeSpan.FromHours(24);
var dataProtectionProvider = options.DataProtectionProvider;
manager.Protector = dataProtectionProvider.Create("ASP.NET Identity");
if (dataProtectionProvider != null)
{
manager.UserTokenProvider =
new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"))
{
TokenLifespan = manager.TokenLifespan
};
}
AccountController.cs
public async Task<ActionResult> ConfirmEmail(string Code, string UserId)
{
// Try/catch, validation, etc.
var tokenExpired = false;
var unprotectedData = UserManager.Protector.Unprotect(Convert.FromBase64String(Code));
var ms = new MemoryStream(unprotectedData);
using (BinaryReader reader = new BinaryReader(ms))
{
var creationTime = new DateTimeOffset(reader.ReadInt64(), TimeSpan.Zero);
var expirationTime = creationTime + UserManager.TokenLifespan;
if (expirationTime < DateTimeOffset.UtcNow)
{
tokenExpired = true;
}
}
// Do something if token is expired, else continue with confirmation
}
I found this blog post and Nkosi's answer to be extremely helpful, and if you want to go through the Identity source code, Microsoft has it here (The previous versions of Identity for MVC5 and lower here). Also, I apologize if its in poor form to answer a question that you, yourself put a bounty on, but I couldn't help but continue looking for a better solution.
I get around this by keeping/storing a copy of the generated token
public class ApplicationUser : IdentityUser {
public string EmailConfirmationToken { get; set; }
public string ResetPasswordToken { get; set; }
}
and associating it with the user in derived UserManager<ApplicationUser>.
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);
if (!string.IsNullOrEmpty(token)) {
var user = await FindByIdAsync(userId);
user.EmailConfirmationToken = token; //<<< Last issued token
//Note: If a token is generated then the current email is no longer confirmed.
user.EmailConfirmed = false;
await UpdateAsync(user);
}
return token;
}
When the token is provided for confirmation, a search for the user via the token is done.
public static class ApplicationUserManagerExtension {
public static Task<string> FindIdByEmailConfirmationTokenAsync(this UserManager<ApplicationUser> manager, string confirmationToken) {
string result = null;
ApplicationUser user = manager.Users.SingleOrDefault(u => u.EmailConfirmationToken != null && u.EmailConfirmationToken == confirmationToken);
if (user != null) {
result = user.Id;
}
return Task.FromResult(result);
}
}
If the token matches a known user that indicates that it was a validly issued token.
Will then attempt to confirm token with User manager.
If confirmation fails then token has expired and an appropriate action is taken.
Else if the token confirmed, it is removed from associated user and thus invalidating the reuse of that token.
public override async System.Threading.Tasks.Task<IdentityResult> ConfirmEmailAsync(string userId, string token) {
var user = await FindByIdAsync(userId);
if (user == null) {
return IdentityResult.Failed("User Id Not Found");
}
var result = await base.ConfirmEmailAsync(userId, token);
if (result.Succeeded) {
user.EmailConfirmationToken = null;
return await UpdateAsync(user);
} else if (user.EmailConfirmationToken == token) {
//Previously Issued Token expired
result = IdentityResult.Failed("Expired Token");
}
return result;
}
A similar approach was implemented for password reset as well.
Here comes an .NET Core 2.1 adaption of the solution provided by #Nkosi :
ApplicationUser class
public class ApplicationUser : IdentityUser
{
public string EmailConfirmationToken { get; set; }
public string ResetPasswordToken { get; set; }
}
Derived UserManager class
public class CustomUserManager : UserManager<ApplicationUser>
{
public CustomUserManager(IUserStore<ApplicationUser> store,
IOptions<IdentityOptions> optionsAccessor,
IPasswordHasher<ApplicationUser> passwordHasher,
IEnumerable<IUserValidator<ApplicationUser>> userValidators,
IEnumerable<IPasswordValidator<ApplicationUser>> passwordValidators,
ILookupNormalizer keyNormalizer,
IdentityErrorDescriber errors,
IServiceProvider services,
ILogger<UserManager<ApplicationUser>> logger)
: base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger)
{
}
public override async Task<string> GenerateEmailConfirmationTokenAsync(ApplicationUser user)
{
/* 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(user);
if (!string.IsNullOrEmpty(token))
{
user.EmailConfirmationToken = token; //<<< Last issued token
//Note: If a token is generated then the current email is no longer confirmed.
user.EmailConfirmed = false;
await UpdateAsync(user);
}
return token;
}
public override async Task<IdentityResult> ConfirmEmailAsync(ApplicationUser user, string token)
{
if (user == null)
{
return IdentityResult.Failed(new IdentityError {Description = "User not found."});
}
var result = await base.ConfirmEmailAsync(user, token);
if (result.Succeeded)
{
user.EmailConfirmationToken = null;
return await UpdateAsync(user);
}
else if (user.EmailConfirmationToken == token)
{
//Previously Issued Token expired
result = IdentityResult.Failed(new IdentityError { Description = "Expired token." });
}
return result;
}
}
UserManager Extension
public static class ApplicationUserManagerExtension
{
public static Task<string> FindIdByEmailConfirmationTokenAsync(this UserManager<ApplicationUser> manager, string confirmationToken)
{
string result = null;
ApplicationUser user = manager.Users
.SingleOrDefault(u => u.EmailConfirmationToken != null && u.EmailConfirmationToken == confirmationToken);
if (user != null)
{
result = user.Id;
}
return Task.FromResult(result);
}
}
Update:
The CustomUserManager has to be added to services in Startup.cs in ConfigureServices Method.
services.AddTransient<CustomUserManager>();
Without this, DependencyInjection fails.
You can use my controller.It's working mate.
public IActionResult ForgotPassword()
{
return View();
}
[HttpPost]
public async Task<IActionResult> ForgotPassword(string Email)
{
if (string.IsNullOrEmpty(Email))
{
return View();
}
var user = await _userManager.FindByEmailAsync(Email);
if (user == null)
{
return View();
}
var code =await _userManager.GeneratePasswordResetTokenAsync(user);
var callback = Url.Action("ResetPassword", "Account", new
{
token=code,
},Request.Scheme);
// send email
await _emailSender.SendEmailAsync(Email, "Confirm Password Reset", $"<a href='{callback}'>If you want to reset your password click please !</a>");
return RedirectToAction("ForgotPasswordConfirmation", "Account");
}
public IActionResult ForgotPasswordConfirmation() => View();
public IActionResult ResetPassword(string token)
{
if (token == null)
{
return View();
}
var model = new ResetPasswordModel()
{
Token = token,
};
return View(model);
}
[HttpPost]
public async Task<IActionResult> ResetPassword(ResetPasswordModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var user = await _userManager.FindByEmailAsync(model.Email);
if (user == null)
{
return RedirectToAction("Index", "Home");
}
var result = await _userManager.ResetPasswordAsync(user, model.Token, model.Password);
if (result.Succeeded)
{
return RedirectToAction("ResetPasswordConfirmation", "Account");
}
return View(model);
}
public ActionResult ResetPasswordConfirmation() => View();

Resources