Fix 2FA config screens

This commit is contained in:
Kukks 2020-07-13 08:13:27 +02:00
parent b728cd61ae
commit 006ebf3f15
10 changed files with 119 additions and 76 deletions

View file

@ -4,6 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using BTCPayServer.Data;
using BTCPayServer.Models;
using BTCPayServer.Models.ManageViewModels;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
@ -26,7 +27,6 @@ namespace BTCPayServer.Controllers
var model = new TwoFactorAuthenticationViewModel
{
HasAuthenticator = await _userManager.GetAuthenticatorKeyAsync(user) != null,
Is2faEnabled = user.TwoFactorEnabled,
RecoveryCodesLeft = await _userManager.CountRecoveryCodesAsync(user),
};
@ -49,11 +49,19 @@ namespace BTCPayServer.Controllers
$"Unexpected error occurred disabling 2FA for user with ID '{user.Id}'.");
}
return View(nameof(Disable2fa));
return View("Confirm",
new ConfirmModel()
{
Title = $"Disable two-factor authentication (2FA)",
DescriptionHtml = true,
Description =
$"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 href=\"{Url.Action(nameof(ResetAuthenticatorWarning))}\"> reset your authenticator keys</a>.",
Action = "Disable 2FA",
ActionUrl = Url.ActionLink(nameof(Disable2fa))
});
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Disable2fa()
{
var user = await _userManager.GetUserAsync(User);
@ -105,7 +113,8 @@ namespace BTCPayServer.Controllers
}
// Strip spaces and hypens
var verificationCode = model.Code.Replace(" ", string.Empty, StringComparison.OrdinalIgnoreCase).Replace("-", string.Empty, StringComparison.OrdinalIgnoreCase);
var verificationCode = model.Code.Replace(" ", string.Empty, StringComparison.OrdinalIgnoreCase)
.Replace("-", string.Empty, StringComparison.OrdinalIgnoreCase);
var is2faTokenValid = await _userManager.VerifyTwoFactorTokenAsync(
user, _userManager.Options.Tokens.AuthenticatorTokenProvider, verificationCode);
@ -122,17 +131,24 @@ namespace BTCPayServer.Controllers
var recoveryCodes = await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(user, 10);
TempData[RecoveryCodesKey] = recoveryCodes.ToArray();
return RedirectToAction(nameof(GenerateRecoveryCodes));
return RedirectToAction(nameof(GenerateRecoveryCodes), new {confirm = false});
}
[HttpGet]
public IActionResult ResetAuthenticatorWarning()
{
return View(nameof(ResetAuthenticator));
return View("Confirm",
new ConfirmModel()
{
Title = $"Reset authenticator key",
Description =
$"This process disables 2FA until you verify your authenticator app and will also reset your 2FA recovery codes.{Environment.NewLine}If you do not complete your authenticator app configuration you may lose access to your account.",
Action = "Reset",
ActionUrl = Url.ActionLink(nameof(ResetAuthenticator))
});
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ResetAuthenticator()
{
var user = await _userManager.GetUserAsync(User);
@ -149,15 +165,39 @@ namespace BTCPayServer.Controllers
}
[HttpGet]
public IActionResult GenerateRecoveryCodes()
public async Task<IActionResult> GenerateRecoveryCodes(bool confirm = true)
{
if (!confirm)
{
return await GenerateRecoveryCodes();
}
return View("Confirm",
new ConfirmModel()
{
Title = $"Are you sure you want to generate new recovery codes?",
Description = "Your existing recovery codes will no longer be valid!",
Action = "Generate",
ActionUrl = Url.ActionLink(nameof(GenerateRecoveryCodes))
});
}
[HttpPost]
public async Task<IActionResult> GenerateRecoveryCodes()
{
var recoveryCodes = (string[])TempData[RecoveryCodesKey];
if (recoveryCodes == null)
{
return RedirectToAction(nameof(TwoFactorAuthentication));
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
recoveryCodes = (await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(user, 10)).ToArray();
}
var model = new GenerateRecoveryCodesViewModel { RecoveryCodes = recoveryCodes };
var model = new GenerateRecoveryCodesViewModel {RecoveryCodes = recoveryCodes};
return View(model);
}

View file

@ -19,6 +19,9 @@ namespace BTCPayServer.Models
{
get; set;
}
public bool DescriptionHtml { get; set; } = false;
public string Action
{
get; set;

View file

@ -2,7 +2,6 @@ namespace BTCPayServer.Models.ManageViewModels
{
public class TwoFactorAuthenticationViewModel
{
public bool HasAuthenticator { get; set; }
public int RecoveryCodesLeft { get; set; }

View file

@ -1,23 +0,0 @@
@{
ViewData.SetActivePageAndTitle(ManageNavPages.TwoFactorAuthentication, "Disable two-factor authentication (2FA)");
}
<div class="alert alert-warning" role="alert">
<h4 class="alert-heading mb-3">
<span class="fa fa-warning"></span>
This action only disables 2FA.
</h4>
<p class="mb-0">
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-action="ResetAuthenticatorWarning" class="alert-link">
reset your authenticator keys.
</a>
</p>
</div>
<div>
<form asp-action="Disable2fa" method="post" class="form-group">
<button class="btn btn-danger" type="submit">Disable 2FA</button>
</form>
</div>

View file

@ -22,6 +22,6 @@
</div>
<div class="row mt-4">
<div class="col-md-12">
<a asp-action="Index" class="btn btn-primary">I wrote down my recovery codes</a>
<a asp-action="TwoFactorAuthentication" class="btn btn-primary">I wrote down my recovery codes</a>
</div>
</div>

View file

@ -1,21 +0,0 @@
@{
ViewData.SetActivePageAndTitle(ManageNavPages.TwoFactorAuthentication, "Reset authenticator key");
}
<div class="alert alert-warning" role="alert">
<h4 class="alert-heading mb-3">
<span class="fa fa-warning"></span>
Your authenticator app will not work until you reconfigure it.
</h4>
<p class="mb-2">
This process disables 2FA until you verify your authenticator app and will also reset your 2FA recovery codes.
</p>
<p class="mb-0">
If you do not complete your authenticator app configuration you may lose access to your account.
</p>
</div>
<div>
<form asp-action="ResetAuthenticator" method="post" class="form-group">
<button class="btn btn-danger" type="submit">Reset authenticator key</button>
</form>
</div>

View file

@ -20,7 +20,7 @@
<div class="alert alert-danger">
<h4 class="alert-heading mb-3">
<span class="fa fa-warning"></span>
You have 1 recovery code left.
You only have 1 recovery code left.
</h4>
<p class="mb-0">You can <a asp-action="GenerateRecoveryCodes" class="alert-link">generate a new set of recovery codes</a>.</p>
</div>
@ -30,31 +30,60 @@
<div class="alert alert-warning">
<h4 class="alert-heading mb-3">
<span class="fa fa-warning"></span>
You have @Model.RecoveryCodesLeft recovery codes left.
You only have @Model.RecoveryCodesLeft recovery codes left.
</h4>
<p class="mb-0">You should <a asp-action="GenerateRecoveryCodes" class="alert-link">generate a new set of recovery codes</a>.</p>
</div>
}
<h5>Your current 2FA</h5>
<ul>
<li><a asp-action="Disable2faWarning">Disable 2FA</a></li>
<li><a asp-action="GenerateRecoveryCodes">Reset recovery codes</a></li>
</ul>
}
<h5 class="mt-4">Authenticator app</h5>
<ul>
<div class="row">
<div class="list-group">
@if(!Model.HasAuthenticator)
{
<li><a asp-action="EnableAuthenticator">Add authenticator app</a></li>
}
else
{
<li><a asp-action="EnableAuthenticator">Configure authenticator app</a></li>
<li><a asp-action="ResetAuthenticatorWarning">Reset authenticator key</a></li>
}
</ul>
@if (Model.Is2faEnabled)
{
<a asp-action="Disable2faWarning" class="list-group-item d-flex justify-content-between align-items-center list-group-item-action">
<div>
<h5 >Disable 2FA</h5>
<p class="mb-1">Disable two-factor authentication. Re-enabling will not require you to reconfigure your Authenticator app. </p>
</div>
<i class="fa fa-chevron-right"></i>
</a>
<a asp-action="GenerateRecoveryCodes" class="list-group-item d-flex justify-content-between align-items-center list-group-item-action">
<div>
<h5 >Reset recovery codes</h5>
<p class="mb-1">Regenerate your two-factor recovery codes.</p>
</div>
<i class="fa fa-chevron-right"></i>
</a>
<a asp-action="EnableAuthenticator" class="list-group-item d-flex justify-content-between align-items-center list-group-item-action">
<div>
<h5 >Configure Authenticator app</h5>
<p class="mb-1">Display the key or QR code to configure an authenticator app with your current setup.</p>
</div>
<i class="fa fa-chevron-right"></i>
</a>
<a asp-action="ResetAuthenticatorWarning" class="list-group-item d-flex justify-content-between align-items-center list-group-item-action">
<div>
<h5 >Reset Authenticator app</h5>
<p class="mb-1">Invalidates the current authenticator configuration. Useful if you believe your authenticator settings were compromised.</p>
</div>
<i class="fa fa-chevron-right"></i>
</a>
}
else
{
<a asp-action="EnableAuthenticator" class="list-group-item d-flex justify-content-between align-items-center list-group-item-action">
<div>
<h5 >Enable 2FA</h5>
<p class="mb-1">Enable two-factor authentication using TOTP with apps such as Google Authenticator.</p>
</div>
<i class="fa fa-chevron-right"></i>
</a>
}
</div>
</div>
@section Scripts {
@await Html.PartialAsync("_ValidationScriptsPartial")

View file

@ -19,7 +19,16 @@
<div class="modal-body">
<div class="row">
<div class="col-lg-12 text-center">
<p>@Model.Description</p>
<p>
@if (Model.DescriptionHtml)
{
@Safe.Raw(Model.Description)
}
else
{
@Model.Description
}
</p>
</div>
</div>
@if (!String.IsNullOrEmpty(Model.Action))

View file

@ -110,3 +110,7 @@ header.masthead::before,
.table th {
border-top: 0;
}
.list-group-item-action {
color: var(--btcpay-body-color);
}

View file

@ -42,3 +42,6 @@
.table-sm tbody tr:hover {
background-color: var(--btcpay-color-neutral-950);
}
.list-group-item-action {
color: var(--btcpay-body-color);
}