Merge pull request #7612 from minmoto/okjodom/store-credentials-permission
What changed, and why it matters
This change introduces a new, more specific permission called 'Manage access tokens' for store-level access token operations in BTCPay Server. Previously, viewing tokens required the broad 'View store settings' permission and creating or revoking tokens required the even broader 'Modify store settings' permission. The patch narrows those checks so that token management now uses its own dedicated permission. The new permission is automatically granted to the built-in 'Manager' role, and it is included by the existing 'Modify store settings' permission, so users who already had full store control keep the same access. This is a security hardening change that reduces the risk of lower-privileged users (for example, guests or employees) being able to create or revoke API access tokens if they had been accidentally given overly broad store settings rights.
No immediate action is required; this is a hardening patch. Operators should review custom store roles after upgrading to ensure they grant `btcpay.store.canmanagestorecredentials` where token management is intended, because only the built-in Manager role is automatically updated. Developers should confirm that no other token-related endpoints or API paths still rely on the broader store-settings permission.
Security signals we found
New fine-grained authorization policy introduced for sensitive credential-management operations
Controller actions and views switched from CanViewStoreSettings/CanModifyStoreSettings to CanManageStoreCredentials
Migration auto-grants new permission to the built-in Manager role to preserve expected access
Integration tests verify that lower-privileged roles (Employee, Guest) are denied token management
Policy is documented in swagger template
Evidence from the diff
The commit adds a new policy constant Policies.CanManageStoreCredentials (‘btcpay.store.canmanagestorecredentials’) and wires it into the BitPay/legacy API token UI controller (UIStoresTokenController), the plugin’s static search entry, and the related Razor views (ListTokens.cshtml, NavExtension.cshtml, RequestPairing.cshtml). A new EF migration grants the permission to the global ‘Manager’ store role. In BTCPayServerServices.cs the policy is registered with includedByPermissions: Policies.CanModifyStoreSettings, meaning anyone with modify-store-settings still implicitly has it. Tests assert that owners, managers, and a custom ‘Credentials only’ role can manage tokens, while employees and guests cannot. The swagger template is also updated to document the new permission.
Changed components
BTCPayServer.Client/Permissions.csBTCPayServer/Hosting/BTCPayServerServices.csBTCPayServer/Plugins/Bitpay/BitpayPlugin.csBTCPayServer/Plugins/Bitpay/Controllers/UIStoresTokenController.csBTCPayServer/Plugins/Bitpay/Views/ListTokens.cshtmlBTCPayServer/Plugins/Bitpay/Views/NavExtension.cshtmlBTCPayServer/Plugins/Bitpay/Views/RequestPairing.cshtmlBTCPayServer.Data/Migrations/20260928000000_AddCredentialManagementToManagerRole.csBTCPayServer.Tests/BitpayTests.csBTCPayServer/wwwroot/swagger/v1/swagger.template.jsonInspect 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.