)>}]
شركة التطبيقات المتكاملة لتصميم وبرمجة البرمجيات الخاصة ش.ش.و.
Integrated Applications Programming Company
Home » Code Library » AccountController (Ia.Ftn.Wa.Controllers)

Public general use code classes and xml files that we've compiled and used over the years:

    1: using System.Security.Claims;
    2: using Microsoft.AspNetCore.Authorization;
    3: using Microsoft.AspNetCore.Identity;
    4: using Microsoft.AspNetCore.Mvc;
    5: using Microsoft.AspNetCore.Mvc.Rendering;
    6:  
    7: namespace Ia.Ftn.Wa.Controllers
    8: {
    9:     ////////////////////////////////////////////////////////////////////////////
   10:  
   11:     /// <summary publish="true">
   12:     ///
   13:     /// </summary>
   14:     /// 
   15:     /// <remarks> 
   16:     /// Copyright � 2024-2025 Jasem Y. Al-Shamlan (info@ia.com.kw), Integrated Applications - Kuwait. All Rights Reserved.
   17:     ///
   18:     /// This library is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by
   19:     /// the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
   20:     ///
   21:     /// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
   22:     /// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
   23:     /// 
   24:     /// You should have received a copy of the GNU General Public License along with this library. If not, see http://www.gnu.org/licenses.
   25:     /// 
   26:     /// Copyright notice: This notice may not be removed or altered from any source distribution.
   27:     /// </remarks> 
   28:     [Authorize]
   29:     public class AccountController : Controller
   30:     {
   31:         private readonly UserManager<Ia.Ftn.Cl.Models.StaffIdentityUser> _userManager;
   32:         private readonly SignInManager<Ia.Ftn.Cl.Models.StaffIdentityUser> _signInManager;
   33:         private readonly Ia.Ftn.Wa.Services.IEmailSender _emailSender;
   34:         private readonly Ia.Ftn.Wa.Services.ISmsSender _smsSender;
   35:         private readonly ILogger _logger;
   36:  
   37:         /////////////////////////////////////////////////////////////////////////////////
   38:  
   39:         /// <summary>
   40:         ///
   41:         /// </summary>
   42:         public AccountController(
   43:             UserManager<Ia.Ftn.Cl.Models.StaffIdentityUser> userManager,
   44:             SignInManager<Ia.Ftn.Cl.Models.StaffIdentityUser> signInManager,
   45:             Ia.Ftn.Wa.Services.IEmailSender emailSender,
   46:             Ia.Ftn.Wa.Services.ISmsSender smsSender,
   47:             ILoggerFactory loggerFactory)
   48:         {
   49:             _userManager = userManager;
   50:             _signInManager = signInManager;
   51:             _emailSender = emailSender;
   52:             _smsSender = smsSender;
   53:             _logger = loggerFactory.CreateLogger<AccountController>();
   54:         }
   55:  
   56:         /////////////////////////////////////////////////////////////////////////////////
   57:  
   58:         /// <summary>
   59:         ///
   60:         /// </summary>
   61:         [HttpGet]
   62:         [AllowAnonymous]
   63:         [Route("identity/account")]
   64:         public IActionResult Index(string returnUrl = null)
   65:         {
   66:             ViewData["ReturnUrl"] = returnUrl;
   67:  
   68:             return View();
   69:         }
   70:  
   71:         /////////////////////////////////////////////////////////////////////////////////
   72:  
   73:         /// <summary>
   74:         ///
   75:         /// </summary>
   76:         [HttpGet]
   77:         [AllowAnonymous]
   78:         [Route("identity/account/login")]
   79:         public IActionResult Login(string returnUrl = null)
   80:         {
   81:             ViewData["ReturnUrl"] = returnUrl;
   82:             return View();
   83:         }
   84:  
   85:         /////////////////////////////////////////////////////////////////////////////////
   86:  
   87:         /// <summary>
   88:         ///
   89:         /// </summary>
   90:         [HttpPost]
   91:         [AllowAnonymous]
   92:         [ValidateAntiForgeryToken]
   93:         [Route("identity/account/login")]
   94:         public async Task<IActionResult> Login(Ia.Ftn.Wa.Models.AccountViewModels.LoginViewModel model, string returnUrl = null)
   95:         {
   96:             ViewData["ReturnUrl"] = returnUrl;
   97:             if (ModelState.IsValid)
   98:             {
   99:                 // This doesn't count login failures towards account lockout
  100:                 // To enable password failures to trigger account lockout, set lockoutOnFailure: true
  101:                 var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: false);
  102:                 if (result.Succeeded)
  103:                 {
  104:                     _logger.LogInformation(1, "User logged in.");
  105:                     return RedirectToLocal(returnUrl);
  106:                 }
  107:                 if (result.RequiresTwoFactor)
  108:                 {
  109:                     return RedirectToAction(nameof(SendCode), new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
  110:                 }
  111:                 if (result.IsLockedOut)
  112:                 {
  113:                     _logger.LogWarning(2, "User account locked out.");
  114:                     return View("Lockout");
  115:                 }
  116:                 else
  117:                 {
  118:                     ModelState.AddModelError(string.Empty, "Invalid login attempt.");
  119:                     return View(model);
  120:                 }
  121:             }
  122:  
  123:             // If we got this far, something failed, redisplay form
  124:             return View(model);
  125:         }
  126:  
  127:         /////////////////////////////////////////////////////////////////////////////////
  128:  
  129:         /// <summary>
  130:         ///
  131:         /// </summary>
  132:         [HttpGet]
  133:         [AllowAnonymous]
  134:         [Route("identity/account/register")]
  135:         public IActionResult Register(string returnUrl = null)
  136:         {
  137:             ViewData["ReturnUrl"] = returnUrl;
  138:             return View();
  139:         }
  140:  
  141:         /////////////////////////////////////////////////////////////////////////////////
  142:  
  143:         /// <summary>
  144:         ///
  145:         /// </summary>
  146:         [HttpPost]
  147:         [AllowAnonymous]
  148:         [ValidateAntiForgeryToken]
  149:         [Route("identity/account/register")]
  150:         public async Task<IActionResult> Register(Ia.Ftn.Wa.Models.AccountViewModels.RegisterViewModel model, string returnUrl = null)
  151:         {
  152:             ViewData["ReturnUrl"] = returnUrl;
  153:             if (ModelState.IsValid)
  154:             {
  155:                 var user = new Ia.Ftn.Cl.Models.StaffIdentityUser { UserName = model.Email, Email = model.Email };
  156:                 var result = await _userManager.CreateAsync(user, model.Password);
  157:                 if (result.Succeeded)
  158:                 {
  159:                     // For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=532713
  160:                     // Send an email with this link
  161:                     //var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
  162:                     //var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: HttpContext.Request.Scheme);
  163:                     //await _emailSender.SendEmailAsync(model.Email, "Confirm your account",
  164:                     //    "Please confirm your account by clicking this link: <a href=\"" + callbackUrl + "\">link</a>");
  165:                     await _signInManager.SignInAsync(user, isPersistent: false);
  166:                     _logger.LogInformation(3, "User created a new account with password.");
  167:                     return RedirectToLocal(returnUrl);
  168:                 }
  169:                 AddErrors(result);
  170:             }
  171:  
  172:             // If we got this far, something failed, redisplay form
  173:             return View(model);
  174:         }
  175:  
  176:         /////////////////////////////////////////////////////////////////////////////////
  177:  
  178:         /// <summary>
  179:         ///
  180:         /// </summary>
  181:         [HttpPost]
  182:         [ValidateAntiForgeryToken]
  183:         [Route("identity/account/logoff")]
  184:         public async Task<IActionResult> LogOff()
  185:         {
  186:             await _signInManager.SignOutAsync();
  187:             _logger.LogInformation(4, "User logged out.");
  188:             return RedirectToAction(nameof(HomeController.Index), "Home");
  189:         }
  190:  
  191:         /////////////////////////////////////////////////////////////////////////////////
  192:  
  193:         /// <summary>
  194:         ///
  195:         /// </summary>
  196:         [HttpPost]
  197:         [AllowAnonymous]
  198:         [ValidateAntiForgeryToken]
  199:         [Route("identity/account/external-login")]
  200:         public IActionResult ExternalLogin(string provider, string returnUrl = null)
  201:         {
  202:             // Request a redirect to the external login provider.
  203:             var redirectUrl = Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl });
  204:             var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl);
  205:             return Challenge(properties, provider);
  206:         }
  207:  
  208:         /////////////////////////////////////////////////////////////////////////////////
  209:  
  210:         /// <summary>
  211:         ///
  212:         /// </summary>
  213:         [HttpGet]
  214:         [AllowAnonymous]
  215:         [Route("identity/account/external-login-callback")]
  216:         public async Task<IActionResult> ExternalLoginCallback(string returnUrl = null, string remoteError = null)
  217:         {
  218:             if (remoteError != null)
  219:             {
  220:                 ModelState.AddModelError(string.Empty, $"Error from external provider: {remoteError}");
  221:                 return View(nameof(Login));
  222:             }
  223:             var info = await _signInManager.GetExternalLoginInfoAsync();
  224:             if (info == null)
  225:             {
  226:                 return RedirectToAction(nameof(Login));
  227:             }
  228:  
  229:             // Sign in the user with this external login provider if the user already has a login.
  230:             var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false);
  231:             if (result.Succeeded)
  232:             {
  233:                 // Update any authentication tokens if login succeeded
  234:                 await _signInManager.UpdateExternalAuthenticationTokensAsync(info);
  235:  
  236:                 _logger.LogInformation(5, "User logged in with {Name} provider.", info.LoginProvider);
  237:                 return RedirectToLocal(returnUrl);
  238:             }
  239:             if (result.RequiresTwoFactor)
  240:             {
  241:                 return RedirectToAction(nameof(SendCode), new { ReturnUrl = returnUrl });
  242:             }
  243:             if (result.IsLockedOut)
  244:             {
  245:                 return View("Lockout");
  246:             }
  247:             else
  248:             {
  249:                 // If the user does not have an account, then ask the user to create an account.
  250:                 ViewData["ReturnUrl"] = returnUrl;
  251:                 ViewData["ProviderDisplayName"] = info.ProviderDisplayName;
  252:                 var email = info.Principal.FindFirstValue(ClaimTypes.Email);
  253:                 return View("ExternalLoginConfirmation", new Ia.Ftn.Wa.Models.AccountViewModels.ExternalLoginConfirmationViewModel { Email = email });
  254:             }
  255:         }
  256:  
  257:         /////////////////////////////////////////////////////////////////////////////////
  258:  
  259:         /// <summary>
  260:         ///
  261:         /// </summary>
  262:         [HttpPost]
  263:         [AllowAnonymous]
  264:         [ValidateAntiForgeryToken]
  265:         [Route("identity/account/external-login-confirmation")]
  266:         public async Task<IActionResult> ExternalLoginConfirmation(Ia.Ftn.Wa.Models.AccountViewModels.ExternalLoginConfirmationViewModel model, string returnUrl = null)
  267:         {
  268:             if (ModelState.IsValid)
  269:             {
  270:                 // Get the information about the user from the external login provider
  271:                 var info = await _signInManager.GetExternalLoginInfoAsync();
  272:                 if (info == null)
  273:                 {
  274:                     return View("ExternalLoginFailure");
  275:                 }
  276:                 var user = new Ia.Ftn.Cl.Models.StaffIdentityUser { UserName = model.Email, Email = model.Email };
  277:                 var result = await _userManager.CreateAsync(user);
  278:                 if (result.Succeeded)
  279:                 {
  280:                     result = await _userManager.AddLoginAsync(user, info);
  281:                     if (result.Succeeded)
  282:                     {
  283:                         await _signInManager.SignInAsync(user, isPersistent: false);
  284:                         _logger.LogInformation(6, "User created an account using {Name} provider.", info.LoginProvider);
  285:  
  286:                         // Update any authentication tokens as well
  287:                         await _signInManager.UpdateExternalAuthenticationTokensAsync(info);
  288:  
  289:                         return RedirectToLocal(returnUrl);
  290:                     }
  291:                 }
  292:                 AddErrors(result);
  293:             }
  294:  
  295:             ViewData["ReturnUrl"] = returnUrl;
  296:             return View(model);
  297:         }
  298:  
  299:         /////////////////////////////////////////////////////////////////////////////////
  300:  
  301:         /// <summary>
  302:         ///
  303:         /// </summary>
  304:         [HttpGet]
  305:         [AllowAnonymous]
  306:         [Route("identity/account/confirm-email")]
  307:         public async Task<IActionResult> ConfirmEmail(string userId, string code)
  308:         {
  309:             if (userId == null || code == null)
  310:             {
  311:                 return View("Error");
  312:             }
  313:             var user = await _userManager.FindByIdAsync(userId);
  314:             if (user == null)
  315:             {
  316:                 return View("Error");
  317:             }
  318:             var result = await _userManager.ConfirmEmailAsync(user, code);
  319:             return View(result.Succeeded ? "ConfirmEmail" : "Error");
  320:         }
  321:  
  322:         /////////////////////////////////////////////////////////////////////////////////
  323:  
  324:         /// <summary>
  325:         ///
  326:         /// </summary>
  327:         [HttpGet]
  328:         [AllowAnonymous]
  329:         [Route("identity/account/forgot-password")]
  330:         public IActionResult ForgotPassword()
  331:         {
  332:             return View();
  333:         }
  334:  
  335:         /////////////////////////////////////////////////////////////////////////////////
  336:  
  337:         /// <summary>
  338:         ///
  339:         /// </summary>
  340:         [HttpPost]
  341:         [AllowAnonymous]
  342:         [ValidateAntiForgeryToken]
  343:         [Route("identity/account/forgot-password")]
  344:         public async Task<IActionResult> ForgotPassword(Ia.Ftn.Wa.Models.AccountViewModels.ForgotPasswordViewModel model)
  345:         {
  346:             if (ModelState.IsValid)
  347:             {
  348:                 var user = await _userManager.FindByEmailAsync(model.Email);
  349:                 if (user == null || !(await _userManager.IsEmailConfirmedAsync(user)))
  350:                 {
  351:                     // Don't reveal that the user does not exist or is not confirmed
  352:                     return View("ForgotPasswordConfirmation");
  353:                 }
  354:  
  355:                 // For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=532713
  356:                 // Send an email with this link
  357:                 //var code = await _userManager.GeneratePasswordResetTokenAsync(user);
  358:                 //var callbackUrl = Url.Action("ResetPassword", "Account", new { userId = user.Id, code = code }, protocol: HttpContext.Request.Scheme);
  359:                 //await _emailSender.SendEmailAsync(model.Email, "Reset Password",
  360:                 //   "Please reset your password by clicking here: <a href=\"" + callbackUrl + "\">link</a>");
  361:                 //return View("ForgotPasswordConfirmation");
  362:             }
  363:  
  364:             // If we got this far, something failed, redisplay form
  365:             return View(model);
  366:         }
  367:  
  368:         /////////////////////////////////////////////////////////////////////////////////
  369:  
  370:         /// <summary>
  371:         ///
  372:         /// </summary>
  373:         [HttpGet]
  374:         [AllowAnonymous]
  375:         [Route("identity/account/forgot-password-confirmation")]
  376:         public IActionResult ForgotPasswordConfirmation()
  377:         {
  378:             return View();
  379:         }
  380:  
  381:         /////////////////////////////////////////////////////////////////////////////////
  382:  
  383:         /// <summary>
  384:         ///
  385:         /// </summary>
  386:         [HttpGet]
  387:         [AllowAnonymous]
  388:         [Route("identity/account/reset-password")]
  389:         public IActionResult ResetPassword(string code = null)
  390:         {
  391:             return code == null ? View("Error") : View();
  392:         }
  393:  
  394:         /////////////////////////////////////////////////////////////////////////////////
  395:  
  396:         /// <summary>
  397:         ///
  398:         /// </summary>
  399:         [HttpPost]
  400:         [AllowAnonymous]
  401:         [ValidateAntiForgeryToken]
  402:         [Route("identity/account/reset-password")]
  403:         public async Task<IActionResult> ResetPassword(Ia.Ftn.Wa.Models.AccountViewModels.ResetPasswordViewModel model)
  404:         {
  405:             if (!ModelState.IsValid)
  406:             {
  407:                 return View(model);
  408:             }
  409:             var user = await _userManager.FindByEmailAsync(model.Email);
  410:             if (user == null)
  411:             {
  412:                 // Don't reveal that the user does not exist
  413:                 return RedirectToAction(nameof(AccountController.ResetPasswordConfirmation), "Account");
  414:             }
  415:             var result = await _userManager.ResetPasswordAsync(user, model.Code, model.Password);
  416:             if (result.Succeeded)
  417:             {
  418:                 return RedirectToAction(nameof(AccountController.ResetPasswordConfirmation), "Account");
  419:             }
  420:             AddErrors(result);
  421:             return View();
  422:         }
  423:  
  424:         /////////////////////////////////////////////////////////////////////////////////
  425:  
  426:         /// <summary>
  427:         ///
  428:         /// </summary>
  429:         [HttpGet]
  430:         [AllowAnonymous]
  431:         [Route("identity/account/reset-password-confirmation")]
  432:         public IActionResult ResetPasswordConfirmation()
  433:         {
  434:             return View();
  435:         }
  436:  
  437:         /////////////////////////////////////////////////////////////////////////////////
  438:  
  439:         /// <summary>
  440:         ///
  441:         /// </summary>
  442:         [HttpGet]
  443:         [AllowAnonymous]
  444:         [Route("identity/account/send-code")]
  445:         public async Task<ActionResult> SendCode(string returnUrl = null, bool rememberMe = false)
  446:         {
  447:             var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
  448:             if (user == null)
  449:             {
  450:                 return View("Error");
  451:             }
  452:             var userFactors = await _userManager.GetValidTwoFactorProvidersAsync(user);
  453:             var factorOptions = userFactors.Select(purpose => new SelectListItem { Text = purpose, Value = purpose }).ToList();
  454:             return View(new Ia.Ftn.Wa.Models.AccountViewModels.SendCodeViewModel { Providers = factorOptions, ReturnUrl = returnUrl, RememberMe = rememberMe });
  455:         }
  456:  
  457:         /////////////////////////////////////////////////////////////////////////////////
  458:  
  459:         /// <summary>
  460:         ///
  461:         /// </summary>
  462:         [HttpPost]
  463:         [AllowAnonymous]
  464:         [ValidateAntiForgeryToken]
  465:         [Route("identity/account/send-code")]
  466:         public async Task<IActionResult> SendCode(Ia.Ftn.Wa.Models.AccountViewModels.SendCodeViewModel model)
  467:         {
  468:             if (!ModelState.IsValid)
  469:             {
  470:                 return View();
  471:             }
  472:  
  473:             var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
  474:             if (user == null)
  475:             {
  476:                 return View("Error");
  477:             }
  478:  
  479:             if (model.SelectedProvider == "Authenticator")
  480:             {
  481:                 return RedirectToAction(nameof(VerifyAuthenticatorCode), new { ReturnUrl = model.ReturnUrl, RememberMe = model.RememberMe });
  482:             }
  483:  
  484:             // Generate the token and send it
  485:             var code = await _userManager.GenerateTwoFactorTokenAsync(user, model.SelectedProvider);
  486:             if (string.IsNullOrWhiteSpace(code))
  487:             {
  488:                 return View("Error");
  489:             }
  490:  
  491:             var message = "Your security code is: " + code;
  492:             if (model.SelectedProvider == "Email")
  493:             {
  494:                 await _emailSender.SendEmailAsync(await _userManager.GetEmailAsync(user), "Security Code", message);
  495:             }
  496:             else if (model.SelectedProvider == "Phone")
  497:             {
  498:                 await _smsSender.SendSmsAsync(await _userManager.GetPhoneNumberAsync(user), message);
  499:             }
  500:  
  501:             return RedirectToAction(nameof(VerifyCode), new { Provider = model.SelectedProvider, ReturnUrl = model.ReturnUrl, RememberMe = model.RememberMe });
  502:         }
  503:  
  504:         /////////////////////////////////////////////////////////////////////////////////
  505:  
  506:         /// <summary>
  507:         ///
  508:         /// </summary>
  509:         [HttpGet]
  510:         [AllowAnonymous]
  511:         [Route("identity/account/verify-code")]
  512:         public async Task<IActionResult> VerifyCode(string provider, bool rememberMe, string returnUrl = null)
  513:         {
  514:             // Require that the user has already logged in via username/password or external login
  515:             var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
  516:             if (user == null)
  517:             {
  518:                 return View("Error");
  519:             }
  520:             return View(new Ia.Ftn.Wa.Models.AccountViewModels.VerifyCodeViewModel { Provider = provider, ReturnUrl = returnUrl, RememberMe = rememberMe });
  521:         }
  522:  
  523:         /////////////////////////////////////////////////////////////////////////////////
  524:  
  525:         /// <summary>
  526:         ///
  527:         /// </summary>
  528:         [HttpPost]
  529:         [AllowAnonymous]
  530:         [ValidateAntiForgeryToken]
  531:         [Route("identity/account/verify-code")]
  532:         public async Task<IActionResult> VerifyCode(Ia.Ftn.Wa.Models.AccountViewModels.VerifyCodeViewModel model)
  533:         {
  534:             if (!ModelState.IsValid)
  535:             {
  536:                 return View(model);
  537:             }
  538:  
  539:             // The following code protects for brute force attacks against the two factor codes.
  540:             // If a user enters incorrect codes for a specified amount of time then the user account
  541:             // will be locked out for a specified amount of time.
  542:             var result = await _signInManager.TwoFactorSignInAsync(model.Provider, model.Code, model.RememberMe, model.RememberBrowser);
  543:             if (result.Succeeded)
  544:             {
  545:                 return RedirectToLocal(model.ReturnUrl);
  546:             }
  547:             if (result.IsLockedOut)
  548:             {
  549:                 _logger.LogWarning(7, "User account locked out.");
  550:                 return View("Lockout");
  551:             }
  552:             else
  553:             {
  554:                 ModelState.AddModelError(string.Empty, "Invalid code.");
  555:                 return View(model);
  556:             }
  557:         }
  558:  
  559:         /////////////////////////////////////////////////////////////////////////////////
  560:  
  561:         /// <summary>
  562:         ///
  563:         /// </summary>
  564:         [HttpGet]
  565:         [AllowAnonymous]
  566:         [Route("identity/account/verify-authenticator-code")]
  567:         public async Task<IActionResult> VerifyAuthenticatorCode(bool rememberMe, string returnUrl = null)
  568:         {
  569:             // Require that the user has already logged in via username/password or external login
  570:             var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
  571:             if (user == null)
  572:             {
  573:                 return View("Error");
  574:             }
  575:             return View(new Ia.Ftn.Wa.Models.AccountViewModels.VerifyAuthenticatorCodeViewModel { ReturnUrl = returnUrl, RememberMe = rememberMe });
  576:         }
  577:  
  578:         /////////////////////////////////////////////////////////////////////////////////
  579:  
  580:         /// <summary>
  581:         ///
  582:         /// </summary>
  583:         [HttpPost]
  584:         [AllowAnonymous]
  585:         [ValidateAntiForgeryToken]
  586:         [Route("identity/account/verify-authenticator-code")]
  587:         public async Task<IActionResult> VerifyAuthenticatorCode(Ia.Ftn.Wa.Models.AccountViewModels.VerifyAuthenticatorCodeViewModel model)
  588:         {
  589:             if (!ModelState.IsValid)
  590:             {
  591:                 return View(model);
  592:             }
  593:  
  594:             // The following code protects for brute force attacks against the two factor codes.
  595:             // If a user enters incorrect codes for a specified amount of time then the user account
  596:             // will be locked out for a specified amount of time.
  597:             var result = await _signInManager.TwoFactorAuthenticatorSignInAsync(model.Code, model.RememberMe, model.RememberBrowser);
  598:             if (result.Succeeded)
  599:             {
  600:                 return RedirectToLocal(model.ReturnUrl);
  601:             }
  602:             if (result.IsLockedOut)
  603:             {
  604:                 _logger.LogWarning(7, "User account locked out.");
  605:                 return View("Lockout");
  606:             }
  607:             else
  608:             {
  609:                 ModelState.AddModelError(string.Empty, "Invalid code.");
  610:                 return View(model);
  611:             }
  612:         }
  613:  
  614:         /////////////////////////////////////////////////////////////////////////////////
  615:  
  616:         /// <summary>
  617:         ///
  618:         /// </summary>
  619:         [HttpGet]
  620:         [AllowAnonymous]
  621:         [Route("identity/account/use-recovery-code")]
  622:         public async Task<IActionResult> UseRecoveryCode(string returnUrl = null)
  623:         {
  624:             // Require that the user has already logged in via username/password or external login
  625:             var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
  626:             if (user == null)
  627:             {
  628:                 return View("Error");
  629:             }
  630:             return View(new Ia.Ftn.Wa.Models.AccountViewModels.UseRecoveryCodeViewModel { ReturnUrl = returnUrl });
  631:         }
  632:  
  633:         /////////////////////////////////////////////////////////////////////////////////
  634:  
  635:         /// <summary>
  636:         ///
  637:         /// </summary>
  638:         [HttpPost]
  639:         [AllowAnonymous]
  640:         [ValidateAntiForgeryToken]
  641:         [Route("identity/account/use-recovery-code")]
  642:         public async Task<IActionResult> UseRecoveryCode(Ia.Ftn.Wa.Models.AccountViewModels.UseRecoveryCodeViewModel model)
  643:         {
  644:             if (!ModelState.IsValid)
  645:             {
  646:                 return View(model);
  647:             }
  648:  
  649:             var result = await _signInManager.TwoFactorRecoveryCodeSignInAsync(model.Code);
  650:             if (result.Succeeded)
  651:             {
  652:                 return RedirectToLocal(model.ReturnUrl);
  653:             }
  654:             else
  655:             {
  656:                 ModelState.AddModelError(string.Empty, "Invalid code.");
  657:                 return View(model);
  658:             }
  659:         }
  660:  
  661:         /////////////////////////////////////////////////////////////////////////////////
  662:         /////////////////////////////////////////////////////////////////////////////////
  663:  
  664:         #region Helpers
  665:  
  666:         private void AddErrors(IdentityResult result)
  667:         {
  668:             foreach (var error in result.Errors)
  669:             {
  670:                 ModelState.AddModelError(string.Empty, error.Description);
  671:             }
  672:         }
  673:  
  674:         private Task<Ia.Ftn.Cl.Models.StaffIdentityUser> GetCurrentUserAsync()
  675:         {
  676:             return _userManager.GetUserAsync(HttpContext.User);
  677:         }
  678:  
  679:         private IActionResult RedirectToLocal(string returnUrl)
  680:         {
  681:             if (Url.IsLocalUrl(returnUrl))
  682:             {
  683:                 return Redirect(returnUrl);
  684:             }
  685:             else
  686:             {
  687:                 return RedirectToAction(nameof(HomeController.Index), "Home");
  688:             }
  689:         }
  690:  
  691:         #endregion
  692:  
  693:         /////////////////////////////////////////////////////////////////////////////////
  694:         /////////////////////////////////////////////////////////////////////////////////
  695:     }
  696: }