> ## 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.

# Customizing Roles and Permissions

> Add permissions, create custom roles, and understand when permission changes take effect.

## Adding a new permission

**Step 1:** Add the permission constant to `Permissions.cs` in Domain:

```csharp theme={null}
// 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()`:

```csharp theme={null}
// 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:

```csharp theme={null}
[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:

```tsx theme={null}
<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:

```http theme={null}
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:

```http theme={null}
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:

```http theme={null}
DELETE /api/v1/tenants/current/roles/{roleId}
Authorization: Bearer <token>
```

<Warning>
  Deleting a role that has members assigned to it fails with a `CONFLICT` error. Reassign all members to a different role before deleting.
</Warning>

## Permission change propagation

<Warning>
  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.
</Warning>

## 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.
