I use asp net core Identity. This is what I am trying to do and I have no Idea how to do this so need some expert help on this. When a new user registers in my application , a password reset link is sent to the person's email, Now when the link token in the link has expired and the user clicks on the link I need to show a message as token expired. How do I find out If the token has expired or not when the user clicks on the link which is in the email. Can any one suggest ways to achieve this scenario?
1
votes
1 Answers
4
votes
The ASP.NET Core has a built-in Claims system to store information about the user .
To do that ,
add a helper method to store the expiration datetime in
Register.cshtml.cs:private async Task AddTokenExpirationInfo(IdentityUser user, int span=1*24*60) { var expiresAt = DateTime.Now.Add(TimeSpan.FromMinutes(span)); var tokenExpiredAtClaim = new Claim("ActivtationTokenExpiredAt", expiresAt.ToUniversalTime().Ticks.ToString()); await _userManager.AddClaimAsync(user, tokenExpiredAtClaim); }add a helper method to check whether the token has expired in the
ConfirmEmail.cshtml.cs:private async Task<bool> TokenExpiredValidate(IdentityUser user) { var claims = (await _userManager.GetClaimsAsync(user)) .Where(c => c.Type == "ActivtationTokenExpiredAt"); var expiredAt = claims.FirstOrDefault()?.Value; bool expired = true; // default value if (expiredAt != null) { var expires = Convert.ToInt64(expiredAt); var now = DateTime.Now.Ticks; expired= now <= expires? false : true; } else { expired = false; } // clear claims await _userManager.RemoveClaimsAsync(user, claims); return expired; }invoke
AddTokenExpirationInfowhen there's a registeration request :public async Task<IActionResult> OnPostAsync(string returnUrl = null) { returnUrl = returnUrl ?? Url.Content("~/"); if (ModelState.IsValid) { var user = new IdentityUser { UserName = Input.Email, Email = Input.Email }; var result = await _userManager.CreateAsync(user, Input.Password); if (result.Succeeded) { _logger.LogInformation("User created a new account with password."); var code = await _userManager.GenerateEmailConfirmationTokenAsync(user); var callbackUrl = Url.Page( "/Account/ConfirmEmail", pageHandler: null, values: new { userId = user.Id, code = code }, protocol: Request.Scheme); await _emailSender.SendEmailAsync(Input.Email, "Confirm your email", $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>."); ///////////////// invoke here //////////////////// AddTokenExpirationInfo(user); await _signInManager.SignInAsync(user, isPersistent: false); return LocalRedirect(returnUrl); } foreach (var error in result.Errors) { ModelState.AddModelError(string.Empty, error.Description); } } // If we got this far, something failed, redisplay form return Page(); }invoke
TokenExpiredValidatein yourConfirmEmail.cshtml.cs:public async Task<IActionResult> OnGetAsync(string userId, string code) { if (userId == null || code == null) { return RedirectToPage("/Index"); } var user = await _userManager.FindByIdAsync(userId); if (user == null) { return NotFound($"Unable to load user with ID '{userId}'."); } var result = await _userManager.ConfirmEmailAsync(user, code); if (!result.Succeeded) { throw new InvalidOperationException($"Error confirming email for user with ID '{userId}':"); } if (await TokenExpiredValidate(user)) throw new InvalidOperationException($"Token has alread expired '{userId}':"); return Page(); }
When a user registers , there' will be a record in the AspNetUserClaims table :
When a user confirmed successfully , the record will be removed . Just as a reminder , a more robust method is to use a background service to clear the expired record .
