public async Task <IActionResult> RecoverUsername([FromBody] StringDTO stringModel) { try { if (!ModelState.IsValid) { _logger.LogWarning("User attempted to recover username with invalid model state."); return(BadRequest("Request failed - invalid request")); } var email = stringModel.Value; if (email == null) { _logger.LogWarning("User attempted to recover username with missing email."); return(BadRequest("Request failed - missing parameters.")); } var user = await _unitOfWork.UserManager.FindByEmailAsync(email).ConfigureAwait(false); if (user == null) { _logger.LogInformation("User attempted to recover username with invalid email."); return(NoContent()); } var emailData = new UsernameTemplate() { Username = user.UserName }; var emailResponse = await _emailSender .SendEmailAsync(user.Email, _config["SendGrid:Templates:UsernameRecovery"], EmailFrom.Account, emailData) .ConfigureAwait(false); if (emailResponse.StatusCode != System.Net.HttpStatusCode.Accepted) { _logger.LogError($"SendGrid failed to send username recovery notification to {user.UserName}."); return(StatusCode(500, "Username recovery unsuccessful.")); } else { _logger.LogInformation($"Username recovery confirmation email dispatched to {user.UserName}."); return(NoContent()); } } catch (Exception ex) { _logger.LogError(ex, "Exception thrown attempting to recover username."); return(StatusCode(500, "Username recovery unsuccessful.")); } }
public async Task <IActionResult> ConfirmEmailChange([FromBody] ChangeEmailDTO changeEmail) { try { if (!ModelState.IsValid) { if (changeEmail.NewEmail != null) { changeEmail.NewEmail = "NEW_EMAIL"; } _logger.LogTrace("Email change failed due to missing parameters.", changeEmail); return(BadRequest("Confirmation details required.")); } var user = await _unitOfWork.UserManager.FindByIdAsync(changeEmail.UserId).ConfigureAwait(false); if (user == null) { _logger.LogWarning("Email change attempted with invalid user details."); return(StatusCode(500, "Email change confirmation unsuccessful.")); } var oldEmail = user.Email; changeEmail.Token = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(changeEmail.Token)); var result = await _unitOfWork.UserManager.ChangeEmailAsync(user, changeEmail.NewEmail, changeEmail.Token).ConfigureAwait(false); if (!result.Succeeded) { return(BadRequest("Email change request failed: validation code invalid.")); } _logger.LogInformation($"{user.UserName} changed their email successfully."); try { user.OldEmail = oldEmail; await RevokeAllActiveUserRefreshCookiesAsync(user).ConfigureAwait(false); _logger.LogInformation($"Active refresh tokens revoked for {user.UserName}."); } catch (InvalidOperationException ex) { _logger.LogError(ex, "Exception thrown attempting to modify refresh tokens in database."); _logger.LogError($"Old email ({oldEmail}) for {user.UserName} may not have updated correctly."); } catch (DbUpdateConcurrencyException ex) { _logger.LogError(ex, "Exception thrown attempting to modify refresh tokens in database."); _logger.LogError($"Old email ({oldEmail}) for {user.UserName} may not have updated correctly."); } InvalidateRefreshCookieInBrowser(); var emailData = new UsernameTemplate() { Username = user.UserName }; var emailResponseOld = await _emailSender .SendEmailAsync(oldEmail, _config["SendGrid:Templates:EmailChanged"], EmailFrom.Account, emailData) .ConfigureAwait(false); if (emailResponseOld.StatusCode != System.Net.HttpStatusCode.Accepted) { _logger.LogError($"SendGrid failed to send email change notification to {user.UserName}'s old email."); } else { _logger.LogInformation($"Email change notification dispatched to {user.UserName}'s old email."); } var emailResponseNew = await _emailSender .SendEmailAsync(changeEmail.NewEmail, _config["SendGrid:Templates:EmailChangeSuccess"], EmailFrom.Account, emailData) .ConfigureAwait(false); if (emailResponseNew.StatusCode != System.Net.HttpStatusCode.Accepted) { _logger.LogError($"SendGrid failed to send email change notification to {user.UserName}'s new email."); } else { _logger.LogInformation($"Email change notification dispatched to {user.UserName}'s new email."); } return(NoContent()); } catch (DbUpdateConcurrencyException ex) { _logger.LogError(ex, "Exception thrown attempting to save new refresh token to database."); return(StatusCode(500, "Email change confirmation unsuccessful.")); } catch (Exception ex) { _logger.LogError(ex, "Exception thrown confirming email change."); return(StatusCode(500, "Email change confirmation unsuccessful.")); } }
public async Task <IActionResult> RequestEmailChange([FromBody] EmailDTO newEmail) { try { if (!ModelState.IsValid) { _logger.LogTrace("User email change failed due to missing parameters."); return(BadRequest("Change email failed - missing parameters.")); } var user = await _unitOfWork.UserManager.FindByNameAsync(User.Identity.Name).ConfigureAwait(false); if (user == null) { _logger.LogInformation("Email change request made by invalid user", newEmail); return(StatusCode(500, "Email change request unsuccessful.")); } if (!ValidEmailDomain(newEmail.NewEmail)) { _logger.LogInformation($"{user.UserName} attempted to change their email to one from an excluded domain ({newEmail.NewEmail})."); return(BadRequest("Email addresses from this domain are not accepted.")); } var token = await _unitOfWork.UserManager.GenerateChangeEmailTokenAsync(user, newEmail.NewEmail).ConfigureAwait(false); token = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(token)); //send notification to new email var parameters = new string[] { "token", token, "userId", user.Id, "newEmail", newEmail.NewEmail }; var emailData = new UsernameLinkTemplate() { Username = user.UserName, HrefLink = HtmlEncoder.Default.Encode(GetBaseURL("email-change", parameters)) }; var emailResponse = await _emailSender .SendEmailAsync(newEmail.NewEmail, _config["SendGrid:Templates:EmailChangeConfirmation"], EmailFrom.Account, emailData) .ConfigureAwait(false); if (emailResponse.StatusCode != System.Net.HttpStatusCode.Accepted) { _logger.LogError($"SendGrid failed to send email change confirmation notification to {user.UserName}'s new email."); return(StatusCode(500, "Email change request unsuccessful.")); } else { _logger.LogInformation($"Email change confirmation request dispatched to {user.UserName}'s new email."); } //send notification to old email var oldEmailData = new UsernameTemplate() { Username = user.UserName }; emailResponse = await _emailSender .SendEmailAsync(user.Email, _config["SendGrid:Templates:EmailChangeRequestNotification"], EmailFrom.Account, oldEmailData) .ConfigureAwait(false); if (emailResponse.StatusCode != System.Net.HttpStatusCode.Accepted) { _logger.LogError($"SendGrid failed to send email change confirmation notification to {user.UserName}'s old email."); } else { _logger.LogInformation($"Email change confirmation request dispatched to {user.UserName}'s old email."); } return(NoContent()); } catch (Exception ex) { _logger.LogError(ex, "Exception thrown requesting email change."); return(StatusCode(500, "Email change request unsuccessful.")); } }
public async Task <IActionResult> ResetPassword([FromBody] PasswordResetDTO resetViewModel) { try { if (!ModelState.IsValid) { _logger.LogTrace("Password reset failed due to missing parameters."); return(BadRequest("Invalid request.")); } if (!resetViewModel.Password.Equals(resetViewModel.ConfirmPassword, StringComparison.Ordinal)) { return(BadRequest("Passwords do not match.")); } var user = await _unitOfWork.UserManager.FindByIdAsync(resetViewModel.UserId).ConfigureAwait(false); if (user == null) { _logger.LogWarning("Password reset attempted with invalid user details."); return(StatusCode(500, "Password reset unsuccessful.")); } var token = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(resetViewModel.Token)); var result = await _unitOfWork.UserManager.ResetPasswordAsync(user, token, resetViewModel.Password).ConfigureAwait(false); if (!result.Succeeded) { return(BadRequest("Password reset failed: validation token not valid.")); } _logger.LogInformation($"{user.UserName} successfully reset their password."); try { await RevokeAllActiveUserRefreshCookiesAsync(user).ConfigureAwait(false); _logger.LogInformation($"Revoked active refresh tokens for {user.UserName}."); } catch (InvalidOperationException ex) { _logger.LogError(ex, "Exception thrown attempting to modify refresh tokens in database."); } catch (DbUpdateConcurrencyException ex) { _logger.LogError(ex, "Exception thrown attempting to modify refresh tokens in database."); } InvalidateRefreshCookieInBrowser(); var emailData = new UsernameTemplate() { Username = user.UserName }; var emailResponse = await _emailSender .SendEmailAsync(user.Email, _config["SendGrid:Templates:PasswordChanged"], EmailFrom.Account, emailData) .ConfigureAwait(false); if (emailResponse.StatusCode != System.Net.HttpStatusCode.Accepted) { _logger.LogError($"SendGrid failed to send password change notification to {user.UserName}."); } else { _logger.LogInformation($"Password change email dispatched to {user.UserName}."); } return(NoContent()); } catch (DbUpdateConcurrencyException ex) { _logger.LogError(ex, "Exception thrown attempting to save new refresh token to database."); return(StatusCode(500, "Password reset unsuccessful.")); } catch (Exception ex) { _logger.LogError(ex, "Exception thrown attempting to reset password."); return(StatusCode(500, "Password reset unsuccessful.")); } }
public async Task <IActionResult> ChangePassword([FromBody] PasswordChangeDTO passwordChangeModel) { try { if (!ModelState.IsValid) { _logger.LogTrace("Password change attempted with invalid model state."); return(BadRequest("Failed to change password - invalid request.")); } if (!passwordChangeModel.Password.Equals(passwordChangeModel.ConfirmPassword, StringComparison.Ordinal)) { _logger.LogInformation("Password change failed due to password mismatch."); return(BadRequest("Failed to change password - passwords do not match.")); } var user = await _unitOfWork.UserManager.FindByNameAsync(User.Identity.Name).ConfigureAwait(false); if (user == null) { _logger.LogWarning("Password change attempted by unidentified user {Model}", passwordChangeModel); return(StatusCode(500, "Password change unsuccessful.")); } var result = await _unitOfWork.UserManager.ChangePasswordAsync(user, passwordChangeModel.CurrentPassword, passwordChangeModel.Password).ConfigureAwait(false); if (!result.Succeeded) { _logger.LogWarning("Password change failed."); return(BadRequest("Failed to change password - operation failed.")); } _logger.LogInformation($"{user.UserName} changed their password successfully."); try { await RevokeAllActiveUserRefreshCookiesAsync(user).ConfigureAwait(false); _logger.LogInformation($"Active refresh tokens revoked for {user.UserName}."); } catch (InvalidOperationException ex) { _logger.LogError(ex, "Exception thrown attempting to modify refresh tokens in database."); } catch (DbUpdateConcurrencyException ex) { _logger.LogError(ex, "Exception thrown attempting to modify refresh tokens in database."); } InvalidateRefreshCookieInBrowser(); var emailData = new UsernameTemplate() { Username = user.UserName }; var emailResponse = await _emailSender .SendEmailAsync(user.Email, _config["SendGrid:Templates:PasswordChanged"], EmailFrom.Account, emailData) .ConfigureAwait(false); if (emailResponse.StatusCode != System.Net.HttpStatusCode.Accepted) { _logger.LogError($"SendGrid failed to send password change notification to {user.UserName}."); } else { _logger.LogInformation($"Password change confirmation email dispatched to {user.UserName}."); } return(NoContent()); } catch (Exception ex) { _logger.LogError(ex, "Exception thrown attempting to change password."); return(StatusCode(500, "Password change unsuccessful.")); } }