Role-Based Access Control (RBAC v2)
ProAuth 3.x introduces RBAC v2, a uniform role-and-scope authorization model that supersedes the legacy V1 ProAuthSecurityRole enum. RBAC v2 is enforced consistently across the V2 Management and UserStore APIs and across runtime token issuance.
Concepts
Permissions
A permission is a tuple (resource, action) describing one atomic capability — e.g. tenant:read, proauthuser:create. The full catalog is defined in ProAuthPermissionCatalog and exposed via the /api/management/v2/Permission endpoint.
Roles
A role is a named bundle of permissions. ProAuth ships with eight system-defined roles that cannot be renamed, modified, or deleted:
| Role name | Purpose |
|---|---|
system.admin | Full administrative access across all customers. |
system.customer-admin | Full administration within a single customer. |
system.subscription-admin | Full administration within one subscription. |
system.tenant-admin | Full administration within one tenant. |
system.userstore-admin | Manage one IdpInstance / UserStore (users, groups, ...). |
system.user-self-service | Self-service: caller may manage only their own ProAuthUser. |
system.application-data-reader | Read-only access to client application data. |
system.audit-trail-reader | Read-only access to audit-trail entries. |
You may also define custom roles via POST /api/management/v2/ProAuthRole. Names that begin with system. are reserved.
Scope grammar
Every role assignment carries a scope — the slice of the platform hierarchy in which the role applies. The grammar is:
/ (global)
/customer:{guid}
[/subscription:{guid}
[/tenant:{guid} | /idpinstance:{guid} | /clientapp:{guid}
[/group:{guid} | /user:{guid}]]]GUIDs must be canonical lowercase 8-4-4-4-12. Segment types must not duplicate at the same level and must follow hierarchy order. Invalid scopes are rejected at the V2 API boundary with HTTP 400 Bad Request.
A scope covers another scope when its path is a strict prefix; e.g. a system.subscription-admin granted at /customer:c1/subscription:s1 automatically governs every tenant under that subscription.
Role assignments
ProAuthRoleAssignment binds a principal (User, Group, or ServicePrincipal) to a role at a specific scope. Assignments are managed through /api/management/v2/ProAuthRoleAssignment. They are immutable: re-scoping is expressed as delete + recreate.
Cascade-blocked deletes
Deleting an aggregate that is referenced by an active role assignment is refused with HTTP 409 Conflict and code cascade_blocked. The following roots are guarded:
SubscriptionTenantProAuthGroupProAuthUser
Remove or re-target the dependent assignments first.
Custom roles — guidance
- Pick a stable, human-readable name (e.g.
acme.support-readonly). - Compose using the smallest set of permissions that meets the business need; prefer read-only permissions where possible.
- Assign at the narrowest scope that still grants the user the access they require — avoid granting at
/unless absolutely necessary. - Treat assignments as auditable infrastructure: changes are recorded in the audit trail and propagate cluster-wide within the cache TTL (see RBAC cache invalidation).
Permission-based v2 administration
Management v2 evaluates resource/action permissions for administrative endpoints. A role name alone does not grant authority. Custom roles can group the same permissions as built-in roles without changing client metadata.
Role, permission and assignment administration requires the corresponding ProAuthRole, ProAuthPermission, ProAuthRolePermission or ProAuthRoleAssignment CRUD permission at global scope /. A subscription-scoped assignment of those permissions does not grant administration rights. This is global access administration: an authorized assignment administrator may delegate permissions they do not personally hold. System-defined roles remain immutable.
Operations without an entity on which to enforce conditions require unconditional global grants:
| Operation | Permission |
|---|---|
| Revoke tokens | Token.Revoke |
| Delete tokens | Token.Delete |
| Prune tokens | Token.Prune |
| Product/license information | ProductInformation.Read |
| Type and option metadata catalogs | IdpType.Read, MfaType.Read, OptionMetadata.Read respectively |
| Label or view-definition administration | Corresponding resource CRUD permission; import needs Create and Update |
| Replace a view-definition deployment set | ViewDefinitionDeployment.Replace |
| Inspect effective permissions | Read on ProAuthRoleAssignment, ProAuthRole, ProAuthPermission and ProAuthRolePermission |
Operations on managed records enforce both assignment scope and permission conditions. Configure conditions on the role-to-permission link. Global RBAC administration still honors those conditions; restrictions on protected records and relationships also apply.
The AdminApp uses permission presence to improve navigation and editing usability. A scoped or conditional ClientApp.Read grant makes the Client Apps view eligible; selecting an unrelated subscription can correctly return no rows. The API enforces the selected data's scope and conditions. Public operation metadata contains distinct resource/action requirements, not role names, scope values or policy internals.
Managing custom role permissions (Management API v2)
/api/management/v2/ProAuthRolePermission manages the links between custom roles and permissions. It provides the standard list, get, create, update, patch, delete and batch-create operations. Existing role, permission and assignment endpoints keep their contracts.
A caller needs the matching global ProAuthRolePermission.Read/Create/Update/Delete permission and global read access to the referenced roles and permissions. Backend entity restrictions still apply. A scoped assignment cannot grant global RBAC administration. Platform-defined role membership is immutable, including through batch creation and PATCH operations.
Permission definitions referenced by platform-defined roles are also protected against update and deletion. Create a separate permission definition for custom roles when different behavior is needed.
For example, after creating a custom role and resolving the Customer.Read permission, attach it with:
{
"proAuthRoleId": "<custom-role-id>",
"proAuthPermissionId": "<customer-read-permission-id>",
"conditionType": "UnconditionalAccess"
}Assign the role to a principal through ProAuthRoleAssignment, with the desired scope. The scope belongs to that assignment, not to the reusable permission link. UI navigation sees the presence of Customer.Read; API reads still enforce its assignment scope.
Optional condition types are FieldEqualsCondition and FieldInListCondition. Their conditionParameters string contains JSON with FieldPath and either Value (a string) or Values (an array of strings). For example, {"FieldPath":"Name","Value":"Example"} restricts the grant to matching entities. Unknown condition types, malformed parameters and extra properties are rejected; invalid persisted restrictions do not become unconditional grants. Conditions use the existing backend expression evaluator.
Update or patch changes the condition. Role and permission IDs cannot be changed on an existing link; explicitly detach and attach a new link instead. Permission definition and membership changes invalidate the shared RBAC caches across instances through the existing entity-change events. PUT, PATCH and DELETE require a concurrency precondition. Tokenless inherited batch update/delete endpoints are not exposed; clients perform conditional individual writes. SQL Server and PostgreSQL schema migrations add and backfill link tokens for existing installations.