Пример #1
0
        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."));
            }
        }
Пример #2
0
        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."));
            }
        }
Пример #3
0
        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."));
            }
        }
Пример #4
0
        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."));
            }
        }
Пример #5
0
        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."));
            }
        }