Add store permission for managing access tokens
What changed, and why it matters
This change adds a new, more specific permission for managing store access tokens in BTCPay Server. Previously, viewing or managing these tokens required only broad 'view store settings' or 'modify store settings' permissions, which meant lower-privileged roles like Guest could see or manage them. Now, a dedicated 'manage store credentials' permission is required, and it is automatically granted to the built-in Manager role. This is a hardening fix that reduces the risk of privilege misuse, but it is not a patch for a known active exploit.
Treat as a security hardening improvement. Review whether any custom store roles besides Manager previously relied on CanModifyStoreSettings to manage tokens and may need explicit CanManageStoreCredentials assignment. Ensure the migration runs successfully in deployments with existing Manager roles. No urgent patching is indicated by the supplied materials.
Security signals we found
Authorization boundary tightening: lower-privileged store roles (e.g., Guest) lose access to token management UI/actions
New granular permission reduces blast radius of 'modify store settings' permission
Database migration retroactively grants new permission to existing Manager role
Integration test added to enforce the new authorization behavior
No evidence of an active vulnerability, CVE, or security advisory in supplied materials
Evidence from the diff
The commit introduces a new policy constant Policies.CanManageStoreCredentials (btcpay.store.canmanagestorecredentials), registers it in policy definitions with includedByPermissions: CanModifyStoreSettings, and applies a database migration that grants it to the built-in ‘Manager’ store role. All legacy BitPay token UI actions (ListTokens, CreateToken, RevokeToken, ShowToken, RequestPairing/Pair) and their Razor views are updated from CanViewStoreSettings/CanModifyStoreSettings to CanManageStoreCredentials. An integration test verifies that Owner and Manager roles can manage tokens, Employee and Guest cannot, and a custom role with only this permission can. Swagger documentation is also updated.
Changed components
BTCPayServer.Client/Permissions.csBTCPayServer/Hosting/BTCPayServerServices.cs (policy registration)BTCPayServer.Data/Migrations/20260928000000_AddCredentialManagementToManagerRole.csBTCPayServer/Plugins/Bitpay/Controllers/UIStoresTokenController.csBTCPayServer/Plugins/Bitpay/Views/ListTokens.cshtmlBTCPayServer/Plugins/Bitpay/Views/NavExtension.cshtmlBTCPayServer/Plugins/Bitpay/Views/RequestPairing.cshtmlBTCPayServer/Plugins/Bitpay/BitpayPlugin.csBTCPayServer/wwwroot/swagger/v1/swagger.template.jsonBTCPayServer.Tests/BitpayTests.csInspect captured patch +101 / −20
### BTCPayServer.Client/Permissions.cs
@@ -14,6 +14,7 @@ public class Policies
public const string CanUseLightningNodeInStore = "btcpay.store.canuselightningnode";
public const string CanModifyServerSettings = "btcpay.server.canmodifyserversettings";
public const string CanModifyStoreSettings = "btcpay.store.canmodifystoresettings";
+ public const string CanManageStoreCredentials = "btcpay.store.canmanagestorecredentials";
public const string CanModifyWebhooks = "btcpay.store.webhooks.canmodifywebhooks";
public const string CanSendStoreEmail = "btcpay.store.cansendstoreemails";
public const string CanModifyStoreSettingsUnscoped = "btcpay.store.canmodifystoresettings:";
### BTCPayServer.Data/Migrations/20260928000000_AddCredentialManagementToManagerRole.cs
@@ -0,0 +1,26 @@
+using BTCPayServer.Data;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace BTCPayServer.Migrations
+{
+ [DbContext(typeof(ApplicationDbContext))]
+ [Migration("20260928000000_AddCredentialManagementToManagerRole")]
+ public partial class AddCredentialManagementToManagerRole : Migration
+ {
+ /// <inheritdoc />
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.Sql("""
+ UPDATE "StoreRoles"
+ SET "Permissions" = COALESCE("Permissions", ARRAY[]::TEXT[]) || ARRAY['btcpay.store.canmanagestorecredentials']::TEXT[]
+ WHERE "Id" = 'Manager'
+ AND "StoreDataId" IS NULL
+ AND NOT (COALESCE("Permissions", ARRAY[]::TEXT[]) @> ARRAY['btcpay.store.canmanagestorecredentials']::TEXT[]);
+ """);
+ }
+ }
+}
### BTCPayServer.Tests/BitpayTests.cs
@@ -6,14 +6,18 @@
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using BTCPayServer.Abstractions.Constants;
+using BTCPayServer.Client;
using BTCPayServer.Client.Models;
using BTCPayServer.Events;
using BTCPayServer.Plugins.Bitpay.Controllers;
using BTCPayServer.Plugins.Bitpay.Models;
using BTCPayServer.Plugins.Bitpay.Security;
using BTCPayServer.Plugins.Bitpay.Views;
+using BTCPayServer.Services.Stores;
using BTCPayServer.Views.Stores;
+using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.DependencyInjection;
using NBitcoin;
using NBitcoin.DataEncoders;
using NBitcoin.Payment;
@@ -51,6 +55,51 @@ public async Task CanUseServerInitiatedPairingCode()
Assert.True(await acc.BitPay.TestAccessAsync(Facade.Merchant));
}
+ [Fact]
+ [Trait("Integration", "Integration")]
+ public async Task AccessTokensRequireStoreCredentialPermission()
+ {
+ using var tester = CreateServerTester();
+ await tester.StartAsync();
+ var owner = tester.NewAccount();
+ await owner.GrantAccessAsync();
+ var storeRepository = tester.PayTester.GetService<StoreRepository>();
+ var credentialsOnly = new StoreRoleId(owner.StoreId, "Credentials only");
+ await storeRepository.AddOrUpdateStoreRole(credentialsOnly, [Policies.CanManageStoreCredentials]);
+
+ async Task<TestAccount> AddMember(StoreRoleId role)
+ {
+ var member = tester.NewAccount();
+ await member.RegisterAsync();
+ Assert.IsType<StoreRepository.AddOrUpdateStoreUserResult.Success>(
+ await storeRepository.AddOrUpdateStoreUser(owner.StoreId, member.UserId, role));
+ return member;
+ }
+
+ async Task<bool> CanManageAccessTokens(TestAccount account)
+ {
+ var controller = account.GetController<UIStoresTokenController>();
+ var authorizationService = controller.HttpContext.RequestServices.GetRequiredService<IAuthorizationService>();
+ return (await authorizationService.AuthorizeAsync(controller.User, owner.StoreId, Policies.CanManageStoreCredentials)).Succeeded;
+ }
+
+ Assert.True(await CanManageAccessTokens(owner));
+ Assert.True(await CanManageAccessTokens(await AddMember(StoreRoleId.Manager)));
+ Assert.True(await CanManageAccessTokens(await AddMember(credentialsOnly)));
+ Assert.False(await CanManageAccessTokens(await AddMember(StoreRoleId.Employee)));
+
+ // Guests can view store settings, but not the store's access tokens.
+ var guest = await AddMember(StoreRoleId.Guest);
+ Assert.False(await CanManageAccessTokens(guest));
+ var guestController = guest.GetController<UIStoresTokenController>();
+ Assert.IsType<RedirectToActionResult>(await guestController.CreateToken());
+ Assert.IsType<ChallengeResult>(await guestController.CreateToken2(new CreateTokenViewModel
+ {
+ Label = "guest",
+ StoreId = owner.StoreId
+ }));
+ }
+
[Fact]
[Trait("Integration", "Integration")]
public async Task CanSendIPN()
### BTCPayServer/Hosting/BTCPayServerServices.cs
@@ -554,6 +554,11 @@ CREATE INDEX IF NOT EXISTS idx_invoices_expired_cleanup
Policies.CanSendStoreEmail,
new PermissionDisplay("Send store emails", "Allows sending emails on behalf of all your stores."),
new PermissionDisplay("Send selected stores' emails", "Allows sending emails on behalf of the selected stores.")),
+ new PolicyDefinition(
+ Policies.CanManageStoreCredentials,
+ new PermissionDisplay("Manage access tokens", "Allows managing the access tokens of all your stores."),
+ new PermissionDisplay("Manage selected stores' access tokens", "Allows managing the access tokens of the selected stores."),
+ includedByPermissions: new[] { Policies.CanModifyStoreSettings }),
new PolicyDefinition(
Policies.CanModifyServerSettings,
new PermissionDisplay("Manage your server", "Grants total control on the server settings of your server."),
### BTCPayServer/Plugins/Bitpay/BitpayPlugin.cs
@@ -41,7 +41,7 @@ public override void Execute(IServiceCollection services)
services.AddStaticSearch(new ActionResultItemViewModel()
{
- RequiredPolicy = Policies.CanViewStoreSettings,
+ RequiredPolicy = Policies.CanManageStoreCredentials,
Title = "View the access tokens (for legacy API access)",
Action = nameof(UIStoresTokenController.ListTokens),
Controller = "UIStoresToken",
### BTCPayServer/Plugins/Bitpay/Controllers/UIStoresTokenController.cs
@@ -24,7 +24,7 @@ namespace BTCPayServer.Plugins.Bitpay.Controllers;
[Route("stores")]
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
-[Authorize(Policy = Policies.CanViewStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
+[Authorize(Policy = Policies.CanManageStoreCredentials, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
[Area(BitpayPlugin.Area)]
public class UIStoresTokenController(
TokenRepository tokenRepository,
@@ -44,7 +44,7 @@ public class UIStoresTokenController(
public bool StoreNotConfigured { get; set; }
public string? GeneratedPairingCode { get; set; }
[HttpGet("{storeId}/tokens")]
- [Authorize(Policy = Policies.CanViewStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
+ [Authorize(Policy = Policies.CanManageStoreCredentials, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
public async Task<IActionResult> ListTokens()
{
var model = new TokensViewModel();
@@ -60,7 +60,7 @@ public async Task<IActionResult> ListTokens()
}
[HttpGet("{storeId}/tokens/{tokenId}/revoke")]
- [Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
+ [Authorize(Policy = Policies.CanManageStoreCredentials, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
public async Task<IActionResult> RevokeToken(string tokenId)
{
var token = await tokenRepository.GetToken(tokenId);
@@ -70,7 +70,7 @@ public async Task<IActionResult> RevokeToken(string tokenId)
}
[HttpPost("{storeId}/tokens/{tokenId}/revoke")]
- [Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
+ [Authorize(Policy = Policies.CanManageStoreCredentials, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
public async Task<IActionResult> RevokeTokenConfirm(string tokenId)
{
var token = await tokenRepository.GetToken(tokenId);
@@ -84,7 +84,7 @@ public async Task<IActionResult> RevokeTokenConfirm(string tokenId)
}
[HttpGet("{storeId}/tokens/{tokenId}")]
- [Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
+ [Authorize(Policy = Policies.CanManageStoreCredentials, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
public async Task<IActionResult> ShowToken(string tokenId)
{
var token = await tokenRepository.GetToken(tokenId);
@@ -94,7 +94,7 @@ public async Task<IActionResult> ShowToken(string tokenId)
}
[HttpGet("{storeId}/tokens/create")]
- [Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
+ [Authorize(Policy = Policies.CanManageStoreCredentials, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
public IActionResult CreateToken(string storeId)
{
var model = new CreateTokenViewModel();
@@ -105,7 +105,7 @@ public IActionResult CreateToken(string storeId)
}
[HttpPost("{storeId}/tokens/create")]
- [Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
+ [Authorize(Policy = Policies.CanManageStoreCredentials, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
public async Task<IActionResult> CreateToken(string storeId, CreateTokenViewModel model)
{
if (!ModelState.IsValid)
@@ -124,7 +124,7 @@ public async Task<IActionResult> CreateToken(string storeId, CreateTokenViewMode
if (store == null)
return Challenge(AuthenticationSchemes.Cookie);
- if (!(await authorizationService.AuthorizeAsync(User, store.Id, Policies.CanModifyStoreSettings)).Succeeded)
+ if (!(await authorizationService.AuthorizeAsync(User, store.Id, Policies.CanManageStoreCredentials)).Succeeded)
return Challenge(AuthenticationSchemes.Cookie);
var tokenRequest = new TokenRequest()
@@ -168,7 +168,7 @@ public async Task<IActionResult> CreateToken()
var model = new CreateTokenViewModel();
ViewBag.HidePublicKey = true;
ViewBag.ShowStores = true;
- var stores = (await storeRepository.GetStoresByUserId(userId)).Where(data => data.HasPolicy(userId, Policies.CanModifyStoreSettings, permissionService)).ToArray();
+ var stores = (await storeRepository.GetStoresByUserId(userId)).Where(data => data.HasPolicy(userId, Policies.CanManageStoreCredentials, permissionService)).ToArray();
model.Stores = new SelectList(stores, nameof(CurrentStore.Id), nameof(CurrentStore.StoreName));
if (!model.Stores.Any())
@@ -209,7 +209,7 @@ public async Task<IActionResult> RequestPairing(string pairingCode, string? sele
return RedirectToAction(nameof(UIHomeController.Index), "UIHome");
}
- var stores = (await storeRepository.GetStoresByUserId(userId)).Where(data => data.HasPolicy(userId, Policies.CanModifyStoreSettings, permissionService)).ToArray();
+ var stores = (await storeRepository.GetStoresByUserId(userId)).Where(data => data.HasPolicy(userId, Policies.CanManageStoreCredentials, permissionService)).ToArray();
return View(new PairingModel
{
Id = pairing.Id,
@@ -225,7 +225,7 @@ public async Task<IActionResult> RequestPairing(string pairingCode, string? sele
}
[HttpPost("/api-access-request")]
- [Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
+ [Authorize(Policy = Policies.CanManageStoreCredentials, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
public async Task<IActionResult> Pair(string pairingCode, string storeId)
{
var store = CurrentStore;
### BTCPayServer/Plugins/Bitpay/Views/ListTokens.cshtml
@@ -12,7 +12,7 @@
<vc:icon symbol="close" />
</button>
<span text-translate="true">Warning: No wallet has been linked to your BTCPay Server Store.</span><br/>
- See <a href="https://docs.btcpayserver.org/Users/#set-up-a-wallet" target="_blank" class="alert-link" rel="noreferrer noopener">this link</a> for more information on how to connect your store and wallet.
+ See <a href="https://docs.btcpayserver.org/WalletSetup/" target="_blank" class="alert-link" rel="noreferrer noopener">this link</a> for more information on how to connect your store and wallet.
</div>
}
<div class="sticky-header">
@@ -35,7 +35,7 @@
<div class="col-xxl-constrain col-xl-8">
<div class="settings-section__heading d-flex align-items-center justify-content-between">
<h3 class="mb-0">@ViewData["Title"]</h3>
- <a id="CreateNewToken" asp-action="CreateToken" class="btn btn-primary" role="button" asp-route-storeId="@Context.GetRouteValue("storeId")" permission="@Policies.CanModifyStoreSettings" text-translate="true">
+ <a id="CreateNewToken" asp-action="CreateToken" class="btn btn-primary" role="button" asp-route-storeId="@Context.GetRouteValue("storeId")" permission="@Policies.CanManageStoreCredentials" text-translate="true">
Create Token
</a>
</div>
@@ -54,15 +54,15 @@
<thead>
<tr>
<th text-translate="true">Label</th>
- <th class="text-end" permission="@Policies.CanModifyStoreSettings" text-translate="true">Actions</th>
+ <th class="text-end" permission="@Policies.CanManageStoreCredentials" text-translate="true">Actions</th>
</tr>
</thead>
<tbody>
@foreach (var token in Model.Tokens)
{
<tr>
<td>@token.Label</td>
- <td class="text-end" permission="@Policies.CanModifyStoreSettings">
+ <td class="text-end" permission="@Policies.CanManageStoreCredentials">
<a asp-action="ShowToken" asp-route-storeId="@Context.GetRouteValue("storeId")" asp-route-tokenId="@token.Id" text-translate="true">See information</a> -
<a asp-action="RevokeToken" asp-route-storeId="@Context.GetRouteValue("storeId")" asp-route-tokenId="@token.Id" data-bs-toggle="modal" data-bs-target="#ConfirmModal" data-description="The access token with the label <strong>@Html.Encode(token.Label)</strong> will be revoked." data-confirm-input="REVOKE" text-translate="true">Revoke</a>
</td>
@@ -82,4 +82,4 @@
</div>
</div>
-<partial name="_Confirm" model="@(new ConfirmModel(StringLocalizer["Revoke access token"], StringLocalizer["The access token will be revoked. Do you wish to continue?"], StringLocalizer["Revoke"]))" permission="@Policies.CanModifyStoreSettings" />
+<partial name="_Confirm" model="@(new ConfirmModel(StringLocalizer["Revoke access token"], StringLocalizer["The access token will be revoked. Do you wish to continue?"], StringLocalizer["Revoke"]))" permission="@Policies.CanManageStoreCredentials" />
### BTCPayServer/Plugins/Bitpay/Views/NavExtension.cshtml
@@ -1,5 +1,5 @@
@using BTCPayServer.Client
@using BTCPayServer.Plugins.Bitpay
-<li class="nav-item nav-item-sub" permission="@Policies.CanViewStoreSettings">
+<li class="nav-item nav-item-sub" permission="@Policies.CanManageStoreCredentials">
<a layout-menu-item="@nameof(StoreNavPages.Tokens)" asp-area="@BitpayPlugin.Area" asp-controller="UIStoresToken" asp-action="ListTokens" asp-route-storeId="@Model.Store.Id" text-translate="true">Access Tokens</a>
</li>
### BTCPayServer/Plugins/Bitpay/Views/RequestPairing.cshtml
@@ -19,7 +19,7 @@
</button>
}
}
-<form asp-action="Pair" method="post" permissioned="@Policies.CanModifyStoreSettings">
+<form asp-action="Pair" method="post" permissioned="@Policies.CanManageStoreCredentials">
<div class="sticky-header">
<vc:title-header />
<button id="page-primary" type="submit" class="btn btn-primary mt-3" title="@StringLocalizer["Approve this pairing demand"]" text-translate="true">Approve</button>
### BTCPayServer/wwwroot/swagger/v1/swagger.template.json
@@ -216,7 +216,7 @@
"securitySchemes": {
"API_Key": {
"type": "apiKey",
- "description": "BTCPay Server supports authenticating and authorizing users through an API Key that is generated by them. Send the API Key as a header value to Authorization with the format: `token {token}`. For a smoother experience, you can generate a url that redirects users to an API key creation screen.\n\n The following permissions are available to the context of the user creating the API Key:\n\n* `btcpay.impersonation.canimpersonate`: Can impersonate users\n* `btcpay.user.candeleteuser`: Delete user\n* `btcpay.user.canmanagenotificationsforuser`: Manage your notifications\n* `btcpay.user.canmodifyprofile`: Manage your profile\n* `btcpay.user.canviewnotificationsforuser`: View your notifications\n* `btcpay.user.canviewprofile`: View your profile\n* `unrestricted`: Unrestricted access\n\nThe following permissions are available if the user is an administrator:\n\n* `btcpay.server.cancreatelightninginvoiceinternalnode`: Create invoices with internal lightning node\n* `btcpay.server.cancreateuser`: Create new users\n* `btcpay.server.canmanageusers`: Manage users\n* `btcpay.server.canmodifyserversettings`: Manage your server\n* `btcpay.server.canuseinternallightningnode`: Use the internal lightning node\n* `btcpay.server.canviewlightninginvoiceinternalnode`: View invoices from internal lightning node\n* `btcpay.server.canviewusers`: View users\n\nThe following permissions applies to all stores of the user, you can limit to a specific store with the following format: `btcpay.store.cancreateinvoice:6HSHAEU4iYWtjxtyRs9KyPjM9GAQp8kw2T9VWbGG1FnZ`:\n\n* `btcpay.store.canarchivepullpayments`: Archive your pull payments\n* `btcpay.store.canbroadcasttransactions`: Broadcast wallet transactions\n* `btcpay.store.cancanceltransactions`: Cancel wallet transactions\n* `btcpay.store.cancreateinvoice`: Create an invoice\n* `btcpay.store.cancreatelightninginvoice`: Create invoices from the lightning nodes associated with your stores\n* `btcpay.store.cancreatenonapprovedpullpayments`: Create non-approved pull payments\n* `btcpay.store.cancreatepullpayments`: Create pull payments\n* `btcpay.store.cancreatetransactions`: Create wallet transactions\n* `btcpay.store.cancreditsubscribers`: Credit your subscribers\n* `btcpay.store.canmanagepayouts`: Manage payouts\n* `btcpay.store.canmanagepullpayments`: Manage your pull payments\n* `btcpay.store.canmanagesubscribers`: Manage your subscribers\n* `btcpay.store.canmanagewallets`: Manage wallets\n* `btcpay.store.canmanagewalletsettings`: Manage wallet settings\n* `btcpay.store.canmanagewallettransactions`: Manage wallet transactions\n* `btcpay.store.canmodifyinvoices`: Modify invoices\n* `btcpay.store.canmodifyofferings`: Modify your offerings\n* `btcpay.store.canmodifypaymentrequests`: Modify your payment requests\n* `btcpay.store.canmodifystoresettings`: Modify your stores\n* `btcpay.store.cansendstoreemails`: Send store emails\n* `btcpay.store.cansigntransactions`: Sign wallet transactions\n* `btcpay.store.canuselightningnode`: Use the lightning nodes associated with your stores\n* `btcpay.store.canviewinvoices`: View invoices\n* `btcpay.store.canviewlightninginvoice`: View the lightning invoices associated with your stores\n* `btcpay.store.canviewofferings`: View your offerings\n* `btcpay.store.canviewpaymentrequests`: View your payment requests\n* `btcpay.store.canviewpayouts`: View payouts\n* `btcpay.store.canviewpullpayments`: View your pull payments\n* `btcpay.store.canviewreports`: View your reports\n* `btcpay.store.canviewstoresettings`: View your stores\n* `btcpay.store.canviewwallet`: View wallets\n* `btcpay.store.webhooks.canmodifywebhooks`: Modify stores webhooks\n\nNote that API Keys only limits permission of a user and can never expand it. If an API Key has the permission `btcpay.server.canmodifyserversettings` but that the user account creating this API Key is not administrator, the API Key will not be able to modify the server settings.\nSome permissions may include other permissions, see [this operation](#operation/permissionsMetadata).\n",
+ "description": "BTCPay Server supports authenticating and authorizing users through an API Key that is generated by them. Send the API Key as a header value to Authorization with the format: `token {token}`. For a smoother experience, you can generate a url that redirects users to an API key creation screen.\n\n The following permissions are available to the context of the user creating the API Key:\n\n* `btcpay.impersonation.canimpersonate`: Can impersonate users\n* `btcpay.user.candeleteuser`: Delete user\n* `btcpay.user.canmanagenotificationsforuser`: Manage your notifications\n* `btcpay.user.canmodifyprofile`: Manage your profile\n* `btcpay.user.canviewnotificationsforuser`: View your notifications\n* `btcpay.user.canviewprofile`: View your profile\n* `unrestricted`: Unrestricted access\n\nThe following permissions are available if the user is an administrator:\n\n* `btcpay.server.cancreatelightninginvoiceinternalnode`: Create invoices with internal lightning node\n* `btcpay.server.cancreateuser`: Create new users\n* `btcpay.server.canmanageusers`: Manage users\n* `btcpay.server.canmodifyserversettings`: Manage your server\n* `btcpay.server.canuseinternallightningnode`: Use the internal lightning node\n* `btcpay.server.canviewlightninginvoiceinternalnode`: View invoices from internal lightning node\n* `btcpay.server.canviewusers`: View users\n\nThe following permissions applies to all stores of the user, you can limit to a specific store with the following format: `btcpay.store.cancreateinvoice:6HSHAEU4iYWtjxtyRs9KyPjM9GAQp8kw2T9VWbGG1FnZ`:\n\n* `btcpay.store.canarchivepullpayments`: Archive your pull payments\n* `btcpay.store.canbroadcasttransactions`: Broadcast wallet transactions\n* `btcpay.store.cancanceltransactions`: Cancel wallet transactions\n* `btcpay.store.cancreateinvoice`: Create an invoice\n* `btcpay.store.cancreatelightninginvoice`: Create invoices from the lightning nodes associated with your stores\n* `btcpay.store.cancreatenonapprovedpullpayments`: Create non-approved pull payments\n* `btcpay.store.cancreatepullpayments`: Create pull payments\n* `btcpay.store.cancreatetransactions`: Create wallet transactions\n* `btcpay.store.cancreditsubscribers`: Credit your subscribers\n* `btcpay.store.canmanagepayouts`: Manage payouts\n* `btcpay.store.canmanagepullpayments`: Manage your pull payments\n* `btcpay.store.canmanagestorecredentials`: Manage access tokens\n* `btcpay.store.canmanagesubscribers`: Manage your subscribers\n* `btcpay.store.canmanagewallets`: Manage wallets\n* `btcpay.store.canmanagewalletsettings`: Manage wallet settings\n* `btcpay.store.canmanagewallettransactions`: Manage wallet transactions\n* `btcpay.store.canmodifyinvoices`: Modify invoices\n* `btcpay.store.canmodifyofferings`: Modify your offerings\n* `btcpay.store.canmodifypaymentrequests`: Modify your payment requests\n* `btcpay.store.canmodifystoresettings`: Modify your stores\n* `btcpay.store.cansendstoreemails`: Send store emails\n* `btcpay.store.cansigntransactions`: Sign wallet transactions\n* `btcpay.store.canuselightningnode`: Use the lightning nodes associated with your stores\n* `btcpay.store.canviewinvoices`: View invoices\n* `btcpay.store.canviewlightninginvoice`: View the lightning invoices associated with your stores\n* `btcpay.store.canviewofferings`: View your offerings\n* `btcpay.store.canviewpaymentrequests`: View your payment requests\n* `btcpay.store.canviewpayouts`: View payouts\n* `btcpay.store.canviewpullpayments`: View your pull payments\n* `btcpay.store.canviewreports`: View your reports\n* `btcpay.store.canviewstoresettings`: View your stores\n* `btcpay.store.canviewwallet`: View wallets\n* `btcpay.store.webhooks.canmodifywebhooks`: Modify stores webhooks\n\nNote that API Keys only limits permission of a user and can never expand it. If an API Key has the permission `btcpay.server.canmodifyserversettings` but that the user account creating this API Key is not administrator, the API Key will not be able to modify the server settings.\nSome permissions may include other permissions, see [this operation](#operation/permissionsMetadata).\n",
"name": "Authorization",
"in": "header"
},Why this scored 51/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.