Replace identity code with built-in version on the templates

This commit is contained in:
Javier Calvarro Nelson 2017-12-19 13:22:01 -08:00
parent 8c94d27d6a
commit 5325c65b88
125 changed files with 197 additions and 4861 deletions

View File

@ -12,6 +12,7 @@
<MicrosoftAspNetCoreDiagnosticsEntityFrameworkCorePackageVersion>2.1.0-preview1-27928</MicrosoftAspNetCoreDiagnosticsEntityFrameworkCorePackageVersion>
<MicrosoftAspNetCoreHttpsPolicyPackageVersion>2.1.0-preview1-27928</MicrosoftAspNetCoreHttpsPolicyPackageVersion>
<MicrosoftAspNetCoreIdentityEntityFrameworkCorePackageVersion>2.1.0-preview1-27928</MicrosoftAspNetCoreIdentityEntityFrameworkCorePackageVersion>
<MicrosoftAspNetCoreIdentityUIPackageVersion>2.1.0-preview1-27928</MicrosoftAspNetCoreIdentityUIPackageVersion>
<MicrosoftAspNetCoreMvcPackageVersion>2.1.0-preview1-27928</MicrosoftAspNetCoreMvcPackageVersion>
<MicrosoftAspNetCoreMvcRazorViewCompilationPackageVersion>2.1.0-preview1-27928</MicrosoftAspNetCoreMvcRazorViewCompilationPackageVersion>
<MicrosoftAspNetCorePackageVersion>2.1.0-preview1-27928</MicrosoftAspNetCorePackageVersion>

View File

@ -27,6 +27,7 @@
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="${MicrosoftAspNetCoreDiagnosticsEntityFrameworkCorePackageVersion}" Condition="'$(IndividualLocalAuth)' == 'True'" />
<PackageReference Include="Microsoft.AspNetCore.HttpsPolicy" Version="${MicrosoftAspNetCoreHttpsPolicyPackageVersion}" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="${MicrosoftAspNetCoreIdentityEntityFrameworkCorePackageVersion}" Condition="'$(IndividualLocalAuth)' == 'True'" />
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="${MicrosoftAspNetCoreIdentityEntityFrameworkCorePackageVersion}" Condition="'$(IndividualLocalAuth)' == 'True'" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="${MicrosoftAspNetCoreMvcPackageVersion}" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.ViewCompilation" Version="${MicrosoftAspNetCoreMvcRazorViewCompilationPackageVersion}" PrivateAssets="All" />
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="${MicrosoftAspNetCoreStaticFilesPackageVersion}" />

View File

@ -27,6 +27,7 @@
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="${MicrosoftAspNetCoreAuthenticationOpenIdConnectPackageVersion}" Condition="'$(OrganizationalAuth)' == 'True' OR '$(IndividualB2CAuth)' == 'True'" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="${MicrosoftAspNetCoreDiagnosticsEntityFrameworkCorePackageVersion}" Condition="'$(IndividualLocalAuth)' == 'True'" />
<PackageReference Include="Microsoft.AspNetCore.HttpsPolicy" Version="${MicrosoftAspNetCoreHttpsPolicyPackageVersion}" />
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="${MicrosoftAspNetCoreIdentityEntityFrameworkCorePackageVersion}" Condition="'$(IndividualLocalAuth)' == 'True'" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="${MicrosoftAspNetCoreIdentityEntityFrameworkCorePackageVersion}" Condition="'$(IndividualLocalAuth)' == 'True'" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="${MicrosoftAspNetCoreMvcPackageVersion}" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.ViewCompilation" Version="${MicrosoftAspNetCoreMvcRazorViewCompilationPackageVersion}" PrivateAssets="All" />

View File

@ -34,13 +34,19 @@
]
},
{
"condition": "(!IndividualAuth && !OrganizationalAuth)",
"condition": "(!OrganizationalAuth)",
"exclude": [
"Controllers/**",
"Pages/Account/**",
"Pages/_LoginPartial.cshtml"
]
},
{
"condition": "(!IndividualLocalAuth)",
"exclude": [
"Pages/Shared/_LoginPartial.cshtml"
]
},
{
"condition": "(!IndividualLocalAuth || UseLocalDB)",
"exclude": [
@ -50,32 +56,6 @@
{
"condition": "(!IndividualLocalAuth)",
"exclude": [
"Pages/Account/ConfirmEmail.cshtml",
"Pages/Account/ConfirmEmail.cshtml.cs",
"Pages/Account/ExternalLogin.cshtml",
"Pages/Account/ExternalLogin.cshtml.cs",
"Pages/Account/ForgotPassword.cshtml",
"Pages/Account/ForgotPassword.cshtml.cs",
"Pages/Account/ForgotPasswordConfirmation.cshtml",
"Pages/Account/ForgotPasswordConfirmation.cshtml.cs",
"Pages/Account/Lockout.cshtml",
"Pages/Account/Lockout.cshtml.cs",
"Pages/Account/Login.cshtml",
"Pages/Account/Login.cshtml.cs",
"Pages/Account/LoginWith2fa.cshtml",
"Pages/Account/LoginWith2fa.cshtml.cs",
"Pages/Account/LoginWithRecoveryCode.cshtml",
"Pages/Account/LoginWithRecoveryCode.cshtml.cs",
"Pages/Account/Register.cshtml",
"Pages/Account/Register.cshtml.cs",
"Pages/Account/ResetPassword.cshtml",
"Pages/Account/ResetPassword.cshtml.cs",
"Pages/Account/ResetPasswordConfirmation.cshtml",
"Pages/Account/ResetPasswordConfirmation.cshtml.cs",
"Pages/Account/Manage/**",
"Extensions/EmailSenderExtensions.cs",
"Extensions/UrlHelperExtensions.cs",
"Services/**",
"Data/**"
]
},
@ -97,6 +77,12 @@
"exclude": [
"Extensions/AzureAdB2C*.cs"
]
},
{
"condition": "(!OrganizationalAuth || !IndividualB2CAuth)",
"exclude": [
"Controllers/AccountController.cs"
]
}
]
}

View File

@ -7,14 +7,7 @@ using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
#endif
#if (IndividualLocalAuth)
using Microsoft.AspNetCore.Identity;
#endif
using Microsoft.AspNetCore.Mvc;
#if (IndividualLocalAuth)
using Microsoft.Extensions.Logging;
using Company.WebApplication1.Data;
#endif
#if (IndividualB2CAuth)
using Microsoft.Extensions.Options;
#endif
@ -24,25 +17,7 @@ namespace Company.WebApplication1.Controllers
[Route("[controller]/[action]")]
public class AccountController : Controller
{
#if (IndividualLocalAuth)
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly ILogger _logger;
public AccountController(SignInManager<ApplicationUser> signInManager, ILogger<AccountController> logger)
{
_signInManager = signInManager;
_logger = logger;
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Logout()
{
await _signInManager.SignOutAsync();
_logger.LogInformation("User logged out.");
return RedirectToPage("/Index");
}
#elseif (OrganizationalAuth)
#if (OrganizationalAuth)
[HttpGet]
public IActionResult SignIn()
{

View File

@ -1,25 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
namespace Company.WebApplication1.Data
{
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
}
}
}

View File

@ -1,13 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
namespace Company.WebApplication1.Data
{
// Add profile data for application users by adding properties to the ApplicationUser class
public class ApplicationUser : IdentityUser
{
}
}

View File

@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
#endif
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
@ -11,7 +12,7 @@ using Microsoft.EntityFrameworkCore.Migrations;
namespace Company.WebApplication1.Data.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[DbContext(typeof(IdentityDbContext))]
[Migration("00000000000000_CreateIdentitySchema")]
partial class CreateIdentitySchema
{
@ -140,7 +141,7 @@ namespace Company.WebApplication1.Data.Migrations
b.ToTable("AspNetUserTokens");
});
modelBuilder.Entity("Company.WebApplication1.Models.ApplicationUser", b =>
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b =>
{
b.Property<string>("Id");
@ -215,7 +216,7 @@ namespace Company.WebApplication1.Data.Migrations
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("Company.WebApplication1.Models.ApplicationUser")
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser")
.WithMany("Claims")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
@ -223,7 +224,7 @@ namespace Company.WebApplication1.Data.Migrations
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("Company.WebApplication1.Models.ApplicationUser")
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser")
.WithMany("Logins")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
@ -236,7 +237,7 @@ namespace Company.WebApplication1.Data.Migrations
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("Company.WebApplication1.Models.ApplicationUser")
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser")
.WithMany("Roles")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);

View File

@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
#endif
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
@ -11,8 +12,8 @@ using Microsoft.EntityFrameworkCore.Migrations;
namespace Company.WebApplication1.Data.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
partial class ApplicationDbContextModelSnapshot : ModelSnapshot
[DbContext(typeof(IdentityDbContext))]
partial class IdentityDbContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
@ -139,7 +140,7 @@ namespace Company.WebApplication1.Data.Migrations
b.ToTable("AspNetUserTokens");
});
modelBuilder.Entity("Company.WebApplication1.Models.ApplicationUser", b =>
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b =>
{
b.Property<string>("Id");
@ -214,7 +215,7 @@ namespace Company.WebApplication1.Data.Migrations
modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserClaim<string>", b =>
{
b.HasOne("Company.WebApplication1.Models.ApplicationUser")
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser")
.WithMany("Claims")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
@ -222,7 +223,7 @@ namespace Company.WebApplication1.Data.Migrations
modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserLogin<string>", b =>
{
b.HasOne("Company.WebApplication1.Models.ApplicationUser")
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser")
.WithMany("Logins")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
@ -235,7 +236,7 @@ namespace Company.WebApplication1.Data.Migrations
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("Company.WebApplication1.Models.ApplicationUser")
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser")
.WithMany("Roles")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);

View File

@ -1,24 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Company.WebApplication1.Services;
namespace Company.WebApplication1.Services
{
public static class EmailSenderExtensions
{
public static Task SendEmailConfirmationAsync(this IEmailSender emailSender, string email, string link)
{
return emailSender.SendEmailAsync(email, "Confirm your email",
$"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(link)}'>clicking here</a>.");
}
public static Task SendResetPasswordAsync(this IEmailSender emailSender, string email, string callbackUrl)
{
return emailSender.SendEmailAsync(email, "Reset Password",
$"Please reset your password by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
}
}
}

View File

@ -1,38 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.AspNetCore.Mvc
{
public static class UrlHelperExtensions
{
public static string GetLocalUrl(this IUrlHelper urlHelper, string localUrl)
{
if (!urlHelper.IsLocalUrl(localUrl))
{
return urlHelper.Page("/Index");
}
return localUrl;
}
public static string EmailConfirmationLink(this IUrlHelper urlHelper, string userId, string code, string scheme)
{
return urlHelper.Page(
"/Account/ConfirmEmail",
pageHandler: null,
values: new { userId, code },
protocol: scheme);
}
public static string ResetPasswordCallbackLink(this IUrlHelper urlHelper, string code, string scheme)
{
return urlHelper.Page(
"/Account/ResetPassword",
pageHandler: null,
values: new { code },
protocol: scheme);
}
}
}

View File

@ -1,10 +0,0 @@
@page
@model AccessDeniedModel
@{
ViewData["Title"] = "Access denied";
}
<header>
<h1 class="text-danger">@ViewData["Title"]</h1>
<p class="text-danger">You do not have access to this resource.</p>
</header>

View File

@ -1,16 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace Company.WebApplication1.Pages.Account
{
public class AccessDeniedModel : PageModel
{
public void OnGet()
{
}
}
}

View File

@ -1,12 +0,0 @@
@page
@model ConfirmEmailModel
@{
ViewData["Title"] = "Confirm email";
}
<h2>@ViewData["Title"]</h2>
<div>
<p>
Thank you for confirming your email.
</p>
</div>

View File

@ -1,43 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Company.WebApplication1.Data;
namespace Company.WebApplication1.Pages.Account
{
public class ConfirmEmailModel : PageModel
{
private readonly UserManager<ApplicationUser> _userManager;
public ConfirmEmailModel(UserManager<ApplicationUser> userManager)
{
_userManager = userManager;
}
public async Task<IActionResult> OnGetAsync(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);
if (!result.Succeeded)
{
throw new ApplicationException($"Error confirming email for user with ID '{userId}':");
}
return Page();
}
}
}

View File

@ -1,33 +0,0 @@
@page
@model ExternalLoginModel
@{
ViewData["Title"] = "Register";
}
<h2>@ViewData["Title"]</h2>
<h4>Associate your @Model.LoginProvider account.</h4>
<hr />
<p class="text-info">
You've successfully authenticated with <strong>@Model.LoginProvider</strong>.
Please enter an email address for this site below and click the Register button to finish
logging in.
</p>
<div class="row">
<div class="col-md-4">
<form asp-page-handler="Confirmation" asp-route-returnUrl="@Model.ReturnUrl" method="post">
<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>
<button type="submit" class="btn btn-default">Register</button>
</form>
</div>
</div>
@section Scripts {
@await Html.PartialAsync("_ValidationScriptsPartial")
}

View File

@ -1,137 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
using Company.WebApplication1.Data;
namespace Company.WebApplication1.Pages.Account
{
public class ExternalLoginModel : PageModel
{
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly UserManager<ApplicationUser> _userManager;
private readonly ILogger<ExternalLoginModel> _logger;
public ExternalLoginModel(
SignInManager<ApplicationUser> signInManager,
UserManager<ApplicationUser> userManager,
ILogger<ExternalLoginModel> logger)
{
_signInManager = signInManager;
_userManager = userManager;
_logger = logger;
}
[BindProperty]
public InputModel Input { get; set; }
public string LoginProvider { get; set; }
public string ReturnUrl { get; set; }
[TempData]
public string ErrorMessage { get; set; }
public class InputModel
{
[Required]
[EmailAddress]
public string Email { get; set; }
}
public IActionResult OnGetAsync()
{
return RedirectToPage("./Login");
}
public IActionResult OnPost(string provider, string returnUrl = null)
{
// Request a redirect to the external login provider.
var redirectUrl = Url.Page("./ExternalLogin", pageHandler: "Callback", values: new { returnUrl });
var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl);
return new ChallengeResult(provider, properties);
}
public async Task<IActionResult> OnGetCallbackAsync(string returnUrl = null, string remoteError = null)
{
if (remoteError != null)
{
ErrorMessage = $"Error from external provider: {remoteError}";
return RedirectToPage("./Login", new { ReturnUrl = returnUrl });
}
var info = await _signInManager.GetExternalLoginInfoAsync();
if (info == null)
{
ErrorMessage = "Error loading external login information.";
return RedirectToPage("./Login", new { ReturnUrl = returnUrl });
}
// Sign in the user with this external login provider if the user already has a login.
var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false, bypassTwoFactor: true);
if (result.Succeeded)
{
_logger.LogInformation("{Name} logged in with {LoginProvider} provider.", info.Principal.Identity.Name, info.LoginProvider);
return LocalRedirect(Url.GetLocalUrl(returnUrl));
}
if (result.IsLockedOut)
{
return RedirectToPage("./Lockout");
}
else
{
// If the user does not have an account, then ask the user to create an account.
ReturnUrl = returnUrl;
LoginProvider = info.LoginProvider;
if (info.Principal.HasClaim(c => c.Type == ClaimTypes.Email))
{
Input = new InputModel
{
Email = info.Principal.FindFirstValue(ClaimTypes.Email)
};
}
return Page();
}
}
public async Task<IActionResult> OnPostConfirmationAsync(string returnUrl = null)
{
// Get the information about the user from the external login provider
var info = await _signInManager.GetExternalLoginInfoAsync();
if (info == null)
{
ErrorMessage = "Error loading external login information during confirmation.";
return RedirectToPage("./Login", new { ReturnUrl = returnUrl });
}
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = Input.Email, Email = Input.Email };
var result = await _userManager.CreateAsync(user);
if (result.Succeeded)
{
result = await _userManager.AddLoginAsync(user, info);
if (result.Succeeded)
{
await _signInManager.SignInAsync(user, isPersistent: false);
_logger.LogInformation("User created an account using {Name} provider.", info.LoginProvider);
return LocalRedirect(Url.GetLocalUrl(returnUrl));
}
}
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
}
LoginProvider = info.LoginProvider;
ReturnUrl = returnUrl;
return Page();
}
}
}

View File

@ -1,26 +0,0 @@
@page
@model ForgotPasswordModel
@{
ViewData["Title"] = "Forgot your password?";
}
<h2>@ViewData["Title"]</h2>
<h4>Enter your email.</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form method="post">
<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>
<button type="submit" class="btn btn-default">Submit</button>
</form>
</div>
</div>
@section Scripts {
@await Html.PartialAsync("_ValidationScriptsPartial")
}

View File

@ -1,56 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Company.WebApplication1.Data;
using Company.WebApplication1.Services;
namespace Company.WebApplication1.Pages.Account
{
public class ForgotPasswordModel : PageModel
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly IEmailSender _emailSender;
public ForgotPasswordModel(UserManager<ApplicationUser> userManager, IEmailSender emailSender)
{
_userManager = userManager;
_emailSender = emailSender;
}
[BindProperty]
public InputModel Input { get; set; }
public class InputModel
{
[Required]
[EmailAddress]
public string Email { get; set; }
}
public async Task<IActionResult> OnPostAsync()
{
if (ModelState.IsValid)
{
var user = await _userManager.FindByEmailAsync(Input.Email);
if (user == null || !(await _userManager.IsEmailConfirmedAsync(user)))
{
// Don't reveal that the user does not exist or is not confirmed
return RedirectToPage("./ForgotPasswordConfirmation");
}
// For more information on how to enable account confirmation and password reset please
// visit https://go.microsoft.com/fwlink/?LinkID=532713
var code = await _userManager.GeneratePasswordResetTokenAsync(user);
var callbackUrl = Url.ResetPasswordCallbackLink(code, Request.Scheme);
await _emailSender.SendResetPasswordAsync(Input.Email, callbackUrl);
return RedirectToPage("./ForgotPasswordConfirmation");
}
return Page();
}
}
}

View File

@ -1,10 +0,0 @@
@page
@model ForgotPasswordConfirmation
@{
ViewData["Title"] = "Forgot password confirmation";
}
<h2>@ViewData["Title"]</h2>
<p>
Please check your email to reset your password.
</p>

View File

@ -1,16 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace Company.WebApplication1.Pages.Account
{
public class ForgotPasswordConfirmation : PageModel
{
public void OnGet()
{
}
}
}

View File

@ -1,10 +0,0 @@
@page
@model LockoutModel
@{
ViewData["Title"] = "Locked out";
}
<header>
<h1 class="text-danger">@ViewData["Title"]</h1>
<p class="text-danger">This account has been locked out, please try again later.</p>
</header>

View File

@ -1,16 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace Company.WebApplication1.Pages.Account
{
public class LockoutModel : PageModel
{
public void OnGet()
{
}
}
}

View File

@ -1,82 +0,0 @@
@page
@model LoginModel
@{
ViewData["Title"] = "Log in";
}
<h2>@ViewData["Title"]</h2>
<div class="row">
<div class="col-md-4">
<section>
<form 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-default">Log in</button>
</div>
<div class="form-group">
<p>
<a 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>
</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 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-default" name="provider" value="@provider.Name" title="Log in using your @provider.DisplayName account">@provider.DisplayName</button>
}
</p>
</div>
</form>
}
}
</section>
</div>
</div>
@section Scripts {
@await Html.PartialAsync("_ValidationScriptsPartial")
}

View File

@ -1,99 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
using Company.WebApplication1.Data;
namespace Company.WebApplication1.Pages.Account
{
public class LoginModel : PageModel
{
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly ILogger<LoginModel> _logger;
public LoginModel(SignInManager<ApplicationUser> signInManager, ILogger<LoginModel> logger)
{
_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);
}
// 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 = returnUrl;
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(Url.GetLocalUrl(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();
}
}
}

View File

@ -1,41 +0,0 @@
@page
@model LoginWith2faModel
@{
ViewData["Title"] = "Two-factor authentication";
}
<h2>@ViewData["Title"]</h2>
<hr />
<p>Your login is protected with an authenticator app. Enter your authenticator code below.</p>
<div class="row">
<div class="col-md-4">
<form method="post" asp-route-returnUrl="@Model.ReturnUrl">
<input asp-for="RememberMe" type="hidden" />
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="Input.TwoFactorCode"></label>
<input asp-for="Input.TwoFactorCode" class="form-control" autocomplete="off" />
<span asp-validation-for="Input.TwoFactorCode" class="text-danger"></span>
</div>
<div class="form-group">
<div class="checkbox">
<label asp-for="Input.RememberMachine">
<input asp-for="Input.RememberMachine" />
@Html.DisplayNameFor(m => m.Input.RememberMachine)
</label>
</div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-default">Log in</button>
</div>
</form>
</div>
</div>
<p>
Don't have access to your authenticator device? You can
<a asp-page="./LoginWithRecoveryCode" asp-route-returnUrl="@Model.ReturnUrl">log in with a recovery code</a>.
</p>
@section Scripts {
@await Html.PartialAsync("_ValidationScriptsPartial")
}

View File

@ -1,95 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
using Company.WebApplication1.Data;
namespace Company.WebApplication1.Pages.Account
{
public class LoginWith2faModel : PageModel
{
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly ILogger<LoginWith2faModel> _logger;
public LoginWith2faModel(SignInManager<ApplicationUser> signInManager, ILogger<LoginWith2faModel> logger)
{
_signInManager = signInManager;
_logger = logger;
}
[BindProperty]
public InputModel Input { get; set; }
public bool RememberMe { get; set; }
public string ReturnUrl { get; set; }
public class InputModel
{
[Required]
[StringLength(7, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
[DataType(DataType.Text)]
[Display(Name = "Authenticator code")]
public string TwoFactorCode { get; set; }
[Display(Name = "Remember this machine")]
public bool RememberMachine { get; set; }
}
public async Task<IActionResult> OnGetAsync(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.");
}
ReturnUrl = returnUrl;
RememberMe = rememberMe;
return Page();
}
public async Task<IActionResult> OnPostAsync(bool rememberMe, string returnUrl = null)
{
if (!ModelState.IsValid)
{
return Page();
}
var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
if (user == null)
{
throw new ApplicationException($"Unable to load two-factor authentication user.");
}
var authenticatorCode = Input.TwoFactorCode.Replace(" ", string.Empty).Replace("-", string.Empty);
var result = await _signInManager.TwoFactorAuthenticatorSignInAsync(authenticatorCode, rememberMe, Input.RememberMachine);
if (result.Succeeded)
{
_logger.LogInformation("User with ID '{UserId}' logged in with 2fa.", user.Id);
return LocalRedirect(Url.GetLocalUrl(returnUrl));
}
else if (result.IsLockedOut)
{
_logger.LogWarning("User with ID '{UserId}' account locked out.", user.Id);
return RedirectToPage("./Lockout");
}
else
{
_logger.LogWarning("Invalid authenticator code entered for user with ID '{UserId}'.", user.Id);
ModelState.AddModelError(string.Empty, "Invalid authenticator code.");
return Page();
}
}
}
}

View File

@ -1,29 +0,0 @@
@page
@model LoginWithRecoveryCodeModel
@{
ViewData["Title"] = "Recovery code verification";
}
<h2>@ViewData["Title"]</h2>
<hr />
<p>
You have requested to log in with a recovery code. This login will not be remembered until you provide
an authenticator app code at log in or disable 2FA and log in again.
</p>
<div class="row">
<div class="col-md-4">
<form method="post">
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="Input.RecoveryCode"></label>
<input asp-for="Input.RecoveryCode" class="form-control" autocomplete="off" />
<span asp-validation-for="Input.RecoveryCode" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-default">Log in</button>
</form>
</div>
</div>
@section Scripts {
@await Html.PartialAsync("_ValidationScriptsPartial")
}

View File

@ -1,88 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
using Company.WebApplication1.Data;
namespace Company.WebApplication1.Pages.Account
{
public class LoginWithRecoveryCodeModel : PageModel
{
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly ILogger<LoginWithRecoveryCodeModel> _logger;
public LoginWithRecoveryCodeModel(SignInManager<ApplicationUser> signInManager, ILogger<LoginWithRecoveryCodeModel> logger)
{
_signInManager = signInManager;
_logger = logger;
}
[BindProperty]
public InputModel Input { get; set; }
public string ReturnUrl { get; set; }
public class InputModel
{
[BindProperty]
[Required]
[DataType(DataType.Text)]
[Display(Name = "Recovery Code")]
public string RecoveryCode { get; set; }
}
public async Task<IActionResult> OnGetAsync(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.");
}
ReturnUrl = returnUrl;
return Page();
}
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
if (!ModelState.IsValid)
{
return Page();
}
var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
if (user == null)
{
throw new ApplicationException($"Unable to load two-factor authentication user.");
}
var recoveryCode = Input.RecoveryCode.Replace(" ", string.Empty);
var result = await _signInManager.TwoFactorRecoveryCodeSignInAsync(recoveryCode);
if (result.Succeeded)
{
_logger.LogInformation("User with ID '{UserId}' logged in with a recovery code.", user.Id);
return LocalRedirect(Url.GetLocalUrl(returnUrl));
}
if (result.IsLockedOut)
{
_logger.LogWarning("User with ID '{UserId}' account locked out.", user.Id);
return RedirectToPage("./Lockout");
}
else
{
_logger.LogWarning("Invalid recovery code entered for user with ID '{UserId}' ", user.Id);
ModelState.AddModelError(string.Empty, "Invalid recovery code entered.");
return Page();
}
}
}
}

View File

@ -1,35 +0,0 @@
@page
@model ChangePasswordModel
@{
ViewData["Title"] = "Change password";
}
<h4>@ViewData["Title"]</h4>
@Html.Partial("_StatusMessage", Model.StatusMessage)
<div class="row">
<div class="col-md-6">
<form method="post">
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="Input.OldPassword"></label>
<input asp-for="Input.OldPassword" class="form-control" />
<span asp-validation-for="Input.OldPassword" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Input.NewPassword"></label>
<input asp-for="Input.NewPassword" class="form-control" />
<span asp-validation-for="Input.NewPassword" 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-default">Update password</button>
</form>
</div>
</div>
@section Scripts {
@await Html.PartialAsync("_ValidationScriptsPartial")
}

View File

@ -1,102 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
using Company.WebApplication1.Data;
namespace Company.WebApplication1.Pages.Account.Manage
{
public class ChangePasswordModel : PageModel
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly ILogger<ChangePasswordModel> _logger;
public ChangePasswordModel(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager,
ILogger<ChangePasswordModel> logger)
{
_userManager = userManager;
_signInManager = signInManager;
_logger = logger;
}
[BindProperty]
public InputModel Input { get; set; }
[TempData]
public string StatusMessage { get; set; }
public class InputModel
{
[Required]
[DataType(DataType.Password)]
[Display(Name = "Current password")]
public string OldPassword { 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 = "New password")]
public string NewPassword { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm new password")]
[Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
public async Task<IActionResult> OnGetAsync()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
var hasPassword = await _userManager.HasPasswordAsync(user);
if (!hasPassword)
{
return RedirectToPage("./SetPassword");
}
return Page();
}
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
var changePasswordResult = await _userManager.ChangePasswordAsync(user, Input.OldPassword, Input.NewPassword);
if (!changePasswordResult.Succeeded)
{
foreach (var error in changePasswordResult.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
return Page();
}
await _signInManager.SignInAsync(user, isPersistent: false);
_logger.LogInformation("User changed their password successfully.");
StatusMessage = "Your password has been changed.";
return RedirectToPage();
}
}
}

View File

@ -1,25 +0,0 @@
@page
@model Disable2faModel
@{
ViewData["Title"] = "Disable two-factor authentication (2FA)";
ViewData["ActivePage"] = "TwoFactorAuthentication";
}
<h2>@ViewData["Title"]</h2>
<div class="alert alert-warning" role="alert">
<p>
<span class="glyphicon glyphicon-warning-sign"></span>
<strong>This action only disables 2FA.</strong>
</p>
<p>
Disabling 2FA does not change the keys used in authenticator apps. If you wish to change the key
used in an authenticator app you should <a asp-page="./ResetAuthenticator">reset your authenticator keys.</a>
</p>
</div>
<div>
<form method="post" class="form-group">
<button class="btn btn-danger" type="submit">Disable 2FA</button>
</form>
</div>

View File

@ -1,61 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
using Company.WebApplication1.Data;
namespace Company.WebApplication1.Pages.Account.Manage
{
public class Disable2faModel : PageModel
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly ILogger<Disable2faModel> _logger;
public Disable2faModel(
UserManager<ApplicationUser> userManager,
ILogger<Disable2faModel> logger)
{
_userManager = userManager;
_logger = logger;
}
public async Task<IActionResult> OnGet()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
if (!await _userManager.GetTwoFactorEnabledAsync(user))
{
throw new ApplicationException($"Cannot disable 2FA for user with ID '{_userManager.GetUserId(User)}' as it's not currently enabled.");
}
return Page();
}
public async Task<IActionResult> OnPostAsync()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
var disable2faResult = await _userManager.SetTwoFactorEnabledAsync(user, false);
if (!disable2faResult.Succeeded)
{
throw new ApplicationException($"Unexpected error occurred disabling 2FA for user with ID '{_userManager.GetUserId(User)}'.");
}
_logger.LogInformation("User with ID '{UserId}' has disabled 2fa.", _userManager.GetUserId(User));
return RedirectToPage("./TwoFactorAuthentication");
}
}
}

View File

@ -1,53 +0,0 @@
@page
@model EnableAuthenticatorModel
@{
ViewData["Title"] = "Configure authenticator app";
ViewData["ActivePage"] = "TwoFactorAuthentication";
}
<h4>@ViewData["Title"]</h4>
<div>
<p>To use an authenticator app go through the following steps:</p>
<ol class="list">
<li>
<p>
Download a two-factor authenticator app like Microsoft Authenticator for
<a href="https://go.microsoft.com/fwlink/?Linkid=825071">Windows Phone</a>,
<a href="https://go.microsoft.com/fwlink/?Linkid=825072">Android</a> and
<a href="https://go.microsoft.com/fwlink/?Linkid=825073">iOS</a> or
Google Authenticator for
<a href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&amp;hl=en">Android</a> and
<a href="https://itunes.apple.com/us/app/google-authenticator/id388497605?mt=8">iOS</a>.
</p>
</li>
<li>
<p>Scan the QR Code or enter this key <kbd>@Model.SharedKey</kbd> into your two factor authenticator app. Spaces and casing do not matter.</p>
<div class="alert alert-info">To enable QR code generation please read our <a href="https://go.microsoft.com/fwlink/?Linkid=852423">documentation</a>.</div>
<div id="qrCode"></div>
<div id="qrCodeData" data-url="@Html.Raw(Model.AuthenticatorUri)"></div>
</li>
<li>
<p>
Once you have scanned the QR code or input the key above, your two factor authentication app will provide you
with a unique code. Enter the code in the confirmation box below.
</p>
<div class="row">
<div class="col-md-6">
<form method="post">
<div class="form-group">
<label asp-for="Input.Code" class="control-label">Verification Code</label>
<input asp-for="Input.Code" class="form-control" autocomplete="off" />
<span asp-validation-for="Input.Code" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-default">Verify</button>
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
</form>
</div>
</div>
</li>
</ol>
</div>
@section Scripts {
@await Html.PartialAsync("_ValidationScriptsPartial")
}

View File

@ -1,138 +0,0 @@
using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
using System.Text;
using System.Text.Encodings.Web;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
using Company.WebApplication1.Data;
namespace Company.WebApplication1.Pages.Account.Manage
{
public class EnableAuthenticatorModel : PageModel
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly ILogger<EnableAuthenticatorModel> _logger;
private readonly UrlEncoder _urlEncoder;
private const string AuthenticatorUriFormat = "otpauth://totp/{0}:{1}?secret={2}&issuer={0}&digits=6";
public EnableAuthenticatorModel(
UserManager<ApplicationUser> userManager,
ILogger<EnableAuthenticatorModel> logger,
UrlEncoder urlEncoder)
{
_userManager = userManager;
_logger = logger;
_urlEncoder = urlEncoder;
}
public string SharedKey { get; set; }
public string AuthenticatorUri { get; set; }
[BindProperty]
public InputModel Input { get; set; }
public class InputModel
{
[Required]
[StringLength(7, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
[DataType(DataType.Text)]
[Display(Name = "Verification Code")]
public string Code { get; set; }
}
public async Task<IActionResult> OnGetAsync()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
await LoadSharedKeyAndQrCodeUriAsync(user);
if (string.IsNullOrEmpty(SharedKey))
{
await _userManager.ResetAuthenticatorKeyAsync(user);
await LoadSharedKeyAndQrCodeUriAsync(user);
}
return Page();
}
public async Task<IActionResult> OnPostAsync()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
if (!ModelState.IsValid)
{
await LoadSharedKeyAndQrCodeUriAsync(user);
return Page();
}
// Strip spaces and hypens
var verificationCode = Input.Code.Replace(" ", string.Empty).Replace("-", string.Empty);
var is2faTokenValid = await _userManager.VerifyTwoFactorTokenAsync(
user, _userManager.Options.Tokens.AuthenticatorTokenProvider, verificationCode);
if (!is2faTokenValid)
{
ModelState.AddModelError("Input.Code", "Verification code is invalid.");
await LoadSharedKeyAndQrCodeUriAsync(user);
return Page();
}
await _userManager.SetTwoFactorEnabledAsync(user, true);
_logger.LogInformation("User with ID '{UserId}' has enabled 2FA with an authenticator app.", user.Id);
return RedirectToPage("./GenerateRecoveryCodes");
}
private async Task LoadSharedKeyAndQrCodeUriAsync(ApplicationUser user)
{
// Load the authenticator key & QR code URI to display on the form
var unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user);
if (!string.IsNullOrEmpty(unformattedKey))
{
SharedKey = FormatKey(unformattedKey);
AuthenticatorUri = GenerateQrCodeUri(user.Email, unformattedKey);
}
}
private string FormatKey(string unformattedKey)
{
var result = new StringBuilder();
int currentPosition = 0;
while (currentPosition + 4 < unformattedKey.Length)
{
result.Append(unformattedKey.Substring(currentPosition, 4)).Append(" ");
currentPosition += 4;
}
if (currentPosition < unformattedKey.Length)
{
result.Append(unformattedKey.Substring(currentPosition));
}
return result.ToString().ToLowerInvariant();
}
private string GenerateQrCodeUri(string email, string unformattedKey)
{
return string.Format(
AuthenticatorUriFormat,
_urlEncoder.Encode("Company.WebApplication1"),
_urlEncoder.Encode(email),
unformattedKey);
}
}
}

View File

@ -1,52 +0,0 @@
@page
@model ExternalLoginsModel
@{
ViewData["Title"] = "Manage your external logins";
}
@Html.Partial("_StatusMessage", Model.StatusMessage)
@if (Model.CurrentLogins?.Count > 0)
{
<h4>Registered Logins</h4>
<table class="table">
<tbody>
@foreach (var login in Model.CurrentLogins)
{
<tr>
<td>@login.LoginProvider</td>
<td>
@if (Model.ShowRemoveButton)
{
<form asp-page-handler="RemoveLogin" method="post">
<div>
<input asp-for="@login.LoginProvider" name="LoginProvider" type="hidden" />
<input asp-for="@login.ProviderKey" name="ProviderKey" type="hidden" />
<button type="submit" class="btn btn-default" title="Remove this @login.LoginProvider login from your account">Remove</button>
</div>
</form>
}
else
{
@: &nbsp;
}
</td>
</tr>
}
</tbody>
</table>
}
@if (Model.OtherLogins?.Count > 0)
{
<h4>Add another service to log in.</h4>
<hr />
<form asp-page-handler="LinkLogin" method="post" class="form-horizontal">
<div id="socialLoginList">
<p>
@foreach (var provider in Model.OtherLogins)
{
<button type="submit" class="btn btn-default" name="provider" value="@provider.Name" title="Log in using your @provider.DisplayName account">@provider.DisplayName</button>
}
</p>
</div>
</form>
}

View File

@ -1,108 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Company.WebApplication1.Data;
namespace Company.WebApplication1.Pages.Account.Manage
{
public class ExternalLoginsModel : PageModel
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
public ExternalLoginsModel(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager)
{
_userManager = userManager;
_signInManager = signInManager;
}
public IList<UserLoginInfo> CurrentLogins { get; set; }
public IList<AuthenticationScheme> OtherLogins { get; set; }
public bool ShowRemoveButton { get; set; }
[TempData]
public string StatusMessage { get; set; }
public async Task<IActionResult> OnGetAsync()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
CurrentLogins = await _userManager.GetLoginsAsync(user);
OtherLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync())
.Where(auth => CurrentLogins.All(ul => auth.Name != ul.LoginProvider))
.ToList();
ShowRemoveButton = user.PasswordHash != null || CurrentLogins.Count > 1;
return Page();
}
public async Task<IActionResult> OnPostRemoveLoginAsync(string loginProvider, string providerKey)
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
var result = await _userManager.RemoveLoginAsync(user, loginProvider, providerKey);
if (!result.Succeeded)
{
throw new ApplicationException($"Unexpected error occurred removing external login for user with ID '{user.Id}'.");
}
await _signInManager.SignInAsync(user, isPersistent: false);
StatusMessage = "The external login was removed.";
return RedirectToPage();
}
public async Task<IActionResult> OnPostLinkLoginAsync(string provider)
{
// Clear the existing external cookie to ensure a clean login process
await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);
// Request a redirect to the external login provider to link a login for the current user
var redirectUrl = Url.Page("./ExternalLogins", pageHandler: "LinkLoginCallback");
var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl, _userManager.GetUserId(User));
return new ChallengeResult(provider, properties);
}
public async Task<IActionResult> OnGetLinkLoginCallbackAsync()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
var info = await _signInManager.GetExternalLoginInfoAsync(await _userManager.GetUserIdAsync(user));
if (info == null)
{
throw new ApplicationException($"Unexpected error occurred loading external login info for user with ID '{user.Id}'.");
}
var result = await _userManager.AddLoginAsync(user, info);
if (!result.Succeeded)
{
throw new ApplicationException($"Unexpected error occurred adding external login for user with ID '{user.Id}'.");
}
// Clear the existing external cookie to ensure a clean login process
await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);
StatusMessage = "The external login was added.";
return RedirectToPage();
}
}
}

View File

@ -1,25 +0,0 @@
@page
@model GenerateRecoveryCodesModel
@{
ViewData["Title"] = "Recovery codes";
ViewData["ActivePage"] = "TwoFactorAuthentication";
}
<h4>@ViewData["Title"]</h4>
<div class="alert alert-warning" role="alert">
<p>
<span class="glyphicon glyphicon-warning-sign"></span>
<strong>Put these codes in a safe place.</strong>
</p>
<p>
If you lose your device and don't have the recovery codes you will lose access to your account.
</p>
</div>
<div class="row">
<div class="col-md-12">
@for (var row = 0; row < Model.RecoveryCodes.Count(); row += 2)
{
<code>@Model.RecoveryCodes[row]</code><text>&nbsp;</text><code>@Model.RecoveryCodes[row + 1]</code><br />
}
</div>
</div>

View File

@ -1,49 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
using Company.WebApplication1.Data;
namespace Company.WebApplication1.Pages.Account.Manage
{
public class GenerateRecoveryCodesModel : PageModel
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly ILogger<GenerateRecoveryCodesModel> _logger;
public GenerateRecoveryCodesModel(
UserManager<ApplicationUser> userManager,
ILogger<GenerateRecoveryCodesModel> logger)
{
_userManager = userManager;
_logger = logger;
}
public string[] RecoveryCodes { get; set; }
public async Task<IActionResult> OnGetAsync()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
if (!user.TwoFactorEnabled)
{
throw new ApplicationException($"Cannot generate recovery codes for user with ID '{user.Id}' as they do not have 2FA enabled.");
}
var recoveryCodes = await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(user, 10);
RecoveryCodes = recoveryCodes.ToArray();
_logger.LogInformation("User with ID '{UserId}' has generated new 2FA recovery codes.", user.Id);
return Page();
}
}
}

View File

@ -1,45 +0,0 @@
@page
@model IndexModel
@{
ViewData["Title"] = "Profile";
}
<h4>@ViewData["Title"]</h4>
@Html.Partial("_StatusMessage", Model.StatusMessage)
<div class="row">
<div class="col-md-6">
<form method="post">
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="Username"></label>
<input asp-for="Username" class="form-control" disabled />
</div>
<div class="form-group">
<label asp-for="Input.Email"></label>
@if (Model.IsEmailConfirmed)
{
<div class="input-group">
<input asp-for="Input.Email" class="form-control" />
<span class="input-group-addon" aria-hidden="true"><span class="glyphicon glyphicon-ok text-success"></span></span>
</div>
}
else
{
<input asp-for="Input.Email" class="form-control" />
<button asp-page-handler="SendVerificationEmail" class="btn btn-link">Send verification email</button>
}
<span asp-validation-for="Input.Email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Input.PhoneNumber"></label>
<input asp-for="Input.PhoneNumber" class="form-control" />
<span asp-validation-for="Input.PhoneNumber" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-default">Save</button>
</form>
</div>
</div>
@section Scripts {
@await Html.PartialAsync("_ValidationScriptsPartial")
}

View File

@ -1,128 +0,0 @@
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.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Company.WebApplication1.Data;
using Company.WebApplication1.Services;
namespace Company.WebApplication1.Pages.Account.Manage
{
public partial class IndexModel : PageModel
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly IEmailSender _emailSender;
public IndexModel(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager,
IEmailSender emailSender)
{
_userManager = userManager;
_signInManager = signInManager;
_emailSender = emailSender;
}
public string Username { get; set; }
public bool IsEmailConfirmed { get; set; }
[TempData]
public string StatusMessage { get; set; }
[BindProperty]
public InputModel Input { get; set; }
public class InputModel
{
[Required]
[EmailAddress]
public string Email { get; set; }
[Phone]
[Display(Name = "Phone number")]
public string PhoneNumber { get; set; }
}
public async Task<IActionResult> OnGetAsync()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
Username = user.UserName;
Input = new InputModel
{
Email = user.Email,
PhoneNumber = user.PhoneNumber
};
IsEmailConfirmed = await _userManager.IsEmailConfirmedAsync(user);
return Page();
}
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
if (Input.Email != user.Email)
{
var setEmailResult = await _userManager.SetEmailAsync(user, Input.Email);
if (!setEmailResult.Succeeded)
{
throw new ApplicationException($"Unexpected error occurred setting email for user with ID '{user.Id}'.");
}
}
if (Input.PhoneNumber != user.PhoneNumber)
{
var setPhoneResult = await _userManager.SetPhoneNumberAsync(user, Input.PhoneNumber);
if (!setPhoneResult.Succeeded)
{
throw new ApplicationException($"Unexpected error occurred setting phone number for user with ID '{user.Id}'.");
}
}
StatusMessage = "Your profile has been updated";
return RedirectToPage();
}
public async Task<IActionResult> OnPostSendVerificationEmailAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
var callbackUrl = Url.EmailConfirmationLink(user.Id, code, Request.Scheme);
await _emailSender.SendEmailConfirmationAsync(user.Email, callbackUrl);
StatusMessage = "Verification email sent. Please check your email.";
return RedirectToPage();
}
}
}

View File

@ -1,34 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Rendering;
namespace Company.WebApplication1.Pages.Account.Manage
{
public static class ManageNavPages
{
public static string Index => "Index";
public static string ChangePassword => "ChangePassword";
public static string ExternalLogins => "ExternalLogins";
public static string TwoFactorAuthentication => "TwoFactorAuthentication";
public static string IndexNavClass(ViewContext viewContext) => PageNavClass(viewContext, Index);
public static string ChangePasswordNavClass(ViewContext viewContext) => PageNavClass(viewContext, ChangePassword);
public static string ExternalLoginsNavClass(ViewContext viewContext) => PageNavClass(viewContext, ExternalLogins);
public static string TwoFactorAuthenticationNavClass(ViewContext viewContext) => PageNavClass(viewContext, TwoFactorAuthentication);
public static string PageNavClass(ViewContext viewContext, string page)
{
var activePage = viewContext.ViewData["ActivePage"] as string
?? System.IO.Path.GetFileNameWithoutExtension(viewContext.ActionDescriptor.DisplayName);
return string.Equals(activePage, page, StringComparison.OrdinalIgnoreCase) ? "active" : null;
}
}
}

View File

@ -1,23 +0,0 @@
@page
@model ResetAuthenticatorModel
@{
ViewData["Title"] = "Reset authenticator key";
ViewData["ActivePage"] = "TwoFactorAuthentication";
}
<h4>@ViewData["Title"]</h4>
<div class="alert alert-warning" role="alert">
<p>
<span class="glyphicon glyphicon-warning-sign"></span>
<strong>If you reset your authenticator key your authenticator app will not work until you reconfigure it.</strong>
</p>
<p>
This process disables 2FA until you verify your authenticator app and will also reset your 2FA recovery codes.
If you do not complete your authenticator app configuration you may lose access to your account.
</p>
</div>
<div>
<form method="post" class="form-group">
<button class="btn btn-danger" type="submit">Reset authenticator key</button>
</form>
</div>

View File

@ -1,51 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
using Company.WebApplication1.Data;
namespace Company.WebApplication1.Pages.Account.Manage
{
public class ResetAuthenticatorModel : PageModel
{
UserManager<ApplicationUser> _userManager;
ILogger<ResetAuthenticatorModel> _logger;
public ResetAuthenticatorModel(
UserManager<ApplicationUser> userManager,
ILogger<ResetAuthenticatorModel> logger)
{
_userManager = userManager;
_logger = logger;
}
public async Task<IActionResult> OnGet()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
return Page();
}
public async Task<IActionResult> OnPostAsync()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
await _userManager.SetTwoFactorEnabledAsync(user, false);
await _userManager.ResetAuthenticatorKeyAsync(user);
_logger.LogInformation("User with ID '{UserId}' has reset their authentication app key.", user.Id);
return RedirectToPage("./EnableAuthenticator");
}
}
}

View File

@ -1,35 +0,0 @@
@page
@model SetPasswordModel
@{
ViewData["Title"] = "Set password";
ViewData["ActivePage"] = "ChangePassword";
}
<h4>Set your password</h4>
@Html.Partial("_StatusMessage", Model.StatusMessage)
<p class="text-info">
You do not have a local username/password for this site. Add a local
account so you can log in without an external login.
</p>
<div class="row">
<div class="col-md-6">
<form method="post">
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="Input.NewPassword"></label>
<input asp-for="Input.NewPassword" class="form-control" />
<span asp-validation-for="Input.NewPassword" 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-default">Set password</button>
</form>
</div>
</div>
@section Scripts {
@await Html.PartialAsync("_ValidationScriptsPartial")
}

View File

@ -1,93 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Company.WebApplication1.Data;
namespace Company.WebApplication1.Pages.Account.Manage
{
public class SetPasswordModel : PageModel
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
public SetPasswordModel(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager)
{
_userManager = userManager;
_signInManager = signInManager;
}
[BindProperty]
public InputModel Input { get; set; }
[TempData]
public string StatusMessage { get; set; }
public class InputModel
{
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "New password")]
public string NewPassword { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm new password")]
[Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
public async Task<IActionResult> OnGetAsync()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
var hasPassword = await _userManager.HasPasswordAsync(user);
if (hasPassword)
{
return RedirectToPage("./ChangePassword");
}
return Page();
}
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
var addPasswordResult = await _userManager.AddPasswordAsync(user, Input.NewPassword);
if (!addPasswordResult.Succeeded)
{
foreach (var error in addPasswordResult.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
return Page();
}
await _signInManager.SignInAsync(user, isPersistent: false);
StatusMessage = "Your password has been set.";
return RedirectToPage();
}
}
}

View File

@ -1,49 +0,0 @@
@page
@model TwoFactorAuthenticationModel
@{
ViewData["Title"] = "Two-factor authentication (2FA)";
}
<h4>@ViewData["Title"]</h4>
@if (Model.Is2faEnabled)
{
if (Model.RecoveryCodesLeft == 0)
{
<div class="alert alert-danger">
<strong>You have no recovery codes left.</strong>
<p>You must <a asp-page="./GenerateRecoveryCodes">generate a new set of recovery codes</a> before you can log in with a recovery code.</p>
</div>
}
else if (Model.RecoveryCodesLeft == 1)
{
<div class="alert alert-danger">
<strong>You have 1 recovery code left.</strong>
<p>You can <a asp-page="./GenerateRecoveryCodes">generate a new set of recovery codes</a>.</p>
</div>
}
else if (Model.RecoveryCodesLeft <= 3)
{
<div class="alert alert-warning">
<strong>You have @Model.RecoveryCodesLeft recovery codes left.</strong>
<p>You should <a asp-page="./GenerateRecoveryCodes">generate a new set of recovery codes</a>.</p>
</div>
}
<a asp-page="./Disable2fa" class="btn btn-default">Disable 2FA</a>
<a asp-page="./GenerateRecoveryCodes" class="btn btn-default">Reset recovery codes</a>
}
<h5>Authenticator app</h5>
@if (!Model.HasAuthenticator)
{
<a asp-page="./EnableAuthenticator" class="btn btn-default">Add authenticator app</a>
}
else
{
<a asp-page="./EnableAuthenticator" class="btn btn-default">Configure authenticator app</a>
<a asp-page="./ResetAuthenticator" class="btn btn-default">Reset authenticator app</a>
}
@section Scripts {
@await Html.PartialAsync("_ValidationScriptsPartial")
}

View File

@ -1,51 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
using Company.WebApplication1.Data;
namespace Company.WebApplication1.Pages.Account.Manage
{
public class TwoFactorAuthenticationModel : PageModel
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly ILogger<TwoFactorAuthenticationModel> _logger;
public TwoFactorAuthenticationModel(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager,
ILogger<TwoFactorAuthenticationModel> logger)
{
_userManager = userManager;
_signInManager = signInManager;
_logger = logger;
}
public bool HasAuthenticator { get; set; }
public int RecoveryCodesLeft { get; set; }
[BindProperty]
public bool Is2faEnabled { get; set; }
public async Task<IActionResult> OnGet()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
HasAuthenticator = await _userManager.GetAuthenticatorKeyAsync(user) != null;
Is2faEnabled = await _userManager.GetTwoFactorEnabledAsync(user);
RecoveryCodesLeft = await _userManager.CountRecoveryCodesAsync(user);
return Page();
}
}
}

View File

@ -1,23 +0,0 @@
@{
Layout = "/Pages/_Layout.cshtml";
}
<h2>Manage your account</h2>
<div>
<h4>Change your account settings</h4>
<hr />
<div class="row">
<div class="col-md-3">
@await Html.PartialAsync("_ManageNav")
</div>
<div class="col-md-9">
@RenderBody()
</div>
</div>
</div>
@section Scripts {
@RenderSection("Scripts", required: false)
}

View File

@ -1,15 +0,0 @@
@inject SignInManager<ApplicationUser> SignInManager
@{
var hasExternalLogins = (await SignInManager.GetExternalAuthenticationSchemesAsync()).Any();
}
<ul class="nav nav-pills nav-stacked">
<li class="@ManageNavPages.IndexNavClass(ViewContext)"><a asp-page="./Index">Profile</a></li>
<li class="@ManageNavPages.ChangePasswordNavClass(ViewContext)"><a asp-page="./ChangePassword">Password</a></li>
@if (hasExternalLogins)
{
<li class="@ManageNavPages.ExternalLoginsNavClass(ViewContext)"><a asp-page="./ExternalLogins">External logins</a></li>
}
<li class="@ManageNavPages.TwoFactorAuthenticationNavClass(ViewContext)"><a asp-page="./TwoFactorAuthentication">Two-factor authentication</a></li>
</ul>

View File

@ -1,10 +0,0 @@
@model string
@if (!String.IsNullOrEmpty(Model))
{
var statusMessageClass = Model.StartsWith("Error") ? "danger" : "success";
<div class="alert alert-@statusMessageClass alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>
@Model
</div>
}

View File

@ -1 +0,0 @@
@using Company.WebApplication1.Pages.Account.Manage

View File

@ -1,37 +0,0 @@
@page
@model RegisterModel
@{
ViewData["Title"] = "Register";
}
<h2>@ViewData["Title"]</h2>
<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-default">Register</button>
</form>
</div>
</div>
@section Scripts {
@await Html.PartialAsync("_ValidationScriptsPartial")
}

View File

@ -1,91 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
using Company.WebApplication1.Data;
using Company.WebApplication1.Services;
namespace Company.WebApplication1.Pages.Account
{
public class RegisterModel : PageModel
{
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly UserManager<ApplicationUser> _userManager;
private readonly ILogger<LoginModel> _logger;
private readonly IEmailSender _emailSender;
public RegisterModel(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager,
ILogger<LoginModel> logger,
IEmailSender emailSender)
{
_userManager = userManager;
_signInManager = signInManager;
_logger = logger;
_emailSender = emailSender;
}
[BindProperty]
public InputModel Input { get; set; }
public string ReturnUrl { 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 void OnGet(string returnUrl = null)
{
ReturnUrl = returnUrl;
}
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
ReturnUrl = returnUrl;
if (ModelState.IsValid)
{
var user = new ApplicationUser { 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);
var callbackUrl = Url.EmailConfirmationLink(user.Id, code, Request.Scheme);
await _emailSender.SendEmailConfirmationAsync(Input.Email, callbackUrl);
await _signInManager.SignInAsync(user, isPersistent: false);
return LocalRedirect(Url.GetLocalUrl(returnUrl));
}
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
}
// If we got this far, something failed, redisplay form
return Page();
}
}
}

View File

@ -1,37 +0,0 @@
@page
@model ResetPasswordModel
@{
ViewData["Title"] = "Reset password";
}
<h2>@ViewData["Title"]</h2>
<h4>Reset your password.</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form method="post">
<div asp-validation-summary="All" class="text-danger"></div>
<input asp-for="Input.Code" type="hidden" />
<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-default">Reset</button>
</form>
</div>
</div>
@section Scripts {
@await Html.PartialAsync("_ValidationScriptsPartial")
}

View File

@ -1,87 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Company.WebApplication1.Data;
namespace Company.WebApplication1.Pages.Account
{
public class ResetPasswordModel : PageModel
{
private readonly UserManager<ApplicationUser> _userManager;
public ResetPasswordModel(UserManager<ApplicationUser> userManager)
{
_userManager = userManager;
}
[BindProperty]
public InputModel Input { get; set; }
public class InputModel
{
[Required]
[EmailAddress]
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)]
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 string Code { get; set; }
}
public IActionResult OnGet(string code = null)
{
if (code == null)
{
throw new ApplicationException("A code must be supplied for password reset.");
}
else
{
Input = new InputModel
{
Code = code
};
return Page();
}
}
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
var user = await _userManager.FindByEmailAsync(Input.Email);
if (user == null)
{
// Don't reveal that the user does not exist
return RedirectToPage("./ResetPasswordConfirmation");
}
var result = await _userManager.ResetPasswordAsync(user, Input.Code, Input.Password);
if (result.Succeeded)
{
return RedirectToPage("./ResetPasswordConfirmation");
}
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
return Page();
}
}
}

View File

@ -1,10 +0,0 @@
@page
@model ResetPasswordConfirmationModel
@{
ViewData["Title"] = "Reset password confirmation";
}
<h2>@ViewData["Title"]</h2>
<p>
Your password has been reset. Please <a asp-page="./Login">click here to log in</a>.
</p>

View File

@ -1,16 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace Company.WebApplication1.Pages.Account
{
public class ResetPasswordConfirmationModel : PageModel
{
public void OnGet()
{
}
}
}

View File

@ -1 +0,0 @@
@using Company.WebApplication1.Pages.Account

View File

@ -0,0 +1,25 @@
@using Microsoft.AspNetCore.Identity
@inject SignInManager<IdentityUser> SignInManager
@inject UserManager<IdentityUser> UserManager
@if (SignInManager.IsSignedIn(User))
{
<form asp-area="Identity" asp-page="/Account/Logout" asp-route-returnUrl="@Url.Page("/Index", new { area = "" })" method="post" id="logoutForm" class="navbar-right">
<ul class="nav navbar-nav navbar-right">
<li>
<a asp-area="Identity" asp-page="/Account/Manage/Index" title="Manage">Hello @UserManager.GetUserName(User)!</a>
</li>
<li>
<button type="submit" class="btn btn-link navbar-btn navbar-link">Logout</button>
</li>
</ul>
</form>
}
else
{
<ul class="nav navbar-nav navbar-right">
<li><a asp-area="Identity" asp-page="/Account/Register">Register</a></li>
<li><a asp-area="Identity" asp-page="/Account/Login">Login</a></li>
</ul>
}

View File

@ -1,35 +1,13 @@
@*#if (IndividualLocalAuth) *@
@using Microsoft.AspNetCore.Identity
@using Company.WebApplication1.Data
@inject SignInManager<ApplicationUser> SignInManager
@inject UserManager<ApplicationUser> UserManager
@*#else
@using System.Security.Principal
#endif *@
@*#if (IndividualB2CAuth)
@using Microsoft.AspNetCore.Authentication
@using Microsoft.Extensions.Options
@inject IOptions<AzureAdB2COptions> AzureAdB2COptions
#endif *@
@*#if (IndividualLocalAuth) *@
@if (SignInManager.IsSignedIn(User))
@*#else
@if (User.Identity.IsAuthenticated)
#endif *@
{
@*#if (IndividualLocalAuth) *@
<form asp-controller="Account" asp-action="Logout" method="post" id="logoutForm" class="navbar-right">
<ul class="nav navbar-nav navbar-right">
<li>
<a asp-page="/Account/Manage/Index" title="Manage">Hello @UserManager.GetUserName(User)!</a>
</li>
<li>
<button type="submit" class="btn btn-link navbar-btn navbar-link">Log out</button>
</li>
</ul>
</form>
@*#elseif (IndividualB2CAuth)
@*#if (IndividualB2CAuth)
<ul class="nav navbar-nav navbar-right">
@if (!string.IsNullOrEmpty(AzureAdB2COptions.Value.EditProfilePolicyId))
{
@ -51,11 +29,6 @@
else
{
<ul class="nav navbar-nav navbar-right">
@*#if (IndividualLocalAuth) *@
<li><a asp-page="/Account/Register">Register</a></li>
<li><a asp-page="/Account/Login">Log in</a></li>
@*#else
<li><a asp-area="" asp-controller="Account" asp-action="Signin">Sign in</a></li>
#endif *@
</ul>
}

View File

@ -1,17 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Company.WebApplication1.Services
{
// This class is used by the application to send email for account confirmation and password reset.
// For more details see https://go.microsoft.com/fwlink/?LinkID=532713
public class EmailSender : IEmailSender
{
public Task SendEmailAsync(string email, string subject, string htmlMessage)
{
return Task.CompletedTask;
}
}
}

View File

@ -1,12 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Company.WebApplication1.Services
{
public interface IEmailSender
{
Task SendEmailAsync(string email, string subject, string htmlMessage);
}
}

View File

@ -13,6 +13,7 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
#if (IndividualLocalAuth)
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
#endif
#if (OrganizationalAuth || IndividualAuth)
using Microsoft.AspNetCore.Http;
@ -33,10 +34,6 @@ using Microsoft.IdentityModel.Protocols.OpenIdConnect;
#if (MultiOrgAuth)
using Microsoft.IdentityModel.Tokens;
#endif
#if (IndividualLocalAuth)
using Company.WebApplication1.Data;
using Company.WebApplication1.Services;
#endif
namespace Company.WebApplication1
{
@ -53,15 +50,21 @@ namespace Company.WebApplication1
public void ConfigureServices(IServiceCollection services)
{
#if (IndividualLocalAuth)
services.AddDbContext<ApplicationDbContext>(options =>
services.AddDbContext<IdentityDbContext>(options =>
#if (UseLocalDB)
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection"),
sqlOptions => sqlOptions.MigrationsAssembly("Company.WebApplication1")
));
#else
options.UseSqlite(Configuration.GetConnectionString("DefaultConnection")));
options.UseSqlite(
Configuration.GetConnectionString("DefaultConnection"),
sqlOptions => sqlOptions.MigrationsAssembly("Company.WebApplication1")));
#endif
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
services.AddIdentity<IdentityUser, IdentityRole>()
.AddEntityFrameworkStores<IdentityDbContext>()
.AddDefaultUI()
.AddDefaultTokenProviders();
#elseif (OrganizationalAuth || IndividualB2CAuth)
@ -86,9 +89,6 @@ namespace Company.WebApplication1
options.Conventions.AuthorizePage("/Account/Logout");
});
// Register no-op EmailSender used by account confirmation and password reset during development
// For more information on how to enable account confirmation and password reset please visit https://go.microsoft.com/fwlink/?LinkID=532713
services.AddSingleton<IEmailSender, EmailSender>();
#elseif (OrganizationalAuth)
services.AddMvc(options =>
{

View File

@ -47,9 +47,9 @@
]
},
{
"condition": "(!IndividualLocalAuth)",
"condition": "(!IndividualAuth)",
"exclude": [
"Areas/Identity/**"
"Data/**"
]
},
{
@ -58,6 +58,13 @@
"Properties/launchSettings.json"
]
},
{
"condition": "(IndividualLocalAuth)",
"exclude": [
"Controllers/AccountController.cs",
"Views/Account/**"
]
},
{
"condition": "(!OrganizationalAuth)",
"exclude": [

View File

@ -1,480 +0,0 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Security.Claims;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Company.WebApplication1.Controllers;
using Company.WebApplication1.Identity.Models;
using Company.WebApplication1.Identity.Models.AccountViewModels;
using Company.WebApplication1.Identity.Services;
using Company.WebApplication1.Models;
namespace Company.WebApplication1.Identity.Controllers
{
[Authorize]
[Area("Identity")]
[Route("tfp/[area]/[controller]/[action]")]
public class AccountController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly IEmailSender _emailSender;
private readonly ILogger _logger;
public AccountController(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager,
IEmailSender emailSender,
ILogger<AccountController> logger)
{
_userManager = userManager;
_signInManager = signInManager;
_emailSender = emailSender;
_logger = logger;
}
[TempData]
public string ErrorMessage { get; set; }
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> Login(string returnUrl = null)
{
// Clear the existing external cookie to ensure a clean login process
await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);
ViewData["ReturnUrl"] = returnUrl;
return View();
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
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(model.Email, model.Password, model.RememberMe, lockoutOnFailure: false);
if (result.Succeeded)
{
_logger.LogInformation("User logged in.");
return RedirectToLocal(returnUrl);
}
if (result.RequiresTwoFactor)
{
return RedirectToAction(nameof(LoginWith2fa), new { returnUrl, model.RememberMe });
}
if (result.IsLockedOut)
{
_logger.LogWarning("User account locked out.");
return RedirectToAction(nameof(Lockout));
}
else
{
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
return View(model);
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> 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<IActionResult> 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 async Task<IActionResult> LoginWithRecoveryCode(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.");
}
ViewData["ReturnUrl"] = returnUrl;
return View();
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> LoginWithRecoveryCode(LoginWithRecoveryCodeViewModel model, string returnUrl = null)
{
if (!ModelState.IsValid)
{
return View(model);
}
var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
if (user == null)
{
throw new ApplicationException($"Unable to load two-factor authentication user.");
}
var recoveryCode = model.RecoveryCode.Replace(" ", string.Empty);
var result = await _signInManager.TwoFactorRecoveryCodeSignInAsync(recoveryCode);
if (result.Succeeded)
{
_logger.LogInformation("User with ID {UserId} logged in with a recovery code.", user.Id);
return RedirectToLocal(returnUrl);
}
if (result.IsLockedOut)
{
_logger.LogWarning("User with ID {UserId} account locked out.", user.Id);
return RedirectToAction(nameof(Lockout));
}
else
{
_logger.LogWarning("Invalid recovery code entered for user with ID {UserId}", user.Id);
ModelState.AddModelError(string.Empty, "Invalid recovery code entered.");
return View();
}
}
[HttpGet]
[AllowAnonymous]
public IActionResult Lockout()
{
return View();
}
[HttpGet]
[AllowAnonymous]
public IActionResult Register(string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
return View();
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Register(RegisterViewModel model, string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
_logger.LogInformation("User created a new account with password.");
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
var callbackUrl = Url.EmailConfirmationLink(user.Id, code, Request.Scheme);
await _emailSender.SendEmailConfirmationAsync(model.Email, callbackUrl);
await _signInManager.SignInAsync(user, isPersistent: false);
_logger.LogInformation("User created a new account with password.");
return RedirectToLocal(returnUrl);
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Logout()
{
await _signInManager.SignOutAsync();
_logger.LogInformation("User logged out.");
return RedirectToAction(nameof(HomeController.Index), "Home");
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public IActionResult ExternalLogin(string provider, string returnUrl = null)
{
// Request a redirect to the external login provider.
var redirectUrl = Url.Action(nameof(ExternalLoginCallback), "Account", new { returnUrl });
var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl);
return Challenge(properties, provider);
}
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> ExternalLoginCallback(string returnUrl = null, string remoteError = null)
{
if (remoteError != null)
{
ErrorMessage = $"Error from external provider: {remoteError}";
return RedirectToAction(nameof(Login), new { ReturnUrl = returnUrl });
}
var info = await _signInManager.GetExternalLoginInfoAsync();
if (info == null)
{
ErrorMessage = "Error loading external login information.";
return RedirectToAction(nameof(Login), new { ReturnUrl = returnUrl });
}
// Sign in the user with this external login provider if the user already has a login.
var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false, bypassTwoFactor: true);
if (result.Succeeded)
{
_logger.LogInformation("User logged in with {Name} provider.", info.LoginProvider);
return RedirectToLocal(returnUrl);
}
if (result.IsLockedOut)
{
return RedirectToAction(nameof(Lockout));
}
else
{
// If the user does not have an account, then ask the user to create an account.
ViewData["ReturnUrl"] = returnUrl;
ViewData["LoginProvider"] = info.LoginProvider;
var email = info.Principal.FindFirstValue(ClaimTypes.Email);
return View(nameof(ExternalLogin), new ExternalLoginViewModel { Email = email });
}
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ExternalLoginConfirmation(ExternalLoginViewModel model, string returnUrl = null)
{
// Get the information about the user from the external login provider
var info = await _signInManager.GetExternalLoginInfoAsync();
if (info == null)
{
ErrorMessage = "Error loading external login information during confirmation.";
return RedirectToAction(nameof(Login), new { ReturnUrl = returnUrl });
}
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var result = await _userManager.CreateAsync(user);
if (result.Succeeded)
{
result = await _userManager.AddLoginAsync(user, info);
if (result.Succeeded)
{
await _signInManager.SignInAsync(user, isPersistent: false);
_logger.LogInformation("User created an account using {Name} provider.", info.LoginProvider);
return RedirectToLocal(returnUrl);
}
}
AddErrors(result);
}
ViewData["LoginProvider"] = info.LoginProvider;
ViewData["ReturnUrl"] = returnUrl;
return View(nameof(ExternalLogin), model);
}
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> ConfirmEmail(string userId, string code)
{
if (userId == null || code == null)
{
return RedirectToAction(nameof(HomeController.Index), "Home");
}
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);
if (result.Succeeded)
{
return View(nameof(ConfirmEmail));
}
else
{
return View(nameof(HomeController.Error), new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}
[HttpGet]
[AllowAnonymous]
public IActionResult ForgotPassword()
{
return View();
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ForgotPassword(ForgotPasswordViewModel model)
{
if (ModelState.IsValid)
{
var user = await _userManager.FindByEmailAsync(model.Email);
if (user == null || !(await _userManager.IsEmailConfirmedAsync(user)))
{
// Don't reveal that the user does not exist or is not confirmed
return RedirectToAction(nameof(ForgotPasswordConfirmation));
}
// For more information on how to enable account confirmation and password reset please
// visit https://go.microsoft.com/fwlink/?LinkID=532713
var code = await _userManager.GeneratePasswordResetTokenAsync(user);
var callbackUrl = Url.ResetPasswordCallbackLink(code, Request.Scheme);
await _emailSender.SendEmailAsync(model.Email, "Reset Password",
$"Please reset your password by clicking here: <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>link</a>");
return RedirectToAction(nameof(ForgotPasswordConfirmation));
}
// If we got this far, something failed, redisplay form
return View(model);
}
[HttpGet]
[AllowAnonymous]
public IActionResult ForgotPasswordConfirmation()
{
return View();
}
[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);
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ResetPassword(ResetPasswordViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var user = await _userManager.FindByEmailAsync(model.Email);
if (user == null)
{
// Don't reveal that the user does not exist
return RedirectToAction(nameof(ResetPasswordConfirmation));
}
var result = await _userManager.ResetPasswordAsync(user, model.Code, model.Password);
if (result.Succeeded)
{
return RedirectToAction(nameof(ResetPasswordConfirmation));
}
AddErrors(result);
return View();
}
[HttpGet]
[AllowAnonymous]
public IActionResult ResetPasswordConfirmation()
{
return View();
}
[HttpGet]
public IActionResult AccessDenied()
{
return View();
}
#region Helpers
private void AddErrors(IdentityResult result)
{
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
}
private IActionResult RedirectToLocal(string returnUrl)
{
if (Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction(nameof(HomeController.Index), "Home");
}
}
#endregion
}
}

View File

@ -1,506 +0,0 @@
using System;
using System.Collections.Generic;
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.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Company.WebApplication1.Identity.Models;
using Company.WebApplication1.Identity.Models.ManageViewModels;
using Company.WebApplication1.Identity.Services;
namespace Company.WebApplication1.Identity.Controllers
{
[Authorize]
[Area("Identity")]
[Route("tfp/[area]/[controller]/[action]")]
public class ManageController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly IEmailSender _emailSender;
private readonly ILogger _logger;
private readonly UrlEncoder _urlEncoder;
private const string AuthenticatorUriFormat = "otpauth://totp/{0}:{1}?secret={2}&issuer={0}&digits=6";
public ManageController(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager,
IEmailSender emailSender,
ILogger<ManageController> logger,
UrlEncoder urlEncoder)
{
_userManager = userManager;
_signInManager = signInManager;
_emailSender = emailSender;
_logger = logger;
_urlEncoder = urlEncoder;
}
[TempData]
public string StatusMessage { get; set; }
[HttpGet]
public async Task<IActionResult> Index()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
var model = new IndexViewModel
{
Username = user.UserName,
Email = user.Email,
PhoneNumber = user.PhoneNumber,
IsEmailConfirmed = user.EmailConfirmed,
StatusMessage = StatusMessage
};
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Index(IndexViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
var email = user.Email;
if (model.Email != email)
{
var setEmailResult = await _userManager.SetEmailAsync(user, model.Email);
if (!setEmailResult.Succeeded)
{
throw new ApplicationException($"Unexpected error occurred setting email for user with ID '{user.Id}'.");
}
}
var phoneNumber = user.PhoneNumber;
if (model.PhoneNumber != phoneNumber)
{
var setPhoneResult = await _userManager.SetPhoneNumberAsync(user, model.PhoneNumber);
if (!setPhoneResult.Succeeded)
{
throw new ApplicationException($"Unexpected error occurred setting phone number for user with ID '{user.Id}'.");
}
}
StatusMessage = "Your profile has been updated";
return RedirectToAction(nameof(Index));
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> SendVerificationEmail(IndexViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
var callbackUrl = Url.EmailConfirmationLink(user.Id, code, Request.Scheme);
var email = user.Email;
await _emailSender.SendEmailConfirmationAsync(email, callbackUrl);
StatusMessage = "Verification email sent. Please check your email.";
return RedirectToAction(nameof(Index));
}
[HttpGet]
public async Task<IActionResult> ChangePassword()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
var hasPassword = await _userManager.HasPasswordAsync(user);
if (!hasPassword)
{
return RedirectToAction(nameof(SetPassword));
}
var model = new ChangePasswordViewModel { StatusMessage = StatusMessage };
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ChangePassword(ChangePasswordViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
var changePasswordResult = await _userManager.ChangePasswordAsync(user, model.OldPassword, model.NewPassword);
if (!changePasswordResult.Succeeded)
{
AddErrors(changePasswordResult);
return View(model);
}
await _signInManager.SignInAsync(user, isPersistent: false);
_logger.LogInformation("User changed their password successfully.");
StatusMessage = "Your password has been changed.";
return RedirectToAction(nameof(ChangePassword));
}
[HttpGet]
public async Task<IActionResult> SetPassword()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
var hasPassword = await _userManager.HasPasswordAsync(user);
if (hasPassword)
{
return RedirectToAction(nameof(ChangePassword));
}
var model = new SetPasswordViewModel { StatusMessage = StatusMessage };
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> SetPassword(SetPasswordViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
var addPasswordResult = await _userManager.AddPasswordAsync(user, model.NewPassword);
if (!addPasswordResult.Succeeded)
{
AddErrors(addPasswordResult);
return View(model);
}
await _signInManager.SignInAsync(user, isPersistent: false);
StatusMessage = "Your password has been set.";
return RedirectToAction(nameof(SetPassword));
}
[HttpGet]
public async Task<IActionResult> ExternalLogins()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
var model = new ExternalLoginsViewModel { CurrentLogins = await _userManager.GetLoginsAsync(user) };
model.OtherLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync())
.Where(auth => model.CurrentLogins.All(ul => auth.Name != ul.LoginProvider))
.ToList();
model.ShowRemoveButton = await _userManager.HasPasswordAsync(user) || model.CurrentLogins.Count > 1;
model.StatusMessage = StatusMessage;
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> LinkLogin(string provider)
{
// Clear the existing external cookie to ensure a clean login process
await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);
// Request a redirect to the external login provider to link a login for the current user
var redirectUrl = Url.Action(nameof(LinkLoginCallback));
var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl, _userManager.GetUserId(User));
return new ChallengeResult(provider, properties);
}
[HttpGet]
public async Task<IActionResult> LinkLoginCallback()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
var info = await _signInManager.GetExternalLoginInfoAsync(user.Id);
if (info == null)
{
throw new ApplicationException($"Unexpected error occurred loading external login info for user with ID '{user.Id}'.");
}
var result = await _userManager.AddLoginAsync(user, info);
if (!result.Succeeded)
{
throw new ApplicationException($"Unexpected error occurred adding external login for user with ID '{user.Id}'.");
}
// Clear the existing external cookie to ensure a clean login process
await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);
StatusMessage = "The external login was added.";
return RedirectToAction(nameof(ExternalLogins));
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> RemoveLogin(RemoveLoginViewModel model)
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
var result = await _userManager.RemoveLoginAsync(user, model.LoginProvider, model.ProviderKey);
if (!result.Succeeded)
{
throw new ApplicationException($"Unexpected error occurred removing external login for user with ID '{user.Id}'.");
}
await _signInManager.SignInAsync(user, isPersistent: false);
StatusMessage = "The external login was removed.";
return RedirectToAction(nameof(ExternalLogins));
}
[HttpGet]
public async Task<IActionResult> TwoFactorAuthentication()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
var model = new TwoFactorAuthenticationViewModel
{
HasAuthenticator = await _userManager.GetAuthenticatorKeyAsync(user) != null,
Is2faEnabled = user.TwoFactorEnabled,
RecoveryCodesLeft = await _userManager.CountRecoveryCodesAsync(user),
};
return View(model);
}
[HttpGet]
public async Task<IActionResult> Disable2faWarning()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
if (!user.TwoFactorEnabled)
{
throw new ApplicationException($"Unexpected error occured disabling 2FA for user with ID '{user.Id}'.");
}
return View(nameof(Disable2fa));
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Disable2fa()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
var disable2faResult = await _userManager.SetTwoFactorEnabledAsync(user, false);
if (!disable2faResult.Succeeded)
{
throw new ApplicationException($"Unexpected error occured disabling 2FA for user with ID '{user.Id}'.");
}
_logger.LogInformation("User with ID {UserId} has disabled 2fa.", user.Id);
return RedirectToAction(nameof(TwoFactorAuthentication));
}
[HttpGet]
public async Task<IActionResult> EnableAuthenticator()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
var unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user);
if (string.IsNullOrEmpty(unformattedKey))
{
await _userManager.ResetAuthenticatorKeyAsync(user);
unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user);
}
var model = new EnableAuthenticatorViewModel
{
SharedKey = FormatKey(unformattedKey),
AuthenticatorUri = GenerateQrCodeUri(user.Email, unformattedKey)
};
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> EnableAuthenticator(EnableAuthenticatorViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
// Strip spaces and hypens
var verificationCode = model.Code.Replace(" ", string.Empty).Replace("-", string.Empty);
var is2faTokenValid = await _userManager.VerifyTwoFactorTokenAsync(
user, _userManager.Options.Tokens.AuthenticatorTokenProvider, verificationCode);
if (!is2faTokenValid)
{
ModelState.AddModelError("model.Code", "Verification code is invalid.");
return View(model);
}
await _userManager.SetTwoFactorEnabledAsync(user, true);
_logger.LogInformation("User with ID {UserId} has enabled 2FA with an authenticator app.", user.Id);
return RedirectToAction(nameof(GenerateRecoveryCodes));
}
[HttpGet]
public IActionResult ResetAuthenticatorWarning()
{
return View(nameof(ResetAuthenticator));
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ResetAuthenticator()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
await _userManager.SetTwoFactorEnabledAsync(user, false);
await _userManager.ResetAuthenticatorKeyAsync(user);
_logger.LogInformation("User with id '{UserId}' has reset their authentication app key.", user.Id);
return RedirectToAction(nameof(EnableAuthenticator));
}
[HttpGet]
public async Task<IActionResult> GenerateRecoveryCodes()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
if (!user.TwoFactorEnabled)
{
throw new ApplicationException($"Cannot generate recovery codes for user with ID '{user.Id}' as they do not have 2FA enabled.");
}
var recoveryCodes = await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(user, 10);
var model = new GenerateRecoveryCodesViewModel { RecoveryCodes = recoveryCodes.ToArray() };
_logger.LogInformation("User with ID {UserId} has generated new 2FA recovery codes.", user.Id);
return View(model);
}
#region Helpers
private void AddErrors(IdentityResult result)
{
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
}
private string FormatKey(string unformattedKey)
{
var result = new StringBuilder();
int currentPosition = 0;
while (currentPosition + 4 < unformattedKey.Length)
{
result.Append(unformattedKey.Substring(currentPosition, 4)).Append(" ");
currentPosition += 4;
}
if (currentPosition < unformattedKey.Length)
{
result.Append(unformattedKey.Substring(currentPosition));
}
return result.ToString().ToLowerInvariant();
}
private string GenerateQrCodeUri(string email, string unformattedKey)
{
return string.Format(
AuthenticatorUriFormat,
_urlEncoder.Encode("Company.WebApplication1"),
_urlEncoder.Encode(email),
unformattedKey);
}
#endregion
}
}

View File

@ -1,26 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Company.WebApplication1.Identity.Models;
namespace Company.WebApplication1.Identity.Data
{
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
}
}
}

View File

@ -1,18 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Company.WebApplication1.Identity.Services;
namespace Company.WebApplication1.Identity.Services
{
public static class EmailSenderExtensions
{
public static Task SendEmailConfirmationAsync(this IEmailSender emailSender, string email, string link)
{
return emailSender.SendEmailAsync(email, "Confirm your email",
$"Please confirm your account by clicking this link: <a href='{HtmlEncoder.Default.Encode(link)}'>link</a>");
}
}
}

View File

@ -1,29 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Company.WebApplication1.Identity.Controllers;
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",
values: new { userId, code },
protocol: scheme);
}
public static string ResetPasswordCallbackLink(this IUrlHelper urlHelper, string code, string scheme)
{
return urlHelper.Action(
action: nameof(AccountController.ResetPassword),
controller: "Account",
values: new { code },
protocol: scheme);
}
}
}

View File

@ -1,15 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
namespace Company.WebApplication1.Identity.Models.AccountViewModels
{
public class ExternalLoginViewModel
{
[Required]
[EmailAddress]
public string Email { get; set; }
}
}

View File

@ -1,15 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
namespace Company.WebApplication1.Identity.Models.AccountViewModels
{
public class ForgotPasswordViewModel
{
[Required]
[EmailAddress]
public string Email { get; set; }
}
}

View File

@ -1,22 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
namespace Company.WebApplication1.Identity.Models.AccountViewModels
{
public class LoginViewModel
{
[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; }
}
}

View File

@ -1,22 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
namespace Company.WebApplication1.Identity.Models.AccountViewModels
{
public class LoginWith2faViewModel
{
[Required]
[StringLength(7, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
[DataType(DataType.Text)]
[Display(Name = "Authenticator code")]
public string TwoFactorCode { get; set; }
[Display(Name = "Remember this machine")]
public bool RememberMachine { get; set; }
public bool RememberMe { get; set; }
}
}

View File

@ -1,16 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
namespace Company.WebApplication1.Identity.Models.AccountViewModels
{
public class LoginWithRecoveryCodeViewModel
{
[Required]
[DataType(DataType.Text)]
[Display(Name = "Recovery Code")]
public string RecoveryCode { get; set; }
}
}

View File

@ -1,27 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
namespace Company.WebApplication1.Identity.Models.AccountViewModels
{
public class RegisterViewModel
{
[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; }
}
}

View File

@ -1,27 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
namespace Company.WebApplication1.Identity.Models.AccountViewModels
{
public class ResetPasswordViewModel
{
[Required]
[EmailAddress]
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)]
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 string Code { get; set; }
}
}

View File

@ -1,13 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
namespace Company.WebApplication1.Identity.Models
{
// Add profile data for application users by adding properties to the ApplicationUser class
public class ApplicationUser : IdentityUser
{
}
}

View File

@ -1,29 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
namespace Company.WebApplication1.Identity.Models.ManageViewModels
{
public class ChangePasswordViewModel
{
[Required]
[DataType(DataType.Password)]
[Display(Name = "Current password")]
public string OldPassword { 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 = "New password")]
public string NewPassword { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm new password")]
[Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
public string StatusMessage { get; set; }
}
}

View File

@ -1,23 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
namespace Company.WebApplication1.Identity.Models.ManageViewModels
{
public class EnableAuthenticatorViewModel
{
[Required]
[StringLength(7, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
[DataType(DataType.Text)]
[Display(Name = "Verification Code")]
public string Code { get; set; }
[ReadOnly(true)]
public string SharedKey { get; set; }
public string AuthenticatorUri { get; set; }
}
}

View File

@ -1,20 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;
namespace Company.WebApplication1.Identity.Models.ManageViewModels
{
public class ExternalLoginsViewModel
{
public IList<UserLoginInfo> CurrentLogins { get; set; }
public IList<AuthenticationScheme> OtherLogins { get; set; }
public bool ShowRemoveButton { get; set; }
public string StatusMessage { get; set; }
}
}

View File

@ -1,13 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
namespace Company.WebApplication1.Identity.Models.ManageViewModels
{
public class GenerateRecoveryCodesViewModel
{
public string[] RecoveryCodes { get; set; }
}
}

View File

@ -1,25 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
namespace Company.WebApplication1.Identity.Models.ManageViewModels
{
public class IndexViewModel
{
public string Username { get; set; }
public bool IsEmailConfirmed { get; set; }
[Required]
[EmailAddress]
public string Email { get; set; }
[Phone]
[Display(Name = "Phone number")]
public string PhoneNumber { get; set; }
public string StatusMessage { get; set; }
}
}

View File

@ -1,14 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
namespace Company.WebApplication1.Identity.Models.ManageViewModels
{
public class RemoveLoginViewModel
{
public string LoginProvider { get; set; }
public string ProviderKey { get; set; }
}
}

View File

@ -1,24 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
namespace Company.WebApplication1.Identity.Models.ManageViewModels
{
public class SetPasswordViewModel
{
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "New password")]
public string NewPassword { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm new password")]
[Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
public string StatusMessage { get; set; }
}
}

View File

@ -1,17 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
namespace Company.WebApplication1.Identity.Models.ManageViewModels
{
public class TwoFactorAuthenticationViewModel
{
public bool HasAuthenticator { get; set; }
public int RecoveryCodesLeft { get; set; }
public bool Is2faEnabled { get; set; }
}
}

View File

@ -1,17 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Company.WebApplication1.Identity.Services
{
// This class is used by the application to send email for account confirmation and password reset.
// For more details see https://go.microsoft.com/fwlink/?LinkID=532713
public class EmailSender : IEmailSender
{
public Task SendEmailAsync(string email, string subject, string htmlMessage)
{
return Task.CompletedTask;
}
}
}

View File

@ -1,12 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Company.WebApplication1.Identity.Services
{
public interface IEmailSender
{
Task SendEmailAsync(string email, string subject, string htmlMessage);
}
}

View File

@ -1,8 +0,0 @@
@{
ViewData["Title"] = "Access denied";
}
<header>
<h2 class="text-danger">@ViewData["Title"]</h2>
<p class="text-danger">You do not have access to this resource.</p>
</header>

View File

@ -1,10 +0,0 @@
@{
ViewData["Title"] = "Confirm email";
}
<h2>@ViewData["Title"]</h2>
<div>
<p>
Thank you for confirming your email.
</p>
</div>

View File

@ -1,32 +0,0 @@
@model ExternalLoginViewModel
@{
ViewData["Title"] = "Register";
}
<h2>@ViewData["Title"]</h2>
<h4>Associate your @ViewData["LoginProvider"] account.</h4>
<hr />
<p class="text-info">
You've successfully authenticated with <strong>@ViewData["LoginProvider"]</strong>.
Please enter an email address for this site below and click the Register button to finish
logging in.
</p>
<div class="row">
<div class="col-md-4">
<form asp-action="ExternalLoginConfirmation" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post">
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="Email"></label>
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-default">Register</button>
</form>
</div>
</div>
@section Scripts {
@await Html.PartialAsync("_ValidationScriptsPartial")
}

View File

@ -1,25 +0,0 @@
@model ForgotPasswordViewModel
@{
ViewData["Title"] = "Forgot your password?";
}
<h2>@ViewData["Title"]</h2>
<h4>Enter your email.</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="ForgotPassword" method="post">
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="Email"></label>
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
</div>
</div>
@section Scripts {
@await Html.PartialAsync("_ValidationScriptsPartial")
}

View File

@ -1,8 +0,0 @@
@{
ViewData["Title"] = "Forgot password confirmation";
}
<h2>@ViewData["Title"]</h2>
<p>
Please check your email to reset your password.
</p>

View File

@ -1,8 +0,0 @@
@{
ViewData["Title"] = "Locked out";
}
<header>
<h2 class="text-danger">@ViewData["Title"]</h2>
<p class="text-danger">This account has been locked out, please try again later.</p>
</header>

View File

@ -1,87 +0,0 @@
@using System.Collections.Generic
@using System.Linq
@using Microsoft.AspNetCore.Http
@using Microsoft.AspNetCore.Http.Authentication
@model LoginViewModel
@inject SignInManager<ApplicationUser> SignInManager
@{
ViewData["Title"] = "Log in";
}
<h2>@ViewData["Title"]</h2>
<div class="row">
<div class="col-md-4">
<section>
<form asp-route-returnurl="@ViewData["ReturnUrl"]" 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="Email"></label>
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Password"></label>
<input asp-for="Password" class="form-control" />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div class="form-group">
<div class="checkbox">
<label asp-for="RememberMe">
<input asp-for="RememberMe" />
@Html.DisplayNameFor(m => m.RememberMe)
</label>
</div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-default">Log in</button>
</div>
<div class="form-group">
<p>
<a asp-action="ForgotPassword">Forgot your password?</a>
</p>
<p>
<a asp-action="Register" asp-route-returnurl="@ViewData["ReturnUrl"]">Register as a new user?</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 />
@{
var loginProviders = (await SignInManager.GetExternalAuthenticationSchemesAsync()).ToList();
if (loginProviders.Count == 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 asp-action="ExternalLogin" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post" class="form-horizontal">
<div>
<p>
@foreach (var provider in loginProviders)
{
<button type="submit" class="btn btn-default" name="provider" value="@provider.Name" title="Log in using your @provider.DisplayName account">@provider.Name</button>
}
</p>
</div>
</form>
}
}
</section>
</div>
</div>
@section Scripts {
@await Html.PartialAsync("_ValidationScriptsPartial")
}

View File

@ -1,40 +0,0 @@
@model LoginWith2faViewModel
@{
ViewData["Title"] = "Two-factor authentication";
}
<h2>@ViewData["Title"]</h2>
<hr />
<p>Your login is protected with an authenticator app. Enter your authenticator code below.</p>
<div class="row">
<div class="col-md-4">
<form method="post" asp-route-returnUrl="@ViewData["ReturnUrl"]">
<input asp-for="RememberMe" type="hidden" />
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="TwoFactorCode"></label>
<input asp-for="TwoFactorCode" class="form-control" autocomplete="off" />
<span asp-validation-for="TwoFactorCode" class="text-danger"></span>
</div>
<div class="form-group">
<div class="checkbox">
<label asp-for="RememberMachine">
<input asp-for="RememberMachine" />
@Html.DisplayNameFor(m => m.RememberMachine)
</label>
</div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-default">Log in</button>
</div>
</form>
</div>
</div>
<p>
Don't have access to your authenticator device? You can
<a asp-action="LoginWithRecoveryCode" asp-route-returnUrl="@ViewData["ReturnUrl"]">log in with a recovery code</a>.
</p>
@section Scripts {
@await Html.PartialAsync("_ValidationScriptsPartial")
}

View File

@ -1,28 +0,0 @@
@model LoginWithRecoveryCodeViewModel
@{
ViewData["Title"] = "Recovery code verification";
}
<h2>@ViewData["Title"]</h2>
<hr />
<p>
You have requested to login with a recovery code. This login will not be remembered until you provide
an authenticator app code at login or disable 2FA and login again.
</p>
<div class="row">
<div class="col-md-4">
<form method="post">
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="RecoveryCode"></label>
<input asp-for="RecoveryCode" class="form-control" autocomplete="off" />
<span asp-validation-for="RecoveryCode" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-default">Log in</button>
</form>
</div>
</div>
@section Scripts {
@await Html.PartialAsync("_ValidationScriptsPartial")
}

View File

@ -1,36 +0,0 @@
@model RegisterViewModel
@{
ViewData["Title"] = "Register";
}
<h2>@ViewData["Title"]</h2>
<div class="row">
<div class="col-md-4">
<form asp-route-returnUrl="@ViewData["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="Email"></label>
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Password"></label>
<input asp-for="Password" class="form-control" />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ConfirmPassword"></label>
<input asp-for="ConfirmPassword" class="form-control" />
<span asp-validation-for="ConfirmPassword" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-default">Register</button>
</form>
</div>
</div>
@section Scripts {
@await Html.PartialAsync("_ValidationScriptsPartial")
}

View File

@ -1,36 +0,0 @@
@model ResetPasswordViewModel
@{
ViewData["Title"] = "Reset password";
}
<h2>@ViewData["Title"]</h2>
<h4>Reset your password.</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form method="post">
<div asp-validation-summary="All" class="text-danger"></div>
<input asp-for="Code" type="hidden" />
<div class="form-group">
<label asp-for="Email"></label>
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Password"></label>
<input asp-for="Password" class="form-control" />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ConfirmPassword"></label>
<input asp-for="ConfirmPassword" class="form-control" />
<span asp-validation-for="ConfirmPassword" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-default">Reset</button>
</form>
</div>
</div>
@section Scripts {
@await Html.PartialAsync("_ValidationScriptsPartial")
}

View File

@ -1,8 +0,0 @@
@{
ViewData["Title"] = "Reset password confirmation";
}
<h2>@ViewData["Title"]</h2>
<p>
Your password has been reset. Please <a asp-action="Login">click here to log in</a>.
</p>

Some files were not shown because too many files have changed in this diff Show More