Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.aspfox.com/llms.txt

Use this file to discover all available pages before exploring further.

Adding a new permission

Step 1: Add the permission constant to Permissions.cs in Domain:
// src/Acme.Domain/Constants/Permissions.cs
public static class Permissions
{
    // existing permissions...
    public const string TenantProjectsRead = "tenant.projects.read";
    public const string TenantProjectsManage = "tenant.projects.manage";
}
Step 2: Add the new permissions to the appropriate built-in roles in GetPermissionsForRole():
// src/Acme.Domain/Services/RolePermissionService.cs
public static IReadOnlyList<string> GetPermissionsForRole(string roleName)
{
    return roleName switch
    {
        RoleNames.Owner => new[]
        {
            // all existing permissions...
            Permissions.TenantProjectsRead,
            Permissions.TenantProjectsManage,
        },
        RoleNames.Admin => new[]
        {
            // existing admin permissions...
            Permissions.TenantProjectsRead,
            Permissions.TenantProjectsManage,
        },
        RoleNames.Member => new[]
        {
            // existing member permissions...
            Permissions.TenantProjectsRead,
            // Members cannot manage projects by default
        },
        _ => Array.Empty<string>()
    };
}
Step 3: Apply the [HasPermission] attribute to controller actions that require the new permission:
[HttpPost]
[HasPermission(Permissions.TenantProjectsManage)]
public async Task<IActionResult> CreateProject([FromBody] CreateProjectRequest request)
{
    // ...
}

[HttpGet]
[HasPermission(Permissions.TenantProjectsRead)]
public async Task<IActionResult> GetProjects()
{
    // ...
}
Step 4: Update the frontend usePermissions() hook usage and command palette to show/hide items based on the new permission:
<PermissionGate permission={Permissions.TenantProjectsManage}>
  <CreateProjectButton />
</PermissionGate>

Creating custom roles via the API

Any tenant member with the tenant.roles.manage permission can create custom roles:
POST /api/v1/tenants/current/roles
Authorization: Bearer <token>

{
  "name": "Developer",
  "permissions": [
    "tenant.settings.read",
    "tenant.members.read",
    "tenant.projects.read",
    "tenant.projects.manage"
  ]
}
Updating a custom role:
PUT /api/v1/tenants/current/roles/{roleId}
Authorization: Bearer <token>

{
  "name": "Senior Developer",
  "permissions": [
    "tenant.settings.read",
    "tenant.members.read",
    "tenant.members.invite",
    "tenant.projects.read",
    "tenant.projects.manage"
  ]
}
Deleting a custom role:
DELETE /api/v1/tenants/current/roles/{roleId}
Authorization: Bearer <token>
Deleting a role that has members assigned to it fails with a CONFLICT error. Reassign all members to a different role before deleting.

Permission change propagation

Permissions are embedded in the JWT access token. If you change a role’s permissions — either by editing GetPermissionsForRole() in code or by updating a custom role via the API — affected users continue using the old permissions until their current access token expires (up to 15 minutes) and they get a new one via refresh.There is no built-in mechanism to immediately revoke access tokens for a specific role change. For permission removals that must take effect immediately, the only option is to force re-authentication for the affected users.

Limitations

  • You cannot use built-in role names (Owner, Admin, Member) for custom roles.
  • A role must have at least one permission.
  • Permission strings must exactly match constants in Permissions.cs. Invalid strings are rejected with a validation error.
  • The Owner role cannot be deleted or modified. It always has all permissions.
  • Each tenant can have at most one Owner.