mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-02-22 22:25:28 +01:00
Add separate recovery seed backup view
fix
This commit is contained in:
parent
f6549cda33
commit
cfef1f3432
8 changed files with 226 additions and 3 deletions
|
@ -7,6 +7,7 @@ using System.Threading.Tasks;
|
||||||
using BTCPayServer.Data;
|
using BTCPayServer.Data;
|
||||||
using BTCPayServer.HostedServices;
|
using BTCPayServer.HostedServices;
|
||||||
using BTCPayServer.Models;
|
using BTCPayServer.Models;
|
||||||
|
using BTCPayServer.Models.StoreViewModels;
|
||||||
using BTCPayServer.Security;
|
using BTCPayServer.Security;
|
||||||
using BTCPayServer.Services.Apps;
|
using BTCPayServer.Services.Apps;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
@ -191,6 +192,13 @@ namespace BTCPayServer.Controllers
|
||||||
return View(vm);
|
return View(vm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Route("recovery-seed-backup")]
|
||||||
|
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||||
|
public IActionResult RecoverySeedBackup(RecoverySeedBackupViewModel vm)
|
||||||
|
{
|
||||||
|
return View("RecoverySeedBackup", vm);
|
||||||
|
}
|
||||||
|
|
||||||
public IActionResult Error()
|
public IActionResult Error()
|
||||||
{
|
{
|
||||||
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
|
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
|
||||||
|
|
|
@ -305,8 +305,17 @@ namespace BTCPayServer.Controllers
|
||||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||||
{
|
{
|
||||||
Severity = StatusMessageModel.StatusSeverity.Success,
|
Severity = StatusMessageModel.StatusSeverity.Success,
|
||||||
Html = $"Your wallet has been generated. Please store your seed securely! <br/><code class=\"alert-link\">{response.Mnemonic}</code>"
|
Html = $"Your wallet has been generated. Please store your seed securely!"
|
||||||
});
|
});
|
||||||
|
var vm = new RecoverySeedBackupViewModel()
|
||||||
|
{
|
||||||
|
CryptoCode = cryptoCode,
|
||||||
|
Mnemonic = response.Mnemonic,
|
||||||
|
Passphrase = response.Passphrase,
|
||||||
|
IsStored = request.SavePrivateKeys,
|
||||||
|
ReturnUrl = Url.Action(nameof(UpdateStore), new { storeId })
|
||||||
|
};
|
||||||
|
return this.RedirectToRecoverySeedBackup(vm);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -9,6 +9,7 @@ using BTCPayServer.Data;
|
||||||
using BTCPayServer.HostedServices;
|
using BTCPayServer.HostedServices;
|
||||||
using BTCPayServer.ModelBinders;
|
using BTCPayServer.ModelBinders;
|
||||||
using BTCPayServer.Models;
|
using BTCPayServer.Models;
|
||||||
|
using BTCPayServer.Models.StoreViewModels;
|
||||||
using BTCPayServer.Models.WalletViewModels;
|
using BTCPayServer.Models.WalletViewModels;
|
||||||
using BTCPayServer.Payments;
|
using BTCPayServer.Payments;
|
||||||
using BTCPayServer.Security;
|
using BTCPayServer.Security;
|
||||||
|
@ -1147,8 +1148,16 @@ namespace BTCPayServer.Controllers
|
||||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||||
{
|
{
|
||||||
Severity = StatusMessageModel.StatusSeverity.Success,
|
Severity = StatusMessageModel.StatusSeverity.Success,
|
||||||
Html = $"Please store your seed securely! <br/><code class=\"alert-link\">{seed}</code>"
|
Html = $"Please store your seed securely!"
|
||||||
});
|
});
|
||||||
|
var recoveryVm = new RecoverySeedBackupViewModel()
|
||||||
|
{
|
||||||
|
CryptoCode = walletId.CryptoCode,
|
||||||
|
Mnemonic = seed,
|
||||||
|
IsStored = true,
|
||||||
|
ReturnUrl = Url.Action(nameof(WalletSettings), new { walletId })
|
||||||
|
};
|
||||||
|
return this.RedirectToRecoverySeedBackup(recoveryVm);
|
||||||
}
|
}
|
||||||
|
|
||||||
return RedirectToAction(nameof(WalletSettings));
|
return RedirectToAction(nameof(WalletSettings));
|
||||||
|
|
|
@ -12,12 +12,14 @@ using BTCPayServer.Configuration;
|
||||||
using BTCPayServer.Data;
|
using BTCPayServer.Data;
|
||||||
using BTCPayServer.Lightning;
|
using BTCPayServer.Lightning;
|
||||||
using BTCPayServer.Models;
|
using BTCPayServer.Models;
|
||||||
|
using BTCPayServer.Models.StoreViewModels;
|
||||||
using BTCPayServer.Payments;
|
using BTCPayServer.Payments;
|
||||||
using BTCPayServer.Payments.Bitcoin;
|
using BTCPayServer.Payments.Bitcoin;
|
||||||
using BTCPayServer.Services;
|
using BTCPayServer.Services;
|
||||||
using BTCPayServer.Services.Invoices;
|
using BTCPayServer.Services.Invoices;
|
||||||
using BTCPayServer.Services.Wallets;
|
using BTCPayServer.Services.Wallets;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
@ -429,5 +431,23 @@ namespace BTCPayServer
|
||||||
{
|
{
|
||||||
ctx.Items["BTCPAY.STORESDATA"] = storeData;
|
ctx.Items["BTCPAY.STORESDATA"] = storeData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IActionResult RedirectToRecoverySeedBackup(this Controller controller, RecoverySeedBackupViewModel vm)
|
||||||
|
{
|
||||||
|
var redirectVm = new PostRedirectViewModel()
|
||||||
|
{
|
||||||
|
AspController = "Home",
|
||||||
|
AspAction = "RecoverySeedBackup",
|
||||||
|
Parameters =
|
||||||
|
{
|
||||||
|
new KeyValuePair<string, string>("cryptoCode", vm.CryptoCode),
|
||||||
|
new KeyValuePair<string, string>("mnemonic", vm.Mnemonic),
|
||||||
|
new KeyValuePair<string, string>("passphrase", vm.Passphrase),
|
||||||
|
new KeyValuePair<string, string>("isStored", vm.IsStored ? "true" : "false"),
|
||||||
|
new KeyValuePair<string, string>("returnUrl", vm.ReturnUrl)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return controller.View("PostRedirect", redirectVm);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using NBitcoin;
|
||||||
|
|
||||||
|
namespace BTCPayServer.Models.StoreViewModels
|
||||||
|
{
|
||||||
|
public class RecoverySeedBackupViewModel
|
||||||
|
{
|
||||||
|
public string CryptoCode { get; set; }
|
||||||
|
public string Mnemonic { get; set; }
|
||||||
|
public string Passphrase { get; set; }
|
||||||
|
public bool IsStored { get; set; }
|
||||||
|
public string ReturnUrl { get; set; }
|
||||||
|
|
||||||
|
public string[] Words
|
||||||
|
{
|
||||||
|
get => Mnemonic.Split(" ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
59
BTCPayServer/Views/Home/RecoverySeedBackup.cshtml
Normal file
59
BTCPayServer/Views/Home/RecoverySeedBackup.cshtml
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
@model RecoverySeedBackupViewModel
|
||||||
|
@{
|
||||||
|
Layout = "_LayoutSimple";
|
||||||
|
ViewData["Title"] = "Save your recovery phrase";
|
||||||
|
}
|
||||||
|
|
||||||
|
<div class="row justify-content-md-center">
|
||||||
|
<div class="col-md-9 col-lg-8">
|
||||||
|
<partial name="_StatusMessage" />
|
||||||
|
|
||||||
|
<h1 class="text-center text-primary mb-5">@ViewData["Title"]</h1>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 col-sm-2 d-flex align-items-center justify-content-center">
|
||||||
|
<span class="fa fa-warning align-self-center p-3" style="font-size:3.5em;"></span>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-sm-10 lead">
|
||||||
|
<p>
|
||||||
|
The words below are called your recovery phrase.
|
||||||
|
Please save these words securely.
|
||||||
|
They will allow you to recover your wallet.
|
||||||
|
</p>
|
||||||
|
@if (!Model.IsStored)
|
||||||
|
{
|
||||||
|
<p class="mt-3 mb-3">The recovery phrase will only be shown before being wiped from the server.</p>
|
||||||
|
}
|
||||||
|
<p class="mt-3 mb-0">
|
||||||
|
Do not photograph or store this in a non air-gapped digital format.
|
||||||
|
Anyone with access to your recovery phrase can steal your funds.
|
||||||
|
</p>
|
||||||
|
@if (!string.IsNullOrEmpty(Model.Passphrase))
|
||||||
|
{
|
||||||
|
<p class="mt-3 mb-0">Please make also sure to store your passphrase.</p>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ol class="my-5 d-flex flex-wrap justify-content-center align-items-center p-0" style="max-width:800px">
|
||||||
|
@foreach (var word in Model.Words)
|
||||||
|
{
|
||||||
|
<li class="ml-4 p-3 text-secondary" style="flex: 0 1 11em">
|
||||||
|
<span class="text-dark h5">@word</span>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
</ol>
|
||||||
|
<style>
|
||||||
|
form#recoveryConfirmation button { position: absolute; bottom:0; left:50%; width:200px; margin-left:-100px; }
|
||||||
|
form#recoveryConfirmation button:not([disabled]) { display: none; }
|
||||||
|
form#recoveryConfirmation input:checked ~ button[disabled] { display: none; }
|
||||||
|
form#recoveryConfirmation input:checked + button:not([disabled]) { display: inline-block; }
|
||||||
|
</style>
|
||||||
|
<form id="recoveryConfirmation" action="@Model.ReturnUrl" class="d-flex align-items-center justify-content-center" style="padding-bottom: 80px">
|
||||||
|
<label class="form-check-label lead order-2" for="confirm">I have saved my recovery phrase in a secure location</label>
|
||||||
|
<input type="checkbox" class="mr-3 order-1" id="confirm">
|
||||||
|
<button type="submit" class="btn btn-primary btn-lg px-5 order-3">Done</button>
|
||||||
|
<button type="submit" class="btn btn-primary btn-lg px-5 order-3" disabled>Done</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -1,12 +1,25 @@
|
||||||
@model PostRedirectViewModel
|
@model PostRedirectViewModel
|
||||||
@{
|
@{
|
||||||
Layout = null;
|
Layout = null;
|
||||||
|
|
||||||
|
var routeData = Context.GetRouteData();
|
||||||
|
var routeParams = new Dictionary<string, string>();
|
||||||
|
if (routeData != null)
|
||||||
|
{
|
||||||
|
routeParams["walletId"] = routeData.Values["walletId"]?.ToString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
<html>
|
<html>
|
||||||
<head></head>
|
<head></head>
|
||||||
<body>
|
<body>
|
||||||
<form method="post" id="postform" asp-action="@Model.AspAction" asp-controller="@Model.AspController" asp-route-walletId="@this.Context.GetRouteValue("walletId").ToString()">
|
<form
|
||||||
|
method="post"
|
||||||
|
id="postform"
|
||||||
|
asp-action="@Model.AspAction"
|
||||||
|
asp-controller="@Model.AspController"
|
||||||
|
asp-all-route-data="@routeParams"
|
||||||
|
>
|
||||||
@foreach(var o in Model.Parameters) {
|
@foreach(var o in Model.Parameters) {
|
||||||
<input type="hidden" name="@o.Key" value="@o.Value" />
|
<input type="hidden" name="@o.Key" value="@o.Value" />
|
||||||
}
|
}
|
||||||
|
|
84
BTCPayServer/Views/Shared/_LayoutPlain.cshtml
Normal file
84
BTCPayServer/Views/Shared/_LayoutPlain.cshtml
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
@{
|
||||||
|
Layout = null;
|
||||||
|
}
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<partial name="Header" />
|
||||||
|
|
||||||
|
<link href="~/main/fonts/Montserrat.css" rel="stylesheet" asp-append-version="true">
|
||||||
|
<style>
|
||||||
|
.content-wrapper {
|
||||||
|
padding: 50px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-head {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
padding: 0 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.head-logo {
|
||||||
|
height: 70px;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lead-title {
|
||||||
|
font-family: Montserrat;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 24px;
|
||||||
|
line-height: 1.2;
|
||||||
|
max-width: 17em;
|
||||||
|
letter-spacing: .1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lead-login {
|
||||||
|
font-family: Montserrat;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: normal;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 1.8;
|
||||||
|
letter-spacing: 0.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@media screen and (min-width: 768px) {
|
||||||
|
.content-wrapper {
|
||||||
|
padding: 75px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-head {
|
||||||
|
text-align: left;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lead-title {
|
||||||
|
font-size: 34px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lead-login {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.head-logo {
|
||||||
|
height: 80px;
|
||||||
|
margin: 0 30px 0 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<section class="content-wrapper">
|
||||||
|
<!-- Dummy navbar-brand, hackish way to keep test AssertNoError passing -->
|
||||||
|
<div class="navbar-brand d-none"></div>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
@RenderBody()
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
@await Html.PartialAsync("_ValidationScriptsPartial")
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Add table
Reference in a new issue