public async Task <IActionResult> TwoFactorValidation(string userName) { ViewData["ButtonID"] = ButtonID.Login; if (string.IsNullOrWhiteSpace(userName)) { TempData[TempDataKeys.Redirect] = RedirectPurpose.TwoFactorSmsResendFailure; return(RedirectToActionPermanent(nameof(Login))); } ApplicationUser user = await _accountRepository.GetUserByNameAsync(userName); if (user == null) { TempData[TempDataKeys.Redirect] = RedirectPurpose.TwoFactorSmsResendFailure; return(RedirectToActionPermanent(nameof(Login))); } // Invalid Authorize Cookie if (!await _twoFactorTokenRepository.TokenValidAsync(user.Id)) { _logger.LogInformation($"USER {user.Id} was rejected two-factor sign in due to invalid authorize cookie."); TempData[TempDataKeys.Redirect] = RedirectPurpose.TwoFactorSmsResendFailure; return(RedirectToActionPermanent(nameof(Login))); } if (user.TwoFactorMethod == (int)TwoFactorMethod.SmsVerify && TempData[TempDataKeys.Redirect] == null && await _timeoutsRepository.CanRequestInitalTwoFactorSmsAsync(user.Id)) { await _accountRepository.SendTwoFactorCodeSms(user); await _timeoutsRepository.UpdateRequestAsync(user.Id, UserRequest.InitalTwoFactorRequestSms); } return(View(nameof(TwoFactorValidation), new TwoFactorAuthenticateModel() { InputUsername = userName, UserMethod = user.TwoFactorMethod == (int)TwoFactorMethod.SmsVerify ? TwoFactorMethod.SmsVerify : TwoFactorMethod.MobileAppVerify, })); }
public async Task <IActionResult> SendSmsCode(string m) { ResendTwoFactorSmsCodeModel model; // Deserialize the resend two factor sms model. #region Parse & deserialize the parameter. try { model = JsonConvert.DeserializeObject <ResendTwoFactorSmsCodeModel>(Encoding.UTF8.GetString(Convert.FromBase64String(m))); } catch (Exception) { _logger.LogInformation($"[CHECK-1] Received invalid serialization for SendSmsCode(m). Model Data = [{m}]"); TempData[TempDataKeys.Redirect] = RedirectPurpose.TwoFactorSmsResendFailure; return(RedirectToActionPermanent(nameof(AuthController.Index), GoliathControllers.AuthController)); } #endregion Parse & deserialize the parameter. #region Validate object data (Non-Null + Valid Two-Factor method) // Check if the model exists and the username is not null. if (model == null || string.IsNullOrWhiteSpace(model.Username) || string.IsNullOrWhiteSpace(model.Controller) || string.IsNullOrWhiteSpace(model.Action)) { _logger.LogInformation($"[CHECK-2] Received invalid serialization for SendSmsCode(m). Model Data = [{m}]"); TempData[TempDataKeys.Redirect] = RedirectPurpose.TwoFactorSmsResendFailure; if (model.UtilizeHtmlMessage ?? true) { TempData[TempDataKeys.HtmlMessage] = "Internal Error. Try again later."; } return(RedirectToActionPermanent(nameof(AuthController.Index), GoliathControllers.AuthController)); } // Query for the user. ApplicationUser user = await _repository.GetUserByNameAsync(model.Username); // Check if the user exists and there two-factor method is SMS. if (user == null || user.TwoFactorMethod != (int)TwoFactorMethod.SmsVerify) { _logger.LogInformation($"[CHECK-3] Received invalid serialization for SendSmsCode(m). Model Data = [{m}]"); TempData[TempDataKeys.Redirect] = RedirectPurpose.TwoFactorSmsResendFailure; if (model.UtilizeHtmlMessage ?? true) { TempData[TempDataKeys.HtmlMessage] = "Internal Error. Try again later."; } if (model.IsUrnRedirect) { return(RedirectToActionPermanent(model.Action, model.Controller)); } return(RedirectToActionPermanent(model.Action, model.Controller, new { userName = model.Username })); } #endregion Validate object data (Non-Null + Valid Two-Factor method) #region Ensure a unauthorized user can request token // Check for Invalid Two-Factor Authorize Cookie + User not logged in if (!await _authorizeTokenRepository.TokenValidAsync(user.Id) && !User.Identity.IsAuthenticated) { _logger.LogInformation($"[CHECK-4A] Unauthorized request for user {user.Id} ({user.UserName} was rejected (Invalid Authorization Token). Model Data = [{m}]"); TempData[TempDataKeys.Redirect] = RedirectPurpose.TwoFactorSmsResendFailure; if (model.UtilizeHtmlMessage ?? true) { TempData[TempDataKeys.HtmlMessage] = "Invalid account session."; } if (model.IsUrnRedirect) { return(RedirectToAction(model.Action, model.Controller)); } return(RedirectToAction(model.Action, model.Controller, new { userName = model.Username })); } #endregion Ensure a unauthorized user can request token #region Ensure that a authorized user can request a token. // If the user is logged in and they are requesting a username that is not theirs. if (User.Identity.IsAuthenticated && (User.Identity.Name != model.Username)) { _logger.LogInformation($"[CHECK-4B] Authorized request for user {user.Id} ({user.UserName} was rejected (Invalid Session). Model Data = [{m}]"); TempData[TempDataKeys.Redirect] = RedirectPurpose.TwoFactorSmsResendFailure; if (model.UtilizeHtmlMessage ?? true) { TempData[TempDataKeys.HtmlMessage] = "Invalid account session."; } if (model.IsUrnRedirect) { return(RedirectToAction(model.Action, model.Controller)); } return(RedirectToAction(model.Action, model.Controller, new { userName = model.Username })); } #endregion Ensure that a authorized user can request a token. #region Check if the user is on timeout // If the user has waited the timeout and is allowed to request another token. if (!User.Identity.IsAuthenticated && !await _timeoutsRepository.CanRequestResendTwoFactorSmsAsync(user.Id)) { _logger.LogInformation($"[CHECK-5A] Unauthorized request for user {user.Id} ({user.UserName} was rejected (Bad Timeout). Model Data = [{m}]"); TempData[TempDataKeys.Redirect] = RedirectPurpose.TwoFactorSmsResendFailureTimeout; if (model.IsUrnRedirect) { return(RedirectToAction(model.Action, model.Controller)); } return(RedirectToAction(model.Action, model.Controller, new { userName = model.Username })); } if (User.Identity.IsAuthenticated && !await _timeoutsRepository.CanRequestAuthorizedTwoFactorSmsAsync(user.Id)) { _logger.LogInformation($"[CHECK-5B] Authorized request for user {user.Id} ({user.UserName} was rejected (Bad Timeout). Model Data = [{m}]"); TempData[TempDataKeys.Redirect] = RedirectPurpose.TwoFactorSmsResendFailureTimeout; if (model.UtilizeHtmlMessage ?? true) { TempData[TempDataKeys.HtmlMessage] = "Please wait before requesting another code."; } if (model.IsUrnRedirect) { return(RedirectToAction(model.Action, model.Controller)); } return(RedirectToAction(model.Action, model.Controller, new { userName = model.Username })); } #endregion Check if the user is on timeout // Send the user their code. await _repository.SendTwoFactorCodeSms(user); // Register the fact that they requested a two-factor code. if (User.Identity.IsAuthenticated) { await _timeoutsRepository.UpdateRequestAsync(user.Id, UserRequest.RequestTwoFactorSmsAuthorized); } else { await _timeoutsRepository.UpdateRequestAsync(user.Id, UserRequest.RequestTwoFactorResendSms); } // Send the success information back. TempData[TempDataKeys.Redirect] = RedirectPurpose.TwoFactorSmsResendSuccess; // If the redirect is a url with query parameters. _logger.LogInformation($"USER {user.Id} ({user.UserName}) - Received two-factor resend successfully. Model Data = [{m}]"); if (model.IsUrnRedirect) { return(LocalRedirect(model.ReturnPath)); } return(RedirectToAction(model.Action, model.Controller, new { userName = model.Username })); }