Replace identity code with built-in version on the templates
This commit is contained in:
parent
8c94d27d6a
commit
5325c65b88
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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}" />
|
||||
|
|
|
|||
|
|
@ -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" />
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
@ -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>.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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")
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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")
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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")
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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")
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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")
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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")
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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&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")
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
@:
|
||||
}
|
||||
</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>
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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> </text><code>@Model.RecoveryCodes[row + 1]</code><br />
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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")
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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")
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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")
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
@ -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>
|
||||
|
||||
|
|
@ -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">×</span></button>
|
||||
@Model
|
||||
</div>
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
@using Company.WebApplication1.Pages.Account.Manage
|
||||
|
|
@ -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")
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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")
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
@using Company.WebApplication1.Pages.Account
|
||||
|
|
@ -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>
|
||||
}
|
||||
|
|
@ -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>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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 =>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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": [
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
@{
|
||||
ViewData["Title"] = "Confirm email";
|
||||
}
|
||||
|
||||
<h2>@ViewData["Title"]</h2>
|
||||
<div>
|
||||
<p>
|
||||
Thank you for confirming your email.
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -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")
|
||||
}
|
||||
|
|
@ -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")
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
@{
|
||||
ViewData["Title"] = "Forgot password confirmation";
|
||||
}
|
||||
|
||||
<h2>@ViewData["Title"]</h2>
|
||||
<p>
|
||||
Please check your email to reset your password.
|
||||
</p>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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")
|
||||
}
|
||||
|
|
@ -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")
|
||||
}
|
||||
|
|
@ -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")
|
||||
}
|
||||
|
|
@ -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")
|
||||
}
|
||||
|
|
@ -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")
|
||||
}
|
||||
|
|
@ -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
Loading…
Reference in New Issue