Support Bootstrap 3 and Bootstrap 4 on idenity UI
This commit is contained in:
parent
51dac741db
commit
ac744d8adb
|
|
@ -8,7 +8,8 @@
|
|||
"Exclusions": {
|
||||
"DOC_MISSING": {
|
||||
"lib/netstandard2.0/Microsoft.AspNetCore.Identity.UI.dll": "This is a library with packaged UI",
|
||||
"lib/netstandard2.0/Microsoft.AspNetCore.Identity.UI.Views.dll": "This assembly is generated and contains precompiled razor pages"
|
||||
"lib/netstandard2.0/Microsoft.AspNetCore.Identity.UI.Views.V3.dll": "This assembly is generated and contains precompiled razor pages",
|
||||
"lib/netstandard2.0/Microsoft.AspNetCore.Identity.UI.Views.V4.dll": "This assembly is generated and contains precompiled razor pages"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,5 +38,5 @@
|
|||
</form>
|
||||
|
||||
@section Scripts {
|
||||
@{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,5 +34,5 @@
|
|||
</form>
|
||||
|
||||
@section Scripts {
|
||||
@{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
@using Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal
|
||||
|
|
@ -1 +0,0 @@
|
|||
@using Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal
|
||||
|
|
@ -3,7 +3,8 @@
|
|||
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal
|
||||
namespace Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Internal
|
||||
|
||||
{
|
||||
public class AccessDeniedModel : PageModel
|
||||
{
|
||||
|
|
@ -7,7 +7,8 @@ using Microsoft.AspNetCore.Authorization;
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal
|
||||
namespace Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Internal
|
||||
|
||||
{
|
||||
[AllowAnonymous]
|
||||
[IdentityDefaultUI(typeof(ConfirmEmailModel<>))]
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
@page
|
||||
@model ExternalLoginModel
|
||||
@{
|
||||
ViewData["Title"] = "Register";
|
||||
}
|
||||
|
||||
<h2>@ViewData["Title"]</h2>
|
||||
<h4 id="external-login-title">Associate your @Model.ProviderDisplayName account.</h4>
|
||||
<hr />
|
||||
|
||||
<p id="external-login-description" class="text-info">
|
||||
You've successfully authenticated with <strong>@Model.ProviderDisplayName</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 {
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
}
|
||||
|
|
@ -11,7 +11,8 @@ using Microsoft.AspNetCore.Mvc;
|
|||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal
|
||||
namespace Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Internal
|
||||
|
||||
{
|
||||
[AllowAnonymous]
|
||||
[IdentityDefaultUI(typeof(ExternalLoginModel<>))]
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
@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 {
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
}
|
||||
|
|
@ -10,7 +10,8 @@ using Microsoft.AspNetCore.Identity.UI.Services;
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal
|
||||
namespace Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Internal
|
||||
|
||||
{
|
||||
[AllowAnonymous]
|
||||
[IdentityDefaultUI(typeof(ForgotPasswordModel<>))]
|
||||
|
|
@ -51,7 +52,7 @@ namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal
|
|||
return RedirectToPage("./ForgotPasswordConfirmation");
|
||||
}
|
||||
|
||||
// For more information on how to enable account confirmation and password reset please
|
||||
// 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.Page(
|
||||
|
|
@ -4,7 +4,8 @@
|
|||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal
|
||||
namespace Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Internal
|
||||
|
||||
{
|
||||
[AllowAnonymous]
|
||||
public class ForgotPasswordConfirmation : PageModel
|
||||
|
|
@ -4,7 +4,8 @@
|
|||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal
|
||||
namespace Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Internal
|
||||
|
||||
{
|
||||
[AllowAnonymous]
|
||||
public class LockoutModel : PageModel
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
@page
|
||||
@model LoginModel
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Log in";
|
||||
}
|
||||
|
||||
<h2>@ViewData["Title"]</h2>
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<section>
|
||||
<form id="account" method="post">
|
||||
<h4>Use a local account to log in.</h4>
|
||||
<hr />
|
||||
<div asp-validation-summary="All" class="text-danger"></div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Input.Email"></label>
|
||||
<input asp-for="Input.Email" class="form-control" />
|
||||
<span asp-validation-for="Input.Email" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Input.Password"></label>
|
||||
<input asp-for="Input.Password" class="form-control" />
|
||||
<span asp-validation-for="Input.Password" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label asp-for="Input.RememberMe">
|
||||
<input asp-for="Input.RememberMe" />
|
||||
@Html.DisplayNameFor(m => m.Input.RememberMe)
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button type="submit" class="btn btn-default">Log in</button>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<p>
|
||||
<a id="forgot-password" asp-page="./ForgotPassword">Forgot your password?</a>
|
||||
</p>
|
||||
<p>
|
||||
<a asp-page="./Register" asp-route-returnUrl="@Model.ReturnUrl">Register as a new user</a>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
</div>
|
||||
<div class="col-md-6 col-md-offset-2">
|
||||
<section>
|
||||
<h4>Use another service to log in.</h4>
|
||||
<hr />
|
||||
@{
|
||||
if ((Model.ExternalLogins?.Count ?? 0) == 0)
|
||||
{
|
||||
<div>
|
||||
<p>
|
||||
There are no external authentication services configured. See <a href="https://go.microsoft.com/fwlink/?LinkID=532715">this article</a>
|
||||
for details on setting up this ASP.NET application to support logging in via external services.
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<form id="external-account" asp-page="./ExternalLogin" asp-route-returnUrl="@Model.ReturnUrl" method="post" class="form-horizontal">
|
||||
<div>
|
||||
<p>
|
||||
@foreach (var provider in Model.ExternalLogins)
|
||||
{
|
||||
<button type="submit" class="btn btn-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 {
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
}
|
||||
|
|
@ -12,7 +12,8 @@ using Microsoft.AspNetCore.Mvc;
|
|||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal
|
||||
namespace Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Internal
|
||||
|
||||
{
|
||||
[AllowAnonymous]
|
||||
[IdentityDefaultUI(typeof(LoginModel<>))]
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
@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 id="recovery-code-login" asp-page="./LoginWithRecoveryCode" asp-route-returnUrl="@Model.ReturnUrl">log in with a recovery code</a>.
|
||||
</p>
|
||||
|
||||
@section Scripts {
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
}
|
||||
|
|
@ -9,7 +9,8 @@ using Microsoft.AspNetCore.Mvc;
|
|||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal
|
||||
namespace Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Internal
|
||||
|
||||
{
|
||||
[AllowAnonymous]
|
||||
[IdentityDefaultUI(typeof(LoginWith2faModel<>))]
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
@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 {
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
}
|
||||
|
|
@ -9,7 +9,8 @@ using Microsoft.AspNetCore.Mvc;
|
|||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal
|
||||
namespace Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Internal
|
||||
|
||||
{
|
||||
[AllowAnonymous]
|
||||
[IdentityDefaultUI(typeof(LoginWithRecoveryCodeModel<>))]
|
||||
|
|
@ -8,7 +8,8 @@ using Microsoft.AspNetCore.Mvc;
|
|||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal
|
||||
namespace Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Internal
|
||||
|
||||
{
|
||||
[AllowAnonymous]
|
||||
[IdentityDefaultUI(typeof(LogoutModel<>))]
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
@page
|
||||
@model ChangePasswordModel
|
||||
@{
|
||||
ViewData["Title"] = "Change password";
|
||||
ViewData["ActivePage"] = ManageNavPages.ChangePassword;
|
||||
}
|
||||
|
||||
<h4>@ViewData["Title"]</h4>
|
||||
<partial name="_StatusMessage" model="Model.StatusMessage" />
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<form id="change-password-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 {
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
}
|
||||
|
|
@ -8,7 +8,8 @@ using Microsoft.AspNetCore.Mvc;
|
|||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal
|
||||
namespace Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Manage.Internal
|
||||
|
||||
{
|
||||
[IdentityDefaultUI(typeof(ChangePasswordModel<>))]
|
||||
public abstract class ChangePasswordModel : PageModel
|
||||
|
|
@ -24,7 +24,7 @@
|
|||
<input asp-for="Input.Password" class="form-control" />
|
||||
<span asp-validation-for="Input.Password" class="text-danger"></span>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
<button class="btn btn-danger" type="submit">Delete data and close my account</button>
|
||||
</form>
|
||||
</div>
|
||||
|
|
@ -8,7 +8,8 @@ using Microsoft.AspNetCore.Mvc;
|
|||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal
|
||||
namespace Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Manage.Internal
|
||||
|
||||
{
|
||||
[IdentityDefaultUI(typeof(DeletePersonalDataModel<>))]
|
||||
public abstract class DeletePersonalDataModel : PageModel
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
@page
|
||||
@model Disable2faModel
|
||||
@{
|
||||
ViewData["Title"] = "Disable two-factor authentication (2FA)";
|
||||
ViewData["ActivePage"] = ManageNavPages.TwoFactorAuthentication;
|
||||
}
|
||||
|
||||
<partial name="_StatusMessage" model="Model.StatusMessage" />
|
||||
<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>
|
||||
|
|
@ -7,7 +7,8 @@ using Microsoft.AspNetCore.Mvc;
|
|||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal
|
||||
namespace Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Manage.Internal
|
||||
|
||||
{
|
||||
[IdentityDefaultUI(typeof(Disable2faModel<>))]
|
||||
public abstract class Disable2faModel : PageModel
|
||||
|
|
@ -11,7 +11,8 @@ using Microsoft.AspNetCore.Mvc.RazorPages;
|
|||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal
|
||||
namespace Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Manage.Internal
|
||||
|
||||
{
|
||||
[IdentityDefaultUI(typeof(DownloadPersonalDataModel<>))]
|
||||
public abstract class DownloadPersonalDataModel : PageModel
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
@page
|
||||
@model EnableAuthenticatorModel
|
||||
@{
|
||||
ViewData["Title"] = "Configure authenticator app";
|
||||
ViewData["ActivePage"] = ManageNavPages.TwoFactorAuthentication;
|
||||
}
|
||||
|
||||
<partial name="_StatusMessage" model="Model.StatusMessage" />
|
||||
<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="@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 id="send-code" 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 {
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
}
|
||||
|
|
@ -11,7 +11,8 @@ using Microsoft.AspNetCore.Mvc;
|
|||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal
|
||||
namespace Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Manage.Internal
|
||||
|
||||
{
|
||||
[IdentityDefaultUI(typeof(EnableAuthenticatorModel<>))]
|
||||
public class EnableAuthenticatorModel : PageModel
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
@page
|
||||
@model ExternalLoginsModel
|
||||
@{
|
||||
ViewData["Title"] = "Manage your external logins";
|
||||
ViewData["ActivePage"] = ManageNavPages.ExternalLogins;
|
||||
}
|
||||
|
||||
<partial name="_StatusMessage" model="Model.StatusMessage" />
|
||||
@if (Model.CurrentLogins?.Count > 0)
|
||||
{
|
||||
<h4>Registered Logins</h4>
|
||||
<table class="table">
|
||||
<tbody>
|
||||
@foreach (var login in Model.CurrentLogins)
|
||||
{
|
||||
<tr>
|
||||
<td id="@($"login-provider-{login.LoginProvider}")">@login.ProviderDisplayName</td>
|
||||
<td>
|
||||
@if (Model.ShowRemoveButton)
|
||||
{
|
||||
<form id="@($"remove-login-{login.LoginProvider}")" 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 id="link-login-form" asp-page-handler="LinkLogin" method="post" class="form-horizontal">
|
||||
<div id="socialLoginList">
|
||||
<p>
|
||||
@foreach (var provider in Model.OtherLogins)
|
||||
{
|
||||
<button id="@($"link-login-button-{provider.Name}")" 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>
|
||||
}
|
||||
|
|
@ -10,7 +10,8 @@ using Microsoft.AspNetCore.Authentication;
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal
|
||||
namespace Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Manage.Internal
|
||||
|
||||
{
|
||||
[IdentityDefaultUI(typeof(ExternalLoginsModel<>))]
|
||||
public abstract class ExternalLoginsModel : PageModel
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
@page
|
||||
@model GenerateRecoveryCodesModel
|
||||
@{
|
||||
ViewData["Title"] = "Generate two-factor authentication (2FA) recovery codes";
|
||||
ViewData["ActivePage"] = ManageNavPages.TwoFactorAuthentication;
|
||||
}
|
||||
|
||||
<partial name="_StatusMessage" model="Model.StatusMessage" />
|
||||
<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>
|
||||
<p>
|
||||
Generating new recovery codes 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">Generate Recovery Codes</button>
|
||||
</form>
|
||||
</div>
|
||||
|
|
@ -8,7 +8,8 @@ using Microsoft.AspNetCore.Mvc;
|
|||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal
|
||||
namespace Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Manage.Internal
|
||||
|
||||
{
|
||||
[IdentityDefaultUI(typeof(GenerateRecoveryCodesModel<>))]
|
||||
public abstract class GenerateRecoveryCodesModel : PageModel
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
@page
|
||||
@model IndexModel
|
||||
@{
|
||||
ViewData["Title"] = "Profile";
|
||||
ViewData["ActivePage"] = ManageNavPages.Index;
|
||||
}
|
||||
|
||||
<h4>@ViewData["Title"]</h4>
|
||||
<partial name="_StatusMessage" model="Model.StatusMessage" />
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<form id="profile-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 id="email-verification" type="submit" 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 id="update-profile-button" type="submit" class="btn btn-default">Save</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
}
|
||||
|
|
@ -9,7 +9,8 @@ using Microsoft.AspNetCore.Identity.UI.Services;
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal
|
||||
namespace Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Manage.Internal
|
||||
|
||||
{
|
||||
[IdentityDefaultUI(typeof(IndexModel<>))]
|
||||
public abstract class IndexModel : PageModel
|
||||
|
|
@ -4,7 +4,8 @@
|
|||
using System;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal
|
||||
namespace Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Manage.Internal
|
||||
|
||||
{
|
||||
public static class ManageNavPages
|
||||
{
|
||||
|
|
@ -35,7 +36,7 @@ namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal
|
|||
public static string PersonalDataNavClass(ViewContext viewContext) => PageNavClass(viewContext, PersonalData);
|
||||
|
||||
public static string TwoFactorAuthenticationNavClass(ViewContext viewContext) => PageNavClass(viewContext, TwoFactorAuthentication);
|
||||
|
||||
|
||||
public static string PageNavClass(ViewContext viewContext, string page)
|
||||
{
|
||||
var activePage = viewContext.ViewData["ActivePage"] as string
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
@page
|
||||
@model PersonalDataModel
|
||||
@{
|
||||
ViewData["Title"] = "Personal Data";
|
||||
ViewData["ActivePage"] = ManageNavPages.PersonalData;
|
||||
}
|
||||
|
||||
<h4>@ViewData["Title"]</h4>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<p>Your account contains personal data that you have given us. This page allows you to download or delete that data.</p>
|
||||
<p>
|
||||
<strong>Deleting this data will permanently remove your account, and this cannot be recovered.</strong>
|
||||
</p>
|
||||
<form id="download-data" asp-page="DownloadPersonalData" method="post" class="form-group">
|
||||
<button class="btn btn-default" type="submit">Download</button>
|
||||
</form>
|
||||
<p>
|
||||
<a id="delete" asp-page="DeletePersonalData" class="btn btn-default">Delete</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
}
|
||||
|
|
@ -7,7 +7,8 @@ using Microsoft.AspNetCore.Mvc;
|
|||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal
|
||||
namespace Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Manage.Internal
|
||||
|
||||
{
|
||||
[IdentityDefaultUI(typeof(PersonalDataModel<>))]
|
||||
public abstract class PersonalDataModel : PageModel
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
@page
|
||||
@model ResetAuthenticatorModel
|
||||
@{
|
||||
ViewData["Title"] = "Reset authenticator key";
|
||||
ViewData["ActivePage"] = ManageNavPages.TwoFactorAuthentication;
|
||||
}
|
||||
|
||||
<partial name="_StatusMessage" model="Model.StatusMessage" />
|
||||
<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.
|
||||
If you do not complete your authenticator app configuration you may lose access to your account.
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<form id="reset-authenticator-form" method="post" class="form-group">
|
||||
<button id="reset-authenticator-button" class="btn btn-danger" type="submit">Reset authenticator key</button>
|
||||
</form>
|
||||
</div>
|
||||
|
|
@ -7,7 +7,8 @@ using Microsoft.AspNetCore.Mvc;
|
|||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal
|
||||
namespace Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Manage.Internal
|
||||
|
||||
{
|
||||
[IdentityDefaultUI(typeof(ResetAuthenticatorModel<>))]
|
||||
public abstract class ResetAuthenticatorModel : PageModel
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
@page
|
||||
@model SetPasswordModel
|
||||
@{
|
||||
ViewData["Title"] = "Set password";
|
||||
ViewData["ActivePage"] = ManageNavPages.ChangePassword;
|
||||
}
|
||||
|
||||
<h4>Set your password</h4>
|
||||
<partial name="_StatusMessage" model="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 id="set-password-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 {
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
}
|
||||
|
|
@ -7,7 +7,8 @@ using System.Threading.Tasks;
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal
|
||||
namespace Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Manage.Internal
|
||||
|
||||
{
|
||||
[IdentityDefaultUI(typeof(SetPasswordModel<>))]
|
||||
public abstract class SetPasswordModel : PageModel
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
@page
|
||||
@model ShowRecoveryCodesModel
|
||||
@{
|
||||
ViewData["Title"] = "Recovery codes";
|
||||
ViewData["ActivePage"] = "TwoFactorAuthentication";
|
||||
}
|
||||
|
||||
<partial name="_StatusMessage" model="Model.StatusMessage" />
|
||||
<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.Length; row += 2)
|
||||
{
|
||||
<code class="recovery-code">@Model.RecoveryCodes[row]</code><text> </text><code class="recovery-code">@Model.RecoveryCodes[row + 1]</code><br />
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -4,7 +4,8 @@
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal
|
||||
namespace Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Manage.Internal
|
||||
|
||||
{
|
||||
public class ShowRecoveryCodesModel : PageModel
|
||||
{
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
@page
|
||||
@model TwoFactorAuthenticationModel
|
||||
@{
|
||||
ViewData["Title"] = "Two-factor authentication (2FA)";
|
||||
ViewData["ActivePage"] = ManageNavPages.TwoFactorAuthentication;
|
||||
}
|
||||
|
||||
<partial name="_StatusMessage" model="Model.StatusMessage" />
|
||||
<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>
|
||||
}
|
||||
|
||||
if (Model.IsMachineRemembered)
|
||||
{
|
||||
<form method="post" style="display: inline-block">
|
||||
<button type="submit" class="btn btn-default">Forget this browser</button>
|
||||
</form>
|
||||
}
|
||||
<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 id="enable-authenticator" asp-page="./EnableAuthenticator" class="btn btn-default">Add authenticator app</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<a id="enable-authenticator" asp-page="./EnableAuthenticator" class="btn btn-default">Setup authenticator app</a>
|
||||
<a id="reset-authenticator" asp-page="./ResetAuthenticator" class="btn btn-default">Reset authenticator app</a>
|
||||
}
|
||||
|
||||
@section Scripts {
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
}
|
||||
|
|
@ -7,7 +7,8 @@ using Microsoft.AspNetCore.Mvc;
|
|||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal
|
||||
namespace Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Manage.Internal
|
||||
|
||||
{
|
||||
[IdentityDefaultUI(typeof(TwoFactorAuthenticationModel<>))]
|
||||
public abstract class TwoFactorAuthenticationModel : PageModel
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
@{
|
||||
@{
|
||||
if (ViewData.TryGetValue("ParentLayout", out var parentLayout))
|
||||
{
|
||||
Layout = (string)parentLayout;
|
||||
|
|
@ -27,4 +27,3 @@
|
|||
@section Scripts {
|
||||
@RenderSection("Scripts", required: false)
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<ul class="nav nav-pills nav-stacked">
|
||||
<li class="@ManageNavPages.IndexNavClass(ViewContext)"><a id="profile" asp-page="./Index">Profile</a></li>
|
||||
<li class="@ManageNavPages.ChangePasswordNavClass(ViewContext)"><a id="change-password" asp-page="./ChangePassword">Password</a></li>
|
||||
@if ((bool)ViewData["ManageNav.HasExternalLogins"])
|
||||
{
|
||||
<li id="external-logins" class="@ManageNavPages.ExternalLoginsNavClass(ViewContext)"><a id="external-login" asp-page="./ExternalLogins">External logins</a></li>
|
||||
}
|
||||
<li class="@ManageNavPages.TwoFactorAuthenticationNavClass(ViewContext)"><a id="two-factor" asp-page="./TwoFactorAuthentication">Two-factor authentication</a></li>
|
||||
<li class="@ManageNavPages.PersonalDataNavClass(ViewContext)"><a id="personal-data" asp-page="./PersonalData">Personal data</a></li>
|
||||
</ul>
|
||||
|
|
@ -0,0 +1 @@
|
|||
@using Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Manage.Internal
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
@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 {
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
}
|
||||
|
|
@ -12,7 +12,8 @@ using Microsoft.AspNetCore.Mvc;
|
|||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal
|
||||
namespace Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Internal
|
||||
|
||||
{
|
||||
[AllowAnonymous]
|
||||
[IdentityDefaultUI(typeof(RegisterModel<>))]
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
@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 {
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
}
|
||||
|
|
@ -8,7 +8,8 @@ using Microsoft.AspNetCore.Authorization;
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal
|
||||
namespace Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Internal
|
||||
|
||||
{
|
||||
[AllowAnonymous]
|
||||
[IdentityDefaultUI(typeof(ResetPasswordModel<>))]
|
||||
|
|
@ -4,7 +4,8 @@
|
|||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal
|
||||
namespace Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Internal
|
||||
|
||||
{
|
||||
[AllowAnonymous]
|
||||
public class ResetPasswordConfirmationModel : PageModel
|
||||
|
|
@ -0,0 +1 @@
|
|||
@using Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Internal
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Diagnostics;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.UI.V3.Pages.Internal
|
||||
|
||||
{
|
||||
[AllowAnonymous]
|
||||
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
|
||||
public class ErrorModel : PageModel
|
||||
{
|
||||
public string RequestId { get; set; }
|
||||
|
||||
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
|
||||
|
||||
public void OnGet()
|
||||
{
|
||||
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
@using Microsoft.AspNetCore.Hosting
|
||||
@using Microsoft.AspNetCore.Mvc.ViewEngines
|
||||
@inject IHostingEnvironment Environment
|
||||
@inject ICompositeViewEngine Engine
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>@ViewData["Title"] - @Environment.ApplicationName</title>
|
||||
|
||||
<environment include="Development">
|
||||
<link rel="stylesheet" href="~/Identity/lib/bootstrap/dist/css/bootstrap.css" />
|
||||
<link rel="stylesheet" href="~/Identity/css/site.css" />
|
||||
</environment>
|
||||
<environment exclude="Development">
|
||||
<link rel="stylesheet" href="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/css/bootstrap.min.css"
|
||||
asp-fallback-href="~/Identity/lib/bootstrap/dist/css/bootstrap.min.css"
|
||||
asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute" />
|
||||
<link rel="stylesheet" href="~/Identity/css/site.min.css" asp-append-version="true" />
|
||||
</environment>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-inverse navbar-fixed-top">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a href="~/" class="navbar-brand">@Environment.ApplicationName</a>
|
||||
</div>
|
||||
<div class="navbar-collapse collapse">
|
||||
@{
|
||||
var result = Engine.FindView(ViewContext, "_LoginPartial", isMainPage: false);
|
||||
}
|
||||
@if (result.Success)
|
||||
{
|
||||
await Html.RenderPartialAsync("_LoginPartial");
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("The default Identity UI layout requires a partial view '_LoginPartial' " +
|
||||
"usually located at '/Pages/_LoginPartial' or at '/Views/Shared/_LoginPartial' to work. Based on your configuration " +
|
||||
$"we have looked at it in the following locations: {System.Environment.NewLine}{string.Join(System.Environment.NewLine, result.SearchedLocations)}.");
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="container body-content">
|
||||
@RenderBody()
|
||||
<hr />
|
||||
<footer>
|
||||
<p>© 2017 - @Environment.ApplicationName</p>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<environment include="Development">
|
||||
<script src="~/Identity/lib/jquery/dist/jquery.js"></script>
|
||||
<script src="~/Identity/lib/bootstrap/dist/js/bootstrap.js"></script>
|
||||
<script src="~/Identity/js/site.js" asp-append-version="true"></script>
|
||||
</environment>
|
||||
<environment exclude="Development">
|
||||
<script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-3.3.1.min.js"
|
||||
asp-fallback-src="~/Identity/lib/jquery/dist/jquery.min.js"
|
||||
asp-fallback-test="window.jQuery"
|
||||
crossorigin="anonymous"
|
||||
integrity="sha384-tsQFqpEReu7ZLhBV2VZlAu7zcOV+rXbYlF2cqB8txI/8aZajjp4Bqd+V6D5IgvKT">
|
||||
</script>
|
||||
<script src="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/bootstrap.min.js"
|
||||
asp-fallback-src="~/Identity/lib/bootstrap/dist/js/bootstrap.min.js"
|
||||
asp-fallback-test="window.jQuery && window.jQuery.fn && window.jQuery.fn.modal"
|
||||
crossorigin="anonymous"
|
||||
integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa">
|
||||
</script>
|
||||
<script src="~/Identity/js/site.min.js" asp-append-version="true"></script>
|
||||
</environment>
|
||||
|
||||
@RenderSection("Scripts", required: false)
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<environment include="Development">
|
||||
<script src="~/Identity/lib/jquery-validation/dist/jquery.validate.js"></script>
|
||||
<script src="~/Identity/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
|
||||
</environment>
|
||||
<environment exclude="Development">
|
||||
<script src="https://ajax.aspnetcdn.com/ajax/jquery.validate/1.17.0/jquery.validate.min.js"
|
||||
asp-fallback-src="~/Identity/lib/jquery-validation/dist/jquery.validate.min.js"
|
||||
asp-fallback-test="window.jQuery && window.jQuery.validator"
|
||||
crossorigin="anonymous"
|
||||
integrity="sha384-rZfj/ogBloos6wzLGpPkkOr/gpkBNLZ6b6yLy4o+ok+t/SAKlL5mvXLr0OXNi1Hp">
|
||||
</script>
|
||||
<script src="https://ajax.aspnetcdn.com/ajax/jquery.validation.unobtrusive/3.2.9/jquery.validate.unobtrusive.min.js"
|
||||
asp-fallback-src="~/Identity/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"
|
||||
asp-fallback-test="window.jQuery && window.jQuery.validator && window.jQuery.validator.unobtrusive"
|
||||
crossorigin="anonymous"
|
||||
integrity="sha384-ifv0TYDWxBHzvAk2Z0n8R434FL1Rlv/Av18DXE43N/1rvHyOG4izKst0f2iSLdds">
|
||||
</script>
|
||||
</environment>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
@namespace Microsoft.AspNetCore.Identity.UI.V3.Pages.Internal
|
||||
|
||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
@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>
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.UI.V4.Pages.Account.Internal
|
||||
|
||||
{
|
||||
public class AccessDeniedModel : PageModel
|
||||
{
|
||||
public void OnGet()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
@page
|
||||
@model ConfirmEmailModel
|
||||
@{
|
||||
ViewData["Title"] = "Confirm email";
|
||||
}
|
||||
|
||||
<h2>@ViewData["Title"]</h2>
|
||||
<div>
|
||||
<p>
|
||||
Thank you for confirming your email.
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.UI.V4.Pages.Account.Internal
|
||||
|
||||
{
|
||||
[AllowAnonymous]
|
||||
[IdentityDefaultUI(typeof(ConfirmEmailModel<>))]
|
||||
public abstract class ConfirmEmailModel : PageModel
|
||||
{
|
||||
public virtual Task<IActionResult> OnGetAsync(string userId, string code) => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
internal class ConfirmEmailModel<TUser> : ConfirmEmailModel where TUser : class
|
||||
{
|
||||
private readonly UserManager<TUser> _userManager;
|
||||
|
||||
public ConfirmEmailModel(UserManager<TUser> userManager)
|
||||
{
|
||||
_userManager = userManager;
|
||||
}
|
||||
|
||||
public override 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)
|
||||
{
|
||||
return NotFound($"Unable to load user with ID '{userId}'.");
|
||||
}
|
||||
|
||||
var result = await _userManager.ConfirmEmailAsync(user, code);
|
||||
if (!result.Succeeded)
|
||||
{
|
||||
throw new InvalidOperationException($"Error confirming email for user with ID '{userId}':");
|
||||
}
|
||||
|
||||
return Page();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,10 +5,10 @@
|
|||
}
|
||||
|
||||
<h2>@ViewData["Title"]</h2>
|
||||
<h4>Associate your @Model.ProviderDisplayName account.</h4>
|
||||
<h4 id="external-login-title">Associate your @Model.ProviderDisplayName account.</h4>
|
||||
<hr />
|
||||
|
||||
<p class="text-info">
|
||||
<p id="external-login-description" class="text-info">
|
||||
You've successfully authenticated with <strong>@Model.ProviderDisplayName</strong>.
|
||||
Please enter an email address for this site below and click the Register button to finish
|
||||
logging in.
|
||||
|
|
@ -0,0 +1,186 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Security.Claims;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.UI.V4.Pages.Account.Internal
|
||||
|
||||
{
|
||||
[AllowAnonymous]
|
||||
[IdentityDefaultUI(typeof(ExternalLoginModel<>))]
|
||||
public class ExternalLoginModel : PageModel
|
||||
{
|
||||
[BindProperty]
|
||||
public InputModel Input { get; set; }
|
||||
|
||||
public string ProviderDisplayName { get; set; }
|
||||
|
||||
public string ReturnUrl { get; set; }
|
||||
|
||||
[TempData]
|
||||
public string ErrorMessage { get; set; }
|
||||
|
||||
public class InputModel
|
||||
{
|
||||
[Required]
|
||||
[EmailAddress]
|
||||
public string Email { get; set; }
|
||||
}
|
||||
|
||||
public virtual IActionResult OnGet() => throw new NotImplementedException();
|
||||
|
||||
public virtual IActionResult OnPost(string provider, string returnUrl = null) => throw new NotImplementedException();
|
||||
|
||||
public virtual Task<IActionResult> OnGetCallbackAsync(string returnUrl = null, string remoteError = null) => throw new NotImplementedException();
|
||||
|
||||
public virtual Task<IActionResult> OnPostConfirmationAsync(string returnUrl = null) => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
internal class ExternalLoginModel<TUser> : ExternalLoginModel where TUser : class
|
||||
{
|
||||
private readonly SignInManager<TUser> _signInManager;
|
||||
private readonly UserManager<TUser> _userManager;
|
||||
private readonly IUserStore<TUser> _userStore;
|
||||
private readonly IUserEmailStore<TUser> _emailStore;
|
||||
private readonly ILogger<ExternalLoginModel> _logger;
|
||||
|
||||
public ExternalLoginModel(
|
||||
SignInManager<TUser> signInManager,
|
||||
UserManager<TUser> userManager,
|
||||
IUserStore<TUser> userStore,
|
||||
ILogger<ExternalLoginModel> logger)
|
||||
{
|
||||
_signInManager = signInManager;
|
||||
_userManager = userManager;
|
||||
_userStore = userStore;
|
||||
_emailStore = GetEmailStore();
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public override IActionResult OnGet()
|
||||
{
|
||||
return RedirectToPage("./Login");
|
||||
}
|
||||
|
||||
public override 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 override async Task<IActionResult> OnGetCallbackAsync(string returnUrl = null, string remoteError = null)
|
||||
{
|
||||
returnUrl = returnUrl ?? Url.Content("~/");
|
||||
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(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;
|
||||
ProviderDisplayName = info.ProviderDisplayName;
|
||||
if (info.Principal.HasClaim(c => c.Type == ClaimTypes.Email))
|
||||
{
|
||||
Input = new InputModel
|
||||
{
|
||||
Email = info.Principal.FindFirstValue(ClaimTypes.Email)
|
||||
};
|
||||
}
|
||||
return Page();
|
||||
}
|
||||
}
|
||||
|
||||
public override async Task<IActionResult> OnPostConfirmationAsync(string returnUrl = null)
|
||||
{
|
||||
returnUrl = returnUrl ?? Url.Content("~/");
|
||||
// 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 = CreateUser();
|
||||
|
||||
await _userStore.SetUserNameAsync(user, Input.Email, CancellationToken.None);
|
||||
await _emailStore.SetEmailAsync(user, Input.Email, CancellationToken.None);
|
||||
|
||||
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(returnUrl);
|
||||
}
|
||||
}
|
||||
foreach (var error in result.Errors)
|
||||
{
|
||||
ModelState.AddModelError(string.Empty, error.Description);
|
||||
}
|
||||
}
|
||||
|
||||
ProviderDisplayName = info.ProviderDisplayName;
|
||||
ReturnUrl = returnUrl;
|
||||
return Page();
|
||||
}
|
||||
|
||||
private TUser CreateUser()
|
||||
{
|
||||
try
|
||||
{
|
||||
return Activator.CreateInstance<TUser>();
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw new InvalidOperationException($"Can't create an instance of '{nameof(TUser)}'. " +
|
||||
$"Ensure that '{nameof(TUser)}' is not an abstract class and has a parameterless constructor, or alternatively " +
|
||||
$"override the external login page in /Areas/Identity/Pages/Account/ExternalLogin.cshtml");
|
||||
}
|
||||
}
|
||||
|
||||
private IUserEmailStore<TUser> GetEmailStore()
|
||||
{
|
||||
if (!_userManager.SupportsUserEmail)
|
||||
{
|
||||
throw new NotSupportedException("The default UI requires a user store with email support.");
|
||||
}
|
||||
return (IUserEmailStore<TUser>)_userStore;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity.UI.Services;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.UI.V4.Pages.Account.Internal
|
||||
|
||||
{
|
||||
[AllowAnonymous]
|
||||
[IdentityDefaultUI(typeof(ForgotPasswordModel<>))]
|
||||
public abstract class ForgotPasswordModel : PageModel
|
||||
{
|
||||
[BindProperty]
|
||||
public InputModel Input { get; set; }
|
||||
|
||||
public class InputModel
|
||||
{
|
||||
[Required]
|
||||
[EmailAddress]
|
||||
public string Email { get; set; }
|
||||
}
|
||||
|
||||
public virtual Task<IActionResult> OnPostAsync() => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
internal class ForgotPasswordModel<TUser> : ForgotPasswordModel where TUser : class
|
||||
{
|
||||
private readonly UserManager<TUser> _userManager;
|
||||
private readonly IEmailSender _emailSender;
|
||||
|
||||
public ForgotPasswordModel(UserManager<TUser> userManager, IEmailSender emailSender)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_emailSender = emailSender;
|
||||
}
|
||||
|
||||
public override 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.Page(
|
||||
"/Account/ResetPassword",
|
||||
pageHandler: null,
|
||||
values: new { code },
|
||||
protocol: Request.Scheme);
|
||||
|
||||
await _emailSender.SendEmailAsync(
|
||||
Input.Email,
|
||||
"Reset Password",
|
||||
$"Please reset your password by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
|
||||
|
||||
return RedirectToPage("./ForgotPasswordConfirmation");
|
||||
}
|
||||
|
||||
return Page();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
@page
|
||||
@model ForgotPasswordConfirmation
|
||||
@{
|
||||
ViewData["Title"] = "Forgot password confirmation";
|
||||
}
|
||||
|
||||
<h2>@ViewData["Title"]</h2>
|
||||
<p>
|
||||
Please check your email to reset your password.
|
||||
</p>
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.UI.V4.Pages.Account.Internal
|
||||
|
||||
{
|
||||
[AllowAnonymous]
|
||||
public class ForgotPasswordConfirmation : PageModel
|
||||
{
|
||||
public void OnGet()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
@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>
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.UI.V4.Pages.Account.Internal
|
||||
|
||||
{
|
||||
[AllowAnonymous]
|
||||
public class LockoutModel : PageModel
|
||||
{
|
||||
public void OnGet()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.UI.V4.Pages.Account.Internal
|
||||
|
||||
{
|
||||
[AllowAnonymous]
|
||||
[IdentityDefaultUI(typeof(LoginModel<>))]
|
||||
public abstract class LoginModel : PageModel
|
||||
{
|
||||
[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 virtual Task OnGetAsync(string returnUrl = null) => throw new NotImplementedException();
|
||||
|
||||
public virtual Task<IActionResult> OnPostAsync(string returnUrl = null) => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
internal class LoginModel<TUser> : LoginModel where TUser : class
|
||||
{
|
||||
private readonly SignInManager<TUser> _signInManager;
|
||||
private readonly ILogger<LoginModel> _logger;
|
||||
|
||||
public LoginModel(SignInManager<TUser> signInManager, ILogger<LoginModel> logger)
|
||||
{
|
||||
_signInManager = signInManager;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public override async Task OnGetAsync(string returnUrl = null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(ErrorMessage))
|
||||
{
|
||||
ModelState.AddModelError(string.Empty, ErrorMessage);
|
||||
}
|
||||
|
||||
returnUrl = returnUrl ?? Url.Content("~/");
|
||||
|
||||
// Clear the existing external cookie to ensure a clean login process
|
||||
await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);
|
||||
|
||||
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
|
||||
|
||||
ReturnUrl = returnUrl;
|
||||
}
|
||||
|
||||
public override async Task<IActionResult> OnPostAsync(string returnUrl = null)
|
||||
{
|
||||
returnUrl = returnUrl ?? Url.Content("~/");
|
||||
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
// This doesn't count login failures towards account lockout
|
||||
// To enable password failures to trigger account lockout, set lockoutOnFailure: true
|
||||
var result = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: true);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
_logger.LogInformation("User logged in.");
|
||||
return LocalRedirect(returnUrl);
|
||||
}
|
||||
if (result.RequiresTwoFactor)
|
||||
{
|
||||
return RedirectToPage("./LoginWith2fa", new { ReturnUrl = returnUrl, RememberMe = Input.RememberMe });
|
||||
}
|
||||
if (result.IsLockedOut)
|
||||
{
|
||||
_logger.LogWarning("User account locked out.");
|
||||
return RedirectToPage("./Lockout");
|
||||
}
|
||||
else
|
||||
{
|
||||
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
|
||||
return Page();
|
||||
}
|
||||
}
|
||||
|
||||
// If we got this far, something failed, redisplay form
|
||||
return Page();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.UI.V4.Pages.Account.Internal
|
||||
|
||||
{
|
||||
[AllowAnonymous]
|
||||
[IdentityDefaultUI(typeof(LoginWith2faModel<>))]
|
||||
public abstract class LoginWith2faModel : PageModel
|
||||
{
|
||||
[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 virtual Task<IActionResult> OnGetAsync(bool rememberMe, string returnUrl = null) => throw new NotImplementedException();
|
||||
|
||||
public virtual Task<IActionResult> OnPostAsync(bool rememberMe, string returnUrl = null) => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
internal class LoginWith2faModel<TUser> : LoginWith2faModel where TUser : class
|
||||
{
|
||||
private readonly SignInManager<TUser> _signInManager;
|
||||
private readonly UserManager<TUser> _userManager;
|
||||
private readonly ILogger<LoginWith2faModel> _logger;
|
||||
|
||||
public LoginWith2faModel(
|
||||
SignInManager<TUser> signInManager,
|
||||
UserManager<TUser> userManager,
|
||||
ILogger<LoginWith2faModel> logger)
|
||||
{
|
||||
_signInManager = signInManager;
|
||||
_userManager = userManager;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public override 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 InvalidOperationException($"Unable to load two-factor authentication user.");
|
||||
}
|
||||
|
||||
ReturnUrl = returnUrl;
|
||||
RememberMe = rememberMe;
|
||||
|
||||
return Page();
|
||||
}
|
||||
|
||||
public override async Task<IActionResult> OnPostAsync(bool rememberMe, string returnUrl = null)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return Page();
|
||||
}
|
||||
|
||||
returnUrl = returnUrl ?? Url.Content("~/");
|
||||
|
||||
var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
|
||||
if (user == null)
|
||||
{
|
||||
throw new InvalidOperationException($"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);
|
||||
|
||||
var userId = await _userManager.GetUserIdAsync(user);
|
||||
|
||||
if (result.Succeeded)
|
||||
{
|
||||
_logger.LogInformation("User with ID '{UserId}' logged in with 2fa.", userId);
|
||||
return LocalRedirect(returnUrl);
|
||||
}
|
||||
else if (result.IsLockedOut)
|
||||
{
|
||||
_logger.LogWarning("User with ID '{UserId}' account locked out.", userId);
|
||||
return RedirectToPage("./Lockout");
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning("Invalid authenticator code entered for user with ID '{UserId}'.", userId);
|
||||
ModelState.AddModelError(string.Empty, "Invalid authenticator code.");
|
||||
return Page();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.UI.V4.Pages.Account.Internal
|
||||
|
||||
{
|
||||
[AllowAnonymous]
|
||||
[IdentityDefaultUI(typeof(LoginWithRecoveryCodeModel<>))]
|
||||
public abstract class LoginWithRecoveryCodeModel : PageModel
|
||||
{
|
||||
[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 virtual Task<IActionResult> OnGetAsync(string returnUrl = null) => throw new NotImplementedException();
|
||||
|
||||
public virtual Task<IActionResult> OnPostAsync(string returnUrl = null) => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
internal class LoginWithRecoveryCodeModel<TUser> : LoginWithRecoveryCodeModel where TUser: class
|
||||
{
|
||||
private readonly SignInManager<TUser> _signInManager;
|
||||
private readonly UserManager<TUser> _userManager;
|
||||
private readonly ILogger<LoginWithRecoveryCodeModel> _logger;
|
||||
|
||||
public LoginWithRecoveryCodeModel(
|
||||
SignInManager<TUser> signInManager,
|
||||
UserManager<TUser> userManager,
|
||||
ILogger<LoginWithRecoveryCodeModel> logger)
|
||||
{
|
||||
_signInManager = signInManager;
|
||||
_userManager = userManager;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public override 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 InvalidOperationException($"Unable to load two-factor authentication user.");
|
||||
}
|
||||
|
||||
ReturnUrl = returnUrl;
|
||||
|
||||
return Page();
|
||||
}
|
||||
|
||||
public override async Task<IActionResult> OnPostAsync(string returnUrl = null)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return Page();
|
||||
}
|
||||
|
||||
var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
|
||||
if (user == null)
|
||||
{
|
||||
throw new InvalidOperationException($"Unable to load two-factor authentication user.");
|
||||
}
|
||||
|
||||
var recoveryCode = Input.RecoveryCode.Replace(" ", string.Empty);
|
||||
|
||||
var result = await _signInManager.TwoFactorRecoveryCodeSignInAsync(recoveryCode);
|
||||
|
||||
var userId = await _userManager.GetUserIdAsync(user);
|
||||
|
||||
if (result.Succeeded)
|
||||
{
|
||||
_logger.LogInformation("User with ID '{UserId}' logged in with a recovery code.", userId);
|
||||
return LocalRedirect(returnUrl ?? Url.Content("~/"));
|
||||
}
|
||||
if (result.IsLockedOut)
|
||||
{
|
||||
_logger.LogWarning("User with ID '{UserId}' account locked out.", userId);
|
||||
return RedirectToPage("./Lockout");
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning("Invalid recovery code entered for user with ID '{UserId}' ", userId);
|
||||
ModelState.AddModelError(string.Empty, "Invalid recovery code entered.");
|
||||
return Page();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
@page
|
||||
@model LogoutModel
|
||||
@{
|
||||
ViewData["Title"] = "Log out";
|
||||
}
|
||||
|
||||
<header>
|
||||
<h1>@ViewData["Title"]</h1>
|
||||
<p>You have successfully logged out of the application.</p>
|
||||
</header>
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.UI.V4.Pages.Account.Internal
|
||||
|
||||
{
|
||||
[AllowAnonymous]
|
||||
[IdentityDefaultUI(typeof(LogoutModel<>))]
|
||||
public abstract class LogoutModel : PageModel
|
||||
{
|
||||
public void OnGet()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual Task<IActionResult> OnPost(string returnUrl = null) => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
internal class LogoutModel<TUser> : LogoutModel where TUser : class
|
||||
{
|
||||
private readonly SignInManager<TUser> _signInManager;
|
||||
private readonly ILogger<LogoutModel> _logger;
|
||||
|
||||
public LogoutModel(SignInManager<TUser> signInManager, ILogger<LogoutModel> logger)
|
||||
{
|
||||
_signInManager = signInManager;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public override async Task<IActionResult> OnPost(string returnUrl = null)
|
||||
{
|
||||
await _signInManager.SignOutAsync();
|
||||
_logger.LogInformation("User logged out.");
|
||||
if (returnUrl != null)
|
||||
{
|
||||
return LocalRedirect(returnUrl);
|
||||
}
|
||||
else
|
||||
{
|
||||
// This needs to be a redirect so that the browser performs a new
|
||||
// request and the identity for the user gets updated.
|
||||
return RedirectToPage();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.UI.V4.Pages.Account.Manage.Internal
|
||||
|
||||
{
|
||||
[IdentityDefaultUI(typeof(ChangePasswordModel<>))]
|
||||
public abstract class ChangePasswordModel : PageModel
|
||||
{
|
||||
[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 virtual Task<IActionResult> OnGetAsync() => throw new NotImplementedException();
|
||||
|
||||
public virtual Task<IActionResult> OnPostAsync() => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
internal class ChangePasswordModel<TUser> : ChangePasswordModel where TUser : class
|
||||
{
|
||||
private readonly UserManager<TUser> _userManager;
|
||||
private readonly SignInManager<TUser> _signInManager;
|
||||
private readonly ILogger<ChangePasswordModel> _logger;
|
||||
|
||||
public ChangePasswordModel(
|
||||
UserManager<TUser> userManager,
|
||||
SignInManager<TUser> signInManager,
|
||||
ILogger<ChangePasswordModel> logger)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_signInManager = signInManager;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public override async Task<IActionResult> OnGetAsync()
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
if (user == null)
|
||||
{
|
||||
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
|
||||
}
|
||||
|
||||
var hasPassword = await _userManager.HasPasswordAsync(user);
|
||||
if (!hasPassword)
|
||||
{
|
||||
return RedirectToPage("./SetPassword");
|
||||
}
|
||||
|
||||
return Page();
|
||||
}
|
||||
|
||||
public override async Task<IActionResult> OnPostAsync()
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return Page();
|
||||
}
|
||||
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
if (user == null)
|
||||
{
|
||||
return NotFound($"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.RefreshSignInAsync(user);
|
||||
_logger.LogInformation("User changed their password successfully.");
|
||||
StatusMessage = "Your password has been changed.";
|
||||
|
||||
return RedirectToPage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
@page
|
||||
@model DeletePersonalDataModel
|
||||
@{
|
||||
ViewData["Title"] = "Delete Personal Data";
|
||||
ViewData["ActivePage"] = ManageNavPages.PersonalData;
|
||||
}
|
||||
|
||||
<h4>@ViewData["Title"]</h4>
|
||||
|
||||
<div class="alert alert-warning" role="alert">
|
||||
<p>
|
||||
<span class="glyphicon glyphicon-warning-sign"></span>
|
||||
<strong>Deleting this data will permanently remove your account, and this cannot be recovered.</strong>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<form id="delete-user" method="post" class="form-group">
|
||||
<div asp-validation-summary="All" class="text-danger"></div>
|
||||
@if (Model.RequirePassword)
|
||||
{
|
||||
<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>
|
||||
}
|
||||
<button class="btn btn-danger" type="submit">Delete data and close my account</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
}
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.UI.V4.Pages.Account.Manage.Internal
|
||||
|
||||
{
|
||||
[IdentityDefaultUI(typeof(DeletePersonalDataModel<>))]
|
||||
public abstract class DeletePersonalDataModel : PageModel
|
||||
{
|
||||
[BindProperty]
|
||||
public InputModel Input { get; set; }
|
||||
|
||||
public class InputModel
|
||||
{
|
||||
[Required]
|
||||
[DataType(DataType.Password)]
|
||||
public string Password { get; set; }
|
||||
}
|
||||
|
||||
public bool RequirePassword { get; set; }
|
||||
|
||||
public virtual Task<IActionResult> OnGet() => throw new NotImplementedException();
|
||||
|
||||
public virtual Task<IActionResult> OnPostAsync() => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
internal class DeletePersonalDataModel<TUser> : DeletePersonalDataModel where TUser: class
|
||||
{
|
||||
private readonly UserManager<TUser> _userManager;
|
||||
private readonly SignInManager<TUser> _signInManager;
|
||||
private readonly ILogger<DeletePersonalDataModel> _logger;
|
||||
|
||||
public DeletePersonalDataModel(
|
||||
UserManager<TUser> userManager,
|
||||
SignInManager<TUser> signInManager,
|
||||
ILogger<DeletePersonalDataModel> logger)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_signInManager = signInManager;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public override async Task<IActionResult> OnGet()
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
if (user == null)
|
||||
{
|
||||
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
|
||||
}
|
||||
|
||||
RequirePassword = await _userManager.HasPasswordAsync(user);
|
||||
return Page();
|
||||
}
|
||||
|
||||
public override async Task<IActionResult> OnPostAsync()
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
if (user == null)
|
||||
{
|
||||
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
|
||||
}
|
||||
|
||||
RequirePassword = await _userManager.HasPasswordAsync(user);
|
||||
if (RequirePassword)
|
||||
{
|
||||
if (!await _userManager.CheckPasswordAsync(user, Input.Password))
|
||||
{
|
||||
ModelState.AddModelError(string.Empty, "Password not correct.");
|
||||
return Page();
|
||||
}
|
||||
}
|
||||
|
||||
var result = await _userManager.DeleteAsync(user);
|
||||
var userId = await _userManager.GetUserIdAsync(user);
|
||||
if (!result.Succeeded)
|
||||
{
|
||||
throw new InvalidOperationException($"Unexpected error occurred deleteing user with ID '{userId}'.");
|
||||
}
|
||||
|
||||
await _signInManager.SignOutAsync();
|
||||
|
||||
_logger.LogInformation("User with ID '{UserId}' deleted themselves.", userId);
|
||||
|
||||
return Redirect("~/");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.UI.V4.Pages.Account.Manage.Internal
|
||||
|
||||
{
|
||||
[IdentityDefaultUI(typeof(Disable2faModel<>))]
|
||||
public abstract class Disable2faModel : PageModel
|
||||
{
|
||||
[TempData]
|
||||
public string StatusMessage { get; set; }
|
||||
|
||||
public virtual Task<IActionResult> OnGet() => throw new NotImplementedException();
|
||||
|
||||
public virtual Task<IActionResult> OnPostAsync() => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
internal class Disable2faModel<TUser> : Disable2faModel where TUser : class
|
||||
{
|
||||
private readonly UserManager<TUser> _userManager;
|
||||
private readonly ILogger<Disable2faModel> _logger;
|
||||
|
||||
public Disable2faModel(
|
||||
UserManager<TUser> userManager,
|
||||
ILogger<Disable2faModel> logger)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public override async Task<IActionResult> OnGet()
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
if (user == null)
|
||||
{
|
||||
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
|
||||
}
|
||||
|
||||
if (!await _userManager.GetTwoFactorEnabledAsync(user))
|
||||
{
|
||||
throw new InvalidOperationException($"Cannot disable 2FA for user with ID '{_userManager.GetUserId(User)}' as it's not currently enabled.");
|
||||
}
|
||||
|
||||
return Page();
|
||||
}
|
||||
|
||||
public override async Task<IActionResult> OnPostAsync()
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
if (user == null)
|
||||
{
|
||||
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
|
||||
}
|
||||
|
||||
var disable2faResult = await _userManager.SetTwoFactorEnabledAsync(user, false);
|
||||
if (!disable2faResult.Succeeded)
|
||||
{
|
||||
throw new InvalidOperationException($"Unexpected error occurred disabling 2FA for user with ID '{_userManager.GetUserId(User)}'.");
|
||||
}
|
||||
|
||||
_logger.LogInformation("User with ID '{UserId}' has disabled 2fa.", _userManager.GetUserId(User));
|
||||
StatusMessage = "2fa has been disabled. You can reenable 2fa when you setup an authenticator app";
|
||||
return RedirectToPage("./TwoFactorAuthentication");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
@page
|
||||
@model DownloadPersonalDataModel
|
||||
@{
|
||||
ViewData["Title"] = "Download Your Data";
|
||||
ViewData["ActivePage"] = ManageNavPages.PersonalData;
|
||||
}
|
||||
|
||||
<h4>@ViewData["Title"]</h4>
|
||||
|
||||
@section Scripts {
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.UI.V4.Pages.Account.Manage.Internal
|
||||
|
||||
{
|
||||
[IdentityDefaultUI(typeof(DownloadPersonalDataModel<>))]
|
||||
public abstract class DownloadPersonalDataModel : PageModel
|
||||
{
|
||||
public virtual IActionResult OnGet() => throw new NotImplementedException();
|
||||
|
||||
public virtual Task<IActionResult> OnPostAsync() => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
internal class DownloadPersonalDataModel<TUser> : DownloadPersonalDataModel where TUser : class
|
||||
{
|
||||
private readonly UserManager<TUser> _userManager;
|
||||
private readonly ILogger<DownloadPersonalDataModel> _logger;
|
||||
|
||||
public DownloadPersonalDataModel(
|
||||
UserManager<TUser> userManager,
|
||||
ILogger<DownloadPersonalDataModel> logger)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public override IActionResult OnGet()
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
public override async Task<IActionResult> OnPostAsync()
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
if (user == null)
|
||||
{
|
||||
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
|
||||
}
|
||||
|
||||
_logger.LogInformation("User with ID '{UserId}' asked for their personal data.", _userManager.GetUserId(User));
|
||||
|
||||
// Only include personal data for download
|
||||
var personalData = new Dictionary<string, string>();
|
||||
var personalDataProps = typeof(TUser).GetProperties().Where(
|
||||
prop => Attribute.IsDefined(prop, typeof(PersonalDataAttribute)));
|
||||
foreach (var p in personalDataProps)
|
||||
{
|
||||
personalData.Add(p.Name, p.GetValue(user)?.ToString() ?? "null");
|
||||
}
|
||||
|
||||
var logins = await _userManager.GetLoginsAsync(user);
|
||||
foreach (var l in logins)
|
||||
{
|
||||
personalData.Add($"{l.LoginProvider} external login provider key", l.ProviderKey);
|
||||
}
|
||||
|
||||
personalData.Add($"Authenticator Key", await _userManager.GetAuthenticatorKeyAsync(user));
|
||||
|
||||
Response.Headers.Add("Content-Disposition", "attachment; filename=PersonalData.json");
|
||||
return new FileContentResult(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(personalData)), "text/json");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,165 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.UI.V4.Pages.Account.Manage.Internal
|
||||
|
||||
{
|
||||
[IdentityDefaultUI(typeof(EnableAuthenticatorModel<>))]
|
||||
public class EnableAuthenticatorModel : PageModel
|
||||
{
|
||||
public string SharedKey { get; set; }
|
||||
|
||||
public string AuthenticatorUri { get; set; }
|
||||
|
||||
[TempData]
|
||||
public string[] RecoveryCodes { get; set; }
|
||||
|
||||
[TempData]
|
||||
public string StatusMessage { 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 virtual Task<IActionResult> OnGetAsync() => throw new NotImplementedException();
|
||||
|
||||
public virtual Task<IActionResult> OnPostAsync() => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
internal class EnableAuthenticatorModel<TUser> : EnableAuthenticatorModel where TUser : class
|
||||
{
|
||||
private readonly UserManager<TUser> _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<TUser> userManager,
|
||||
ILogger<EnableAuthenticatorModel> logger,
|
||||
UrlEncoder urlEncoder)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_logger = logger;
|
||||
_urlEncoder = urlEncoder;
|
||||
}
|
||||
|
||||
public override async Task<IActionResult> OnGetAsync()
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
if (user == null)
|
||||
{
|
||||
NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
|
||||
}
|
||||
|
||||
await LoadSharedKeyAndQrCodeUriAsync(user);
|
||||
|
||||
return Page();
|
||||
}
|
||||
|
||||
public override async Task<IActionResult> OnPostAsync()
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
if (user == null)
|
||||
{
|
||||
return NotFound($"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);
|
||||
var userId = await _userManager.GetUserIdAsync(user);
|
||||
_logger.LogInformation("User with ID '{UserId}' has enabled 2FA with an authenticator app.", userId);
|
||||
|
||||
StatusMessage = "Your authenticator app has been verified.";
|
||||
|
||||
if (await _userManager.CountRecoveryCodesAsync(user) == 0)
|
||||
{
|
||||
var recoveryCodes = await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(user, 10);
|
||||
RecoveryCodes = recoveryCodes.ToArray();
|
||||
return RedirectToPage("./ShowRecoveryCodes");
|
||||
}
|
||||
else
|
||||
{
|
||||
return RedirectToPage("./TwoFactorAuthentication");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task LoadSharedKeyAndQrCodeUriAsync(TUser user)
|
||||
{
|
||||
// Load the authenticator key & QR code URI to display on the form
|
||||
var unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user);
|
||||
if (string.IsNullOrEmpty(unformattedKey))
|
||||
{
|
||||
await _userManager.ResetAuthenticatorKeyAsync(user);
|
||||
unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user);
|
||||
}
|
||||
|
||||
SharedKey = FormatKey(unformattedKey);
|
||||
|
||||
var email = await _userManager.GetEmailAsync(user);
|
||||
AuthenticatorUri = GenerateQrCodeUri(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("Microsoft.AspNetCore.Identity.UI"),
|
||||
_urlEncoder.Encode(email),
|
||||
unformattedKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue