From e19ade962a76572318074b50fc9fec841a43ba65 Mon Sep 17 00:00:00 2001 From: Eric Fleming Date: Fri, 21 Jun 2019 21:18:50 -0400 Subject: [PATCH 1/4] Removing unused UrlHelperExtension --- src/Web/Extensions/UrlHelperExtensions.cs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/Web/Extensions/UrlHelperExtensions.cs b/src/Web/Extensions/UrlHelperExtensions.cs index 95d971a..c5c940b 100644 --- a/src/Web/Extensions/UrlHelperExtensions.cs +++ b/src/Web/Extensions/UrlHelperExtensions.cs @@ -12,14 +12,5 @@ namespace Microsoft.AspNetCore.Mvc values: new { userId, code }, protocol: scheme); } - - public static string ResetPasswordCallbackLink(this IUrlHelper urlHelper, string userId, string code, string scheme) - { - return urlHelper.Action( - action: nameof(AccountController.ResetPassword), - controller: "Account", - values: new { userId, code }, - protocol: scheme); - } } } From c2e37b9019d06e8c03ea934c832abf0d93d4bdea Mon Sep 17 00:00:00 2001 From: Eric Fleming Date: Fri, 21 Jun 2019 21:39:52 -0400 Subject: [PATCH 2/4] Adding confirmEmail scaffolding - Also updating the UrlHelpExtensions to not have a dependency on the AccountController --- .../Pages/Account/ConfirmEmail.cshtml | 12 +++++ .../Pages/Account/ConfirmEmail.cshtml.cs | 45 +++++++++++++++++++ src/Web/Extensions/UrlHelperExtensions.cs | 8 ++-- 3 files changed, 60 insertions(+), 5 deletions(-) create mode 100644 src/Web/Areas/Identity/Pages/Account/ConfirmEmail.cshtml create mode 100644 src/Web/Areas/Identity/Pages/Account/ConfirmEmail.cshtml.cs diff --git a/src/Web/Areas/Identity/Pages/Account/ConfirmEmail.cshtml b/src/Web/Areas/Identity/Pages/Account/ConfirmEmail.cshtml new file mode 100644 index 0000000..401bf32 --- /dev/null +++ b/src/Web/Areas/Identity/Pages/Account/ConfirmEmail.cshtml @@ -0,0 +1,12 @@ +@page +@model ConfirmEmailModel +@{ + ViewData["Title"] = "Confirm email"; +} + +

@ViewData["Title"]

+
+

+ Thank you for confirming your email. +

+
diff --git a/src/Web/Areas/Identity/Pages/Account/ConfirmEmail.cshtml.cs b/src/Web/Areas/Identity/Pages/Account/ConfirmEmail.cshtml.cs new file mode 100644 index 0000000..b113768 --- /dev/null +++ b/src/Web/Areas/Identity/Pages/Account/ConfirmEmail.cshtml.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.eShopWeb.Infrastructure.Identity; + +namespace Microsoft.eShopWeb.Web.Areas.Identity.Pages.Account +{ + [AllowAnonymous] + public class ConfirmEmailModel : PageModel + { + private readonly UserManager _userManager; + + public ConfirmEmailModel(UserManager userManager) + { + _userManager = userManager; + } + + public async Task 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}':"); + } + + return Page(); + } + } +} diff --git a/src/Web/Extensions/UrlHelperExtensions.cs b/src/Web/Extensions/UrlHelperExtensions.cs index c5c940b..6edc697 100644 --- a/src/Web/Extensions/UrlHelperExtensions.cs +++ b/src/Web/Extensions/UrlHelperExtensions.cs @@ -1,14 +1,12 @@ -using Microsoft.eShopWeb.Web.Controllers; - -namespace Microsoft.AspNetCore.Mvc +namespace Microsoft.AspNetCore.Mvc { public static class UrlHelperExtensions { public static string EmailConfirmationLink(this IUrlHelper urlHelper, string userId, string code, string scheme) { return urlHelper.Action( - action: nameof(AccountController.ConfirmEmail), - controller: "Account", + action: "GET", + controller: "ConfirmEmail", values: new { userId, code }, protocol: scheme); } From 89ab19cf193e0258c208f0f75eb744b3bf85304f Mon Sep 17 00:00:00 2001 From: Eric Fleming Date: Fri, 21 Jun 2019 21:40:02 -0400 Subject: [PATCH 3/4] Removing the AccountController --- src/Web/Controllers/AccountController.cs | 232 ----------------------- 1 file changed, 232 deletions(-) delete mode 100644 src/Web/Controllers/AccountController.cs diff --git a/src/Web/Controllers/AccountController.cs b/src/Web/Controllers/AccountController.cs deleted file mode 100644 index 281497b..0000000 --- a/src/Web/Controllers/AccountController.cs +++ /dev/null @@ -1,232 +0,0 @@ -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.eShopWeb.ApplicationCore.Interfaces; -using Microsoft.eShopWeb.Infrastructure.Identity; -using Microsoft.eShopWeb.Web.ViewModels.Account; -using System; -using System.Threading.Tasks; - -namespace Microsoft.eShopWeb.Web.Controllers -{ - [ApiExplorerSettings(IgnoreApi = true)] - [Route("[controller]/[action]")] - [Authorize] // Controllers that mainly require Authorization still use Controller/View; other pages use Pages - public class AccountController : Controller - { - private readonly UserManager _userManager; - private readonly SignInManager _signInManager; - private readonly IBasketService _basketService; - private readonly IAppLogger _logger; - - public AccountController( - UserManager userManager, - SignInManager signInManager, - IBasketService basketService, - IAppLogger logger) - { - _userManager = userManager; - _signInManager = signInManager; - _basketService = basketService; - _logger = logger; - } - - // GET: /Account/SignIn - [HttpGet] - [AllowAnonymous] - public async Task SignIn(string returnUrl = null) - { - await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme); - - ViewData["ReturnUrl"] = returnUrl; - if (!String.IsNullOrEmpty(returnUrl) && - returnUrl.IndexOf("checkout", StringComparison.OrdinalIgnoreCase) >= 0) - { - ViewData["ReturnUrl"] = "/Basket/Index"; - } - - return View(); - } - - // POST: /Account/SignIn - [HttpPost] - [AllowAnonymous] - [ValidateAntiForgeryToken] - public async Task SignIn(LoginViewModel model, string returnUrl = null) - { - if (!ModelState.IsValid) - { - return View(model); - } - ViewData["ReturnUrl"] = returnUrl; - - var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: false); - if (result.RequiresTwoFactor) - { - return RedirectToAction(nameof(LoginWith2fa), new { returnUrl, model.RememberMe }); - } - if (result.Succeeded) - { - string anonymousBasketId = Request.Cookies[Constants.BASKET_COOKIENAME]; - if (!String.IsNullOrEmpty(anonymousBasketId)) - { - await _basketService.TransferBasketAsync(anonymousBasketId, model.Email); - Response.Cookies.Delete(Constants.BASKET_COOKIENAME); - } - return RedirectToLocal(returnUrl); - } - ModelState.AddModelError(string.Empty, "Invalid login attempt."); - return View(model); - } - - [HttpGet] - [AllowAnonymous] - public async Task LoginWith2fa(bool rememberMe, string returnUrl = null) - { - // Ensure the user has gone through the username & password screen first - var user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); - - if (user == null) - { - throw new ApplicationException($"Unable to load two-factor authentication user."); - } - - var model = new LoginWith2faViewModel { RememberMe = rememberMe }; - ViewData["ReturnUrl"] = returnUrl; - - return View(model); - } - - [HttpPost] - [AllowAnonymous] - [ValidateAntiForgeryToken] - public async Task LoginWith2fa(LoginWith2faViewModel model, bool rememberMe, string returnUrl = null) - { - if (!ModelState.IsValid) - { - return View(model); - } - - var user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); - if (user == null) - { - throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - var authenticatorCode = model.TwoFactorCode.Replace(" ", string.Empty).Replace("-", string.Empty); - - var result = await _signInManager.TwoFactorAuthenticatorSignInAsync(authenticatorCode, rememberMe, model.RememberMachine); - - if (result.Succeeded) - { - _logger.LogInformation("User with ID {UserId} logged in with 2fa.", user.Id); - return RedirectToLocal(returnUrl); - } - else if (result.IsLockedOut) - { - _logger.LogWarning("User with ID {UserId} account locked out.", user.Id); - return RedirectToAction(nameof(Lockout)); - } - else - { - _logger.LogWarning("Invalid authenticator code entered for user with ID {UserId}.", user.Id); - ModelState.AddModelError(string.Empty, "Invalid authenticator code."); - return View(); - } - } - - [HttpGet] - [AllowAnonymous] - public IActionResult Lockout() - { - return View(); - } - - - [HttpPost] - [ValidateAntiForgeryToken] - public async Task SignOut() - { - await _signInManager.SignOutAsync(); - - return RedirectToPage("/Index"); - } - - [AllowAnonymous] - [HttpGet] - public IActionResult Register() - { - return View(); - } - - [HttpPost] - [AllowAnonymous] - [ValidateAntiForgeryToken] - public async Task Register(RegisterViewModel model, string returnUrl = null) - { - if (ModelState.IsValid) - { - var user = new ApplicationUser { UserName = model.Email, Email = model.Email }; - var result = await _userManager.CreateAsync(user, model.Password); - if (result.Succeeded) - { - await _signInManager.SignInAsync(user, isPersistent: false); - return RedirectToLocal(returnUrl); - } - AddErrors(result); - } - // If we got this far, something failed, redisplay form - return View(model); - } - - [HttpGet] - [AllowAnonymous] - public async Task ConfirmEmail(string userId, string code) - { - if (userId == null || code == null) - { - return RedirectToPage("/Index"); - } - var user = await _userManager.FindByIdAsync(userId); - if (user == null) - { - throw new ApplicationException($"Unable to load user with ID '{userId}'."); - } - var result = await _userManager.ConfirmEmailAsync(user, code); - return View(result.Succeeded ? "ConfirmEmail" : "Error"); - } - - [HttpGet] - [AllowAnonymous] - public IActionResult ResetPassword(string code = null) - { - if (code == null) - { - throw new ApplicationException("A code must be supplied for password reset."); - } - var model = new ResetPasswordViewModel { Code = code }; - return View(model); - } - - private IActionResult RedirectToLocal(string returnUrl) - { - if (Url.IsLocalUrl(returnUrl)) - { - return Redirect(returnUrl); - } - else - { - return RedirectToPage("/Index"); - } - } - - private void AddErrors(IdentityResult result) - { - foreach (var error in result.Errors) - { - ModelState.AddModelError("", error.Description); - } - } - } -} From b3d855847bec0de45ee12bb970e12782fee86afb Mon Sep 17 00:00:00 2001 From: Eric Fleming Date: Fri, 21 Jun 2019 21:40:20 -0400 Subject: [PATCH 4/4] Updating route in test to use the proper login route --- .../FunctionalTests/Web/Controllers/AccountControllerSignIn.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/FunctionalTests/Web/Controllers/AccountControllerSignIn.cs b/tests/FunctionalTests/Web/Controllers/AccountControllerSignIn.cs index 0035a83..c706368 100644 --- a/tests/FunctionalTests/Web/Controllers/AccountControllerSignIn.cs +++ b/tests/FunctionalTests/Web/Controllers/AccountControllerSignIn.cs @@ -81,7 +81,7 @@ namespace Microsoft.eShopWeb.FunctionalTests.Web.Controllers keyValues.Add(new KeyValuePair("__RequestVerificationToken", token)); var formContent = new FormUrlEncodedContent(keyValues); - var postResponse = await Client.PostAsync("/account/sign-in", formContent); + var postResponse = await Client.PostAsync("/identity/account/login", formContent); Assert.Equal(HttpStatusCode.Redirect, postResponse.StatusCode); Assert.Equal(new System.Uri("/", UriKind.Relative), postResponse.Headers.Location); }