From fbde5dae90a6d97add6e728bf282d5265095c553 Mon Sep 17 00:00:00 2001 From: Eric Fleming Date: Fri, 15 Feb 2019 21:54:35 -0500 Subject: [PATCH] Added scaffolded Login page for Identity - Also updated the _LoginPartial to use the new page --- .../Areas/Identity/IdentityHostingStartup.cs | 21 ++++ .../Areas/Identity/Pages/Account/Login.cshtml | 63 +++++++++++ .../Identity/Pages/Account/Login.cshtml.cs | 103 ++++++++++++++++++ .../Pages/Account/_ViewImports.cshtml | 1 + .../Pages/_ValidationScriptsPartial.cshtml | 18 +++ .../Areas/Identity/Pages/_ViewImports.cshtml | 5 + src/Web/Startup.cs | 3 +- src/Web/Views/Shared/_LoginPartial.cshtml | 2 +- src/Web/Web.csproj | 1 + 9 files changed, 215 insertions(+), 2 deletions(-) create mode 100644 src/Web/Areas/Identity/IdentityHostingStartup.cs create mode 100644 src/Web/Areas/Identity/Pages/Account/Login.cshtml create mode 100644 src/Web/Areas/Identity/Pages/Account/Login.cshtml.cs create mode 100644 src/Web/Areas/Identity/Pages/Account/_ViewImports.cshtml create mode 100644 src/Web/Areas/Identity/Pages/_ValidationScriptsPartial.cshtml create mode 100644 src/Web/Areas/Identity/Pages/_ViewImports.cshtml diff --git a/src/Web/Areas/Identity/IdentityHostingStartup.cs b/src/Web/Areas/Identity/IdentityHostingStartup.cs new file mode 100644 index 0000000..57a55bd --- /dev/null +++ b/src/Web/Areas/Identity/IdentityHostingStartup.cs @@ -0,0 +1,21 @@ +using System; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Identity.UI; +using Microsoft.EntityFrameworkCore; +using Microsoft.eShopWeb.Infrastructure.Identity; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +[assembly: HostingStartup(typeof(Microsoft.eShopWeb.Web.Areas.Identity.IdentityHostingStartup))] +namespace Microsoft.eShopWeb.Web.Areas.Identity +{ + public class IdentityHostingStartup : IHostingStartup + { + public void Configure(IWebHostBuilder builder) + { + builder.ConfigureServices((context, services) => { + }); + } + } +} \ No newline at end of file diff --git a/src/Web/Areas/Identity/Pages/Account/Login.cshtml b/src/Web/Areas/Identity/Pages/Account/Login.cshtml new file mode 100644 index 0000000..e3bbc04 --- /dev/null +++ b/src/Web/Areas/Identity/Pages/Account/Login.cshtml @@ -0,0 +1,63 @@ +@page +@model LoginModel + +@{ + ViewData["Title"] = "Log in"; +} + +

@ViewData["Title"]

+
+
+
+
+
+

Use a local account to log in.

+
+
+
+ + + +
+
+ + + +
+
+
+ +
+
+
+ +
+ +

+ Note that for demo purposes you don't need to register and can login with these credentials: +

+

+ User: demouser@microsoft.com +

+

+ Password: Pass@word1 +

+
+
+
+
+
+ +@section Scripts { + +} diff --git a/src/Web/Areas/Identity/Pages/Account/Login.cshtml.cs b/src/Web/Areas/Identity/Pages/Account/Login.cshtml.cs new file mode 100644 index 0000000..72ecf94 --- /dev/null +++ b/src/Web/Areas/Identity/Pages/Account/Login.cshtml.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.eShopWeb.Infrastructure.Identity; +using Microsoft.Extensions.Logging; + +namespace Microsoft.eShopWeb.Web.Areas.Identity.Pages.Account +{ + [AllowAnonymous] + public class LoginModel : PageModel + { + private readonly SignInManager _signInManager; + private readonly ILogger _logger; + + public LoginModel(SignInManager signInManager, ILogger logger) + { + _signInManager = signInManager; + _logger = logger; + } + + [BindProperty] + public InputModel Input { get; set; } + + public IList ExternalLogins { get; set; } + + public string ReturnUrl { get; set; } + + [TempData] + public string ErrorMessage { get; set; } + + public class InputModel + { + [Required] + [EmailAddress] + public string Email { get; set; } + + [Required] + [DataType(DataType.Password)] + public string Password { get; set; } + + [Display(Name = "Remember me?")] + public bool RememberMe { get; set; } + } + + public async Task OnGetAsync(string returnUrl = null) + { + if (!string.IsNullOrEmpty(ErrorMessage)) + { + ModelState.AddModelError(string.Empty, ErrorMessage); + } + + returnUrl = returnUrl ?? Url.Content("~/"); + + // Clear the existing external cookie to ensure a clean login process + await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme); + + ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList(); + + ReturnUrl = returnUrl; + } + + public async Task OnPostAsync(string returnUrl = null) + { + returnUrl = returnUrl ?? Url.Content("~/"); + + if (ModelState.IsValid) + { + // This doesn't count login failures towards account lockout + // To enable password failures to trigger account lockout, set lockoutOnFailure: true + var result = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: true); + if (result.Succeeded) + { + _logger.LogInformation("User logged in."); + return LocalRedirect(returnUrl); + } + if (result.RequiresTwoFactor) + { + return RedirectToPage("./LoginWith2fa", new { ReturnUrl = returnUrl, RememberMe = Input.RememberMe }); + } + if (result.IsLockedOut) + { + _logger.LogWarning("User account locked out."); + return RedirectToPage("./Lockout"); + } + else + { + ModelState.AddModelError(string.Empty, "Invalid login attempt."); + return Page(); + } + } + + // If we got this far, something failed, redisplay form + return Page(); + } + } +} diff --git a/src/Web/Areas/Identity/Pages/Account/_ViewImports.cshtml b/src/Web/Areas/Identity/Pages/Account/_ViewImports.cshtml new file mode 100644 index 0000000..65840eb --- /dev/null +++ b/src/Web/Areas/Identity/Pages/Account/_ViewImports.cshtml @@ -0,0 +1 @@ +@using Microsoft.eShopWeb.Web.Areas.Identity.Pages.Account \ No newline at end of file diff --git a/src/Web/Areas/Identity/Pages/_ValidationScriptsPartial.cshtml b/src/Web/Areas/Identity/Pages/_ValidationScriptsPartial.cshtml new file mode 100644 index 0000000..bacc0ae --- /dev/null +++ b/src/Web/Areas/Identity/Pages/_ValidationScriptsPartial.cshtml @@ -0,0 +1,18 @@ + + + + + + + + diff --git a/src/Web/Areas/Identity/Pages/_ViewImports.cshtml b/src/Web/Areas/Identity/Pages/_ViewImports.cshtml new file mode 100644 index 0000000..82c4f63 --- /dev/null +++ b/src/Web/Areas/Identity/Pages/_ViewImports.cshtml @@ -0,0 +1,5 @@ +@using Microsoft.AspNetCore.Identity +@using Microsoft.eShopWeb.Web.Areas.Identity +@using Microsoft.eShopWeb.Infrastructure.Identity +@namespace Microsoft.eShopWeb.Web.Areas.Identity.Pages +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers diff --git a/src/Web/Startup.cs b/src/Web/Startup.cs index f35bd23..93d399a 100644 --- a/src/Web/Startup.cs +++ b/src/Web/Startup.cs @@ -114,11 +114,13 @@ namespace Microsoft.eShopWeb.Web { options.Conventions.Add(new RouteTokenTransformerConvention( new SlugifyParameterTransformer())); + } ) .AddRazorPagesOptions(options => { options.Conventions.AuthorizePage("/Basket/Checkout"); + options.AllowAreas = true; }) .SetCompatibilityVersion(CompatibilityVersion.Version_2_2); @@ -164,7 +166,6 @@ namespace Microsoft.eShopWeb.Web { options.Cookie.HttpOnly = true; options.ExpireTimeSpan = TimeSpan.FromHours(1); - options.LoginPath = "/Account/Signin"; options.LogoutPath = "/Account/Signout"; options.Cookie = new CookieBuilder { diff --git a/src/Web/Views/Shared/_LoginPartial.cshtml b/src/Web/Views/Shared/_LoginPartial.cshtml index fce87e4..1f454f6 100644 --- a/src/Web/Views/Shared/_LoginPartial.cshtml +++ b/src/Web/Views/Shared/_LoginPartial.cshtml @@ -40,7 +40,7 @@ else
diff --git a/src/Web/Web.csproj b/src/Web/Web.csproj index c27268f..c27cb26 100644 --- a/src/Web/Web.csproj +++ b/src/Web/Web.csproj @@ -18,6 +18,7 @@ +