scaffolding identity
This commit is contained in:
parent
c81f7c08f3
commit
143f708cff
@ -11,6 +11,9 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Identity" Version="2.2.0" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="5.0.11" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="5.0.11" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.11" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.11" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.11">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.11">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
@ -5,10 +5,12 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
using Microsoft.EntityFrameworkCore.Design;
|
using Microsoft.EntityFrameworkCore.Design;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace Selector.Model
|
namespace Selector.Model
|
||||||
{
|
{
|
||||||
|
|
||||||
public class SelectorContext : DbContext
|
public class SelectorContext : IdentityDbContext
|
||||||
{
|
{
|
||||||
public DbSet<Watcher> Watcher { get; set; }
|
public DbSet<Watcher> Watcher { get; set; }
|
||||||
|
|
||||||
@ -24,6 +26,7 @@ namespace Selector.Model
|
|||||||
|
|
||||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
{
|
{
|
||||||
|
base.OnModelCreating(modelBuilder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
22
Selector.Web/Areas/Identity/IdentityHostingStartup.cs
Normal file
22
Selector.Web/Areas/Identity/IdentityHostingStartup.cs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
using Microsoft.AspNetCore.Identity.UI;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Selector.Model;
|
||||||
|
|
||||||
|
[assembly: HostingStartup(typeof(Selector.Web.Areas.Identity.IdentityHostingStartup))]
|
||||||
|
namespace Selector.Web.Areas.Identity
|
||||||
|
{
|
||||||
|
public class IdentityHostingStartup : IHostingStartup
|
||||||
|
{
|
||||||
|
public void Configure(IWebHostBuilder builder)
|
||||||
|
{
|
||||||
|
builder.ConfigureServices((context, services) => {
|
||||||
|
//services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
85
Selector.Web/Areas/Identity/Pages/Account/Login.cshtml
Normal file
85
Selector.Web/Areas/Identity/Pages/Account/Login.cshtml
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
@page
|
||||||
|
@model LoginModel
|
||||||
|
|
||||||
|
@{
|
||||||
|
ViewData["Title"] = "Log in";
|
||||||
|
}
|
||||||
|
|
||||||
|
<h1>@ViewData["Title"]</h1>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<section>
|
||||||
|
<form id="account" method="post">
|
||||||
|
<h4>Use a local account to log in.</h4>
|
||||||
|
<hr />
|
||||||
|
<div asp-validation-summary="All" class="text-danger"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="Input.Email"></label>
|
||||||
|
<input asp-for="Input.Email" class="form-control" />
|
||||||
|
<span asp-validation-for="Input.Email" class="text-danger"></span>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="Input.Password"></label>
|
||||||
|
<input asp-for="Input.Password" class="form-control" />
|
||||||
|
<span asp-validation-for="Input.Password" class="text-danger"></span>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="checkbox">
|
||||||
|
<label asp-for="Input.RememberMe">
|
||||||
|
<input asp-for="Input.RememberMe" />
|
||||||
|
@Html.DisplayNameFor(m => m.Input.RememberMe)
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<button type="submit" class="btn btn-primary">Log in</button>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<p>
|
||||||
|
<a id="forgot-password" asp-page="./ForgotPassword">Forgot your password?</a>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<a asp-page="./Register" asp-route-returnUrl="@Model.ReturnUrl">Register as a new user</a>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<a id="resend-confirmation" asp-page="./ResendEmailConfirmation">Resend email confirmation</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 col-md-offset-2">
|
||||||
|
<section>
|
||||||
|
<h4>Use another service to log in.</h4>
|
||||||
|
<hr />
|
||||||
|
@{
|
||||||
|
if ((Model.ExternalLogins?.Count ?? 0) == 0)
|
||||||
|
{
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
There are no external authentication services configured. See <a href="https://go.microsoft.com/fwlink/?LinkID=532715">this article</a>
|
||||||
|
for details on setting up this ASP.NET application to support logging in via external services.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<form id="external-account" asp-page="./ExternalLogin" asp-route-returnUrl="@Model.ReturnUrl" method="post" class="form-horizontal">
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
@foreach (var provider in Model.ExternalLogins)
|
||||||
|
{
|
||||||
|
<button type="submit" class="btn btn-primary" name="provider" value="@provider.Name" title="Log in using your @provider.DisplayName account">@provider.DisplayName</button>
|
||||||
|
}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@section Scripts {
|
||||||
|
<partial name="_ValidationScriptsPartial" />
|
||||||
|
}
|
110
Selector.Web/Areas/Identity/Pages/Account/Login.cshtml.cs
Normal file
110
Selector.Web/Areas/Identity/Pages/Account/Login.cshtml.cs
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.Encodings.Web;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Authentication;
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
using Microsoft.AspNetCore.Identity.UI.Services;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Selector.Web.Areas.Identity.Pages.Account
|
||||||
|
{
|
||||||
|
[AllowAnonymous]
|
||||||
|
public class LoginModel : PageModel
|
||||||
|
{
|
||||||
|
private readonly UserManager<IdentityUser> _userManager;
|
||||||
|
private readonly SignInManager<IdentityUser> _signInManager;
|
||||||
|
private readonly ILogger<LoginModel> _logger;
|
||||||
|
|
||||||
|
public LoginModel(SignInManager<IdentityUser> signInManager,
|
||||||
|
ILogger<LoginModel> logger,
|
||||||
|
UserManager<IdentityUser> userManager)
|
||||||
|
{
|
||||||
|
_userManager = userManager;
|
||||||
|
_signInManager = signInManager;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BindProperty]
|
||||||
|
public InputModel Input { get; set; }
|
||||||
|
|
||||||
|
public IList<AuthenticationScheme> 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 ??= 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<IActionResult> OnPostAsync(string returnUrl = null)
|
||||||
|
{
|
||||||
|
returnUrl ??= Url.Content("~/");
|
||||||
|
|
||||||
|
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
|
||||||
|
|
||||||
|
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: false);
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
21
Selector.Web/Areas/Identity/Pages/Account/Logout.cshtml
Normal file
21
Selector.Web/Areas/Identity/Pages/Account/Logout.cshtml
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
@page
|
||||||
|
@model LogoutModel
|
||||||
|
@{
|
||||||
|
ViewData["Title"] = "Log out";
|
||||||
|
}
|
||||||
|
|
||||||
|
<header>
|
||||||
|
<h1>@ViewData["Title"]</h1>
|
||||||
|
@{
|
||||||
|
if (User.Identity.IsAuthenticated)
|
||||||
|
{
|
||||||
|
<form class="form-inline" asp-area="Identity" asp-page="/Account/Logout" asp-route-returnUrl="@Url.Page("/", new { area = "" })" method="post">
|
||||||
|
<button type="submit" class="nav-link btn btn-link text-dark">Click here to Logout</button>
|
||||||
|
</form>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<p>You have successfully logged out of the application.</p>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</header>
|
43
Selector.Web/Areas/Identity/Pages/Account/Logout.cshtml.cs
Normal file
43
Selector.Web/Areas/Identity/Pages/Account/Logout.cshtml.cs
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
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.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Selector.Web.Areas.Identity.Pages.Account
|
||||||
|
{
|
||||||
|
[AllowAnonymous]
|
||||||
|
public class LogoutModel : PageModel
|
||||||
|
{
|
||||||
|
private readonly SignInManager<IdentityUser> _signInManager;
|
||||||
|
private readonly ILogger<LogoutModel> _logger;
|
||||||
|
|
||||||
|
public LogoutModel(SignInManager<IdentityUser> signInManager, ILogger<LogoutModel> logger)
|
||||||
|
{
|
||||||
|
_signInManager = signInManager;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnGet()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IActionResult> OnPost(string returnUrl = null)
|
||||||
|
{
|
||||||
|
await _signInManager.SignOutAsync();
|
||||||
|
_logger.LogInformation("User logged out.");
|
||||||
|
if (returnUrl != null)
|
||||||
|
{
|
||||||
|
return LocalRedirect(returnUrl);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return RedirectToPage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
67
Selector.Web/Areas/Identity/Pages/Account/Register.cshtml
Normal file
67
Selector.Web/Areas/Identity/Pages/Account/Register.cshtml
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
@page
|
||||||
|
@model RegisterModel
|
||||||
|
@{
|
||||||
|
ViewData["Title"] = "Register";
|
||||||
|
}
|
||||||
|
|
||||||
|
<h1>@ViewData["Title"]</h1>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<form asp-route-returnUrl="@Model.ReturnUrl" method="post">
|
||||||
|
<h4>Create a new account.</h4>
|
||||||
|
<hr />
|
||||||
|
<div asp-validation-summary="All" class="text-danger"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="Input.Email"></label>
|
||||||
|
<input asp-for="Input.Email" class="form-control" />
|
||||||
|
<span asp-validation-for="Input.Email" class="text-danger"></span>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="Input.Password"></label>
|
||||||
|
<input asp-for="Input.Password" class="form-control" />
|
||||||
|
<span asp-validation-for="Input.Password" class="text-danger"></span>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="Input.ConfirmPassword"></label>
|
||||||
|
<input asp-for="Input.ConfirmPassword" class="form-control" />
|
||||||
|
<span asp-validation-for="Input.ConfirmPassword" class="text-danger"></span>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">Register</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 col-md-offset-2">
|
||||||
|
<section>
|
||||||
|
<h4>Use another service to register.</h4>
|
||||||
|
<hr />
|
||||||
|
@{
|
||||||
|
if ((Model.ExternalLogins?.Count ?? 0) == 0)
|
||||||
|
{
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
There are no external authentication services configured. See <a href="https://go.microsoft.com/fwlink/?LinkID=532715">this article</a>
|
||||||
|
for details on setting up this ASP.NET application to support logging in via external services.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<form id="external-account" asp-page="./ExternalLogin" asp-route-returnUrl="@Model.ReturnUrl" method="post" class="form-horizontal">
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
@foreach (var provider in Model.ExternalLogins)
|
||||||
|
{
|
||||||
|
<button type="submit" class="btn btn-primary" name="provider" value="@provider.Name" title="Log in using your @provider.DisplayName account">@provider.DisplayName</button>
|
||||||
|
}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@section Scripts {
|
||||||
|
<partial name="_ValidationScriptsPartial" />
|
||||||
|
}
|
114
Selector.Web/Areas/Identity/Pages/Account/Register.cshtml.cs
Normal file
114
Selector.Web/Areas/Identity/Pages/Account/Register.cshtml.cs
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Encodings.Web;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Authentication;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
using Microsoft.AspNetCore.Identity.UI.Services;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||||
|
using Microsoft.AspNetCore.WebUtilities;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Selector.Web.Areas.Identity.Pages.Account
|
||||||
|
{
|
||||||
|
[AllowAnonymous]
|
||||||
|
public class RegisterModel : PageModel
|
||||||
|
{
|
||||||
|
private readonly SignInManager<IdentityUser> _signInManager;
|
||||||
|
private readonly UserManager<IdentityUser> _userManager;
|
||||||
|
private readonly ILogger<RegisterModel> _logger;
|
||||||
|
private readonly IEmailSender _emailSender;
|
||||||
|
|
||||||
|
public RegisterModel(
|
||||||
|
UserManager<IdentityUser> userManager,
|
||||||
|
SignInManager<IdentityUser> signInManager,
|
||||||
|
ILogger<RegisterModel> logger,
|
||||||
|
IEmailSender emailSender)
|
||||||
|
{
|
||||||
|
_userManager = userManager;
|
||||||
|
_signInManager = signInManager;
|
||||||
|
_logger = logger;
|
||||||
|
_emailSender = emailSender;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BindProperty]
|
||||||
|
public InputModel Input { get; set; }
|
||||||
|
|
||||||
|
public string ReturnUrl { get; set; }
|
||||||
|
|
||||||
|
public IList<AuthenticationScheme> ExternalLogins { get; set; }
|
||||||
|
|
||||||
|
public class InputModel
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
[EmailAddress]
|
||||||
|
[Display(Name = "Email")]
|
||||||
|
public string Email { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
//[StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
|
||||||
|
[DataType(DataType.Password)]
|
||||||
|
[Display(Name = "Password")]
|
||||||
|
public string Password { get; set; }
|
||||||
|
|
||||||
|
[DataType(DataType.Password)]
|
||||||
|
[Display(Name = "Confirm password")]
|
||||||
|
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
|
||||||
|
public string ConfirmPassword { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task OnGetAsync(string returnUrl = null)
|
||||||
|
{
|
||||||
|
ReturnUrl = returnUrl;
|
||||||
|
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
|
||||||
|
{
|
||||||
|
returnUrl ??= Url.Content("~/");
|
||||||
|
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
|
||||||
|
if (ModelState.IsValid)
|
||||||
|
{
|
||||||
|
var user = new IdentityUser { UserName = Input.Email, Email = Input.Email };
|
||||||
|
var result = await _userManager.CreateAsync(user, Input.Password);
|
||||||
|
if (result.Succeeded)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("User created a new account with password.");
|
||||||
|
|
||||||
|
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
|
||||||
|
code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
|
||||||
|
var callbackUrl = Url.Page(
|
||||||
|
"/Account/ConfirmEmail",
|
||||||
|
pageHandler: null,
|
||||||
|
values: new { area = "Identity", userId = user.Id, code = code, returnUrl = returnUrl },
|
||||||
|
protocol: Request.Scheme);
|
||||||
|
|
||||||
|
await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
|
||||||
|
$"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
|
||||||
|
|
||||||
|
if (_userManager.Options.SignIn.RequireConfirmedAccount)
|
||||||
|
{
|
||||||
|
return RedirectToPage("RegisterConfirmation", new { email = Input.Email, returnUrl = returnUrl });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await _signInManager.SignInAsync(user, isPersistent: false);
|
||||||
|
return LocalRedirect(returnUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach (var error in result.Errors)
|
||||||
|
{
|
||||||
|
ModelState.AddModelError(string.Empty, error.Description);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we got this far, something failed, redisplay form
|
||||||
|
return Page();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
@using Selector.Web.Areas.Identity.Pages.Account
|
@ -0,0 +1,18 @@
|
|||||||
|
<environment include="Development">
|
||||||
|
<script src="~/Identity/lib/jquery-validation/dist/jquery.validate.js"></script>
|
||||||
|
<script src="~/Identity/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
|
||||||
|
</environment>
|
||||||
|
<environment exclude="Development">
|
||||||
|
<script src="https://ajax.aspnetcdn.com/ajax/jquery.validate/1.17.0/jquery.validate.min.js"
|
||||||
|
asp-fallback-src="~/Identity/lib/jquery-validation/dist/jquery.validate.min.js"
|
||||||
|
asp-fallback-test="window.jQuery && window.jQuery.validator"
|
||||||
|
crossorigin="anonymous"
|
||||||
|
integrity="sha384-rZfj/ogBloos6wzLGpPkkOr/gpkBNLZ6b6yLy4o+ok+t/SAKlL5mvXLr0OXNi1Hp">
|
||||||
|
</script>
|
||||||
|
<script src="https://ajax.aspnetcdn.com/ajax/jquery.validation.unobtrusive/3.2.9/jquery.validate.unobtrusive.min.js"
|
||||||
|
asp-fallback-src="~/Identity/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"
|
||||||
|
asp-fallback-test="window.jQuery && window.jQuery.validator && window.jQuery.validator.unobtrusive"
|
||||||
|
crossorigin="anonymous"
|
||||||
|
integrity="sha384-ifv0TYDWxBHzvAk2Z0n8R434FL1Rlv/Av18DXE43N/1rvHyOG4izKst0f2iSLdds">
|
||||||
|
</script>
|
||||||
|
</environment>
|
4
Selector.Web/Areas/Identity/Pages/_ViewImports.cshtml
Normal file
4
Selector.Web/Areas/Identity/Pages/_ViewImports.cshtml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
@using Microsoft.AspNetCore.Identity
|
||||||
|
@using Selector.Web.Areas.Identity
|
||||||
|
@using Selector.Web.Areas.Identity.Pages
|
||||||
|
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
4
Selector.Web/Areas/Identity/Pages/_ViewStart.cshtml
Normal file
4
Selector.Web/Areas/Identity/Pages/_ViewStart.cshtml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
|
||||||
|
@{
|
||||||
|
Layout = "/Pages/Shared/_Layout.cshtml";
|
||||||
|
}
|
@ -7,6 +7,7 @@
|
|||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<h1 class="display-4">Welcome</h1>
|
<h1 class="display-4">Welcome</h1>
|
||||||
<p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
|
<p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
|
||||||
|
<partial name="_LoginPartial" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@section Scripts {
|
@section Scripts {
|
||||||
|
27
Selector.Web/Pages/Shared/_LoginPartial.cshtml
Normal file
27
Selector.Web/Pages/Shared/_LoginPartial.cshtml
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
@using Microsoft.AspNetCore.Identity
|
||||||
|
|
||||||
|
@inject SignInManager<IdentityUser> SignInManager
|
||||||
|
@inject UserManager<IdentityUser> UserManager
|
||||||
|
|
||||||
|
<ul class="navbar-nav">
|
||||||
|
@if (SignInManager.IsSignedIn(User))
|
||||||
|
{
|
||||||
|
<li class="nav-item">
|
||||||
|
<a id="manage" class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Manage/Index" title="Manage">Hello @UserManager.GetUserName(User)!</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<form id="logoutForm" class="form-inline" asp-area="Identity" asp-page="/Account/Logout" asp-route-returnUrl="@Url.Page("/Index", new { area = "" })">
|
||||||
|
<button id="logout" type="submit" class="nav-link btn btn-link text-dark">Logout</button>
|
||||||
|
</form>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link text-dark" id="register" asp-area="Identity" asp-page="/Account/Register">Register</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link text-dark" id="login" asp-area="Identity" asp-page="/Account/Login">Login</a>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
</ul>
|
@ -12,9 +12,16 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="5.0.11" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="5.0.11" />
|
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="5.0.11" />
|
||||||
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="5.0.11" />
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="5.0.11" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.2.0" />
|
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.2.0" />
|
||||||
|
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.11">
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.TypeScript.MSBuild" Version="4.4.4">
|
<PackageReference Include="Microsoft.TypeScript.MSBuild" Version="4.4.4">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
@ -9,6 +9,7 @@ using Microsoft.Extensions.Configuration;
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
using Selector.Model;
|
using Selector.Model;
|
||||||
@ -33,6 +34,44 @@ namespace Selector.Web
|
|||||||
services.AddDbContext<SelectorContext>(options =>
|
services.AddDbContext<SelectorContext>(options =>
|
||||||
options.UseNpgsql(Configuration.GetConnectionString("Default"))
|
options.UseNpgsql(Configuration.GetConnectionString("Default"))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
services.AddIdentity<IdentityUser, IdentityRole>()
|
||||||
|
.AddEntityFrameworkStores<SelectorContext>()
|
||||||
|
.AddDefaultUI()
|
||||||
|
.AddDefaultTokenProviders();
|
||||||
|
|
||||||
|
services.Configure<IdentityOptions>(options =>
|
||||||
|
{
|
||||||
|
// Password settings.
|
||||||
|
//options.Password.RequireDigit = true;
|
||||||
|
//options.Password.RequireLowercase = true;
|
||||||
|
//options.Password.RequireNonAlphanumeric = true;
|
||||||
|
//options.Password.RequireUppercase = true;
|
||||||
|
options.Password.RequiredLength = 3;
|
||||||
|
options.Password.RequiredUniqueChars = 1;
|
||||||
|
|
||||||
|
// Lockout settings.
|
||||||
|
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
|
||||||
|
options.Lockout.MaxFailedAccessAttempts = 5;
|
||||||
|
options.Lockout.AllowedForNewUsers = true;
|
||||||
|
|
||||||
|
// User settings.
|
||||||
|
options.User.AllowedUserNameCharacters =
|
||||||
|
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
|
||||||
|
options.User.RequireUniqueEmail = false;
|
||||||
|
options.SignIn.RequireConfirmedEmail = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
services.ConfigureApplicationCookie(options =>
|
||||||
|
{
|
||||||
|
// Cookie settings
|
||||||
|
options.Cookie.HttpOnly = true;
|
||||||
|
options.ExpireTimeSpan = TimeSpan.FromMinutes(5);
|
||||||
|
|
||||||
|
options.LoginPath = "/Identity/Account/Login";
|
||||||
|
options.AccessDeniedPath = "/Identity/Account/AccessDenied";
|
||||||
|
options.SlidingExpiration = true;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||||
@ -54,6 +93,7 @@ namespace Selector.Web
|
|||||||
|
|
||||||
app.UseRouting();
|
app.UseRouting();
|
||||||
|
|
||||||
|
app.UseAuthentication();
|
||||||
app.UseAuthorization();
|
app.UseAuthorization();
|
||||||
|
|
||||||
app.UseEndpoints(endpoints =>
|
app.UseEndpoints(endpoints =>
|
||||||
|
Loading…
Reference in New Issue
Block a user