Hide wallet creation option when permissions disabled
What changed, and why it matters
This commit fixes a UI bug where BTCPay Server was showing a 'Create a new wallet' button to users who did not have permission to create wallets. The button itself was just a link; the actual wallet creation endpoint still enforced permissions separately. So the fix is about hiding an option that shouldn't appear, not about blocking an unauthorized action. It is a defense-in-depth or user-experience security improvement rather than a critical vulnerability.
No urgent action required beyond applying the patch. If running an older version, verify that the GenerateWallet controller action still enforces CanCreateHotWallet / CanCreateWalletWatchOnly server-side, which this diff implies but does not modify. Consider reviewing related setup views for similar UI-only permission leaks.
Security signals we found
UI now respects server-side wallet creation permissions
Information disclosure / privilege confusion reduced by hiding unauthorized options
Server-side authorization on GenerateWallet endpoint already existed
Fixes GitHub issue #6991
Evidence from the diff
The SetupWallet view previously rendered a ‘Create a new wallet’ link unconditionally. The patch passes wallet creation permissions (CanCreateHotWallet and CanCreateWalletWatchOnly) into the view model and only renders the GenerateWallet link when at least one of those permissions is true. The underlying GenerateWallet action still requires the appropriate policy, so the server-side enforcement was already present. The change removes misleading section headings and keeps the ‘Connect an existing wallet’ import option always visible because import is always allowed.
Changed components
BTCPayServer/Controllers/UIStoresController.Onchain.csBTCPayServer/Views/UIStores/SetupWallet.cshtmlOn-chain wallet setup UIInspect captured patch +21 / −17
diff --git a/BTCPayServer/Controllers/UIStoresController.Onchain.cs b/BTCPayServer/Controllers/UIStoresController.Onchain.cs
index de16eb2..9ea7ed2 100644
--- a/BTCPayServer/Controllers/UIStoresController.Onchain.cs
+++ b/BTCPayServer/Controllers/UIStoresController.Onchain.cs
@@ -31,7 +31,7 @@ public partial class UIStoresController
{
[HttpGet("{storeId}/onchain/{cryptoCode}")]
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
- public ActionResult SetupWallet(WalletSetupViewModel vm)
+ public async Task<ActionResult> SetupWallet(WalletSetupViewModel vm)
{
var checkResult = IsAvailable(vm.CryptoCode, out var store, out _);
if (checkResult != null)
@@ -42,6 +42,9 @@ public partial class UIStoresController
var derivation = GetExistingDerivationStrategy(vm.CryptoCode, store);
vm.DerivationScheme = derivation?.AccountDerivation.ToString();
+ var perm = await CanUseHotWallet();
+ vm.SetPermission(perm);
+
return View(vm);
}
diff --git a/BTCPayServer/Views/UIStores/SetupWallet.cshtml b/BTCPayServer/Views/UIStores/SetupWallet.cshtml
index 78f17ce..60cc805 100644
--- a/BTCPayServer/Views/UIStores/SetupWallet.cshtml
+++ b/BTCPayServer/Views/UIStores/SetupWallet.cshtml
@@ -10,7 +10,6 @@
<h1 class="text-center" text-translate="true">Let's get started</h1>
<br>
<div class="mt-5">
- <h3 class="my-4" text-translate="true">I have a wallet</h3>
<div class="list-group">
<a asp-controller="UIStores" asp-action="ImportWallet" asp-route-storeId="@Model.StoreId" asp-route-cryptoCode="@Model.CryptoCode" id="ImportWalletOptionsLink" class="list-group-item list-group-item-action">
<div class="image">
@@ -25,19 +24,21 @@
</div>
</div>
-<br>
-<div class="mt-5">
- <h3 class="my-4" text-translate="true">I don't have a wallet</h3>
- <div class="list-group">
- <a asp-controller="UIStores" asp-action="GenerateWallet" asp-route-storeId="@Model.StoreId" asp-route-cryptoCode="@Model.CryptoCode" id="GenerateWalletLink" class="list-group-item list-group-item-action">
- <div class="image">
- <vc:icon symbol="wallet-new"/>
- </div>
- <div class="content">
- <h4 text-translate="true">Create a new wallet</h4>
- <p class="mb-0 text-secondary" text-translate="true">Generate a brand-new wallet to use</p>
- </div>
- <vc:icon symbol="caret-right"/>
- </a>
+@if (Model.CanCreateHotWallet || Model.CanCreateWalletWatchOnly)
+{
+ <br>
+ <div class="mt-5">
+ <div class="list-group">
+ <a asp-controller="UIStores" asp-action="GenerateWallet" asp-route-storeId="@Model.StoreId" asp-route-cryptoCode="@Model.CryptoCode" id="GenerateWalletLink" class="list-group-item list-group-item-action">
+ <div class="image">
+ <vc:icon symbol="wallet-new"/>
+ </div>
+ <div class="content">
+ <h4 text-translate="true">Create a new wallet</h4>
+ <p class="mb-0 text-secondary" text-translate="true">Generate a brand-new wallet to use</p>
+ </div>
+ <vc:icon symbol="caret-right"/>
+ </a>
+ </div>
</div>
-</div>
+}
Why this scored 40/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.