RBAC Model

The Enterprise Stack uses Role-Based Access Control (RBAC) to authorize access to APIs and administrative operations.

Core Concepts

RBAC in the Enterprise Stack is built around four core concepts:

ConceptDescription
PermissionsIndividual operations that can be performed (e.g., issuer-credential-issue, list-keys)
RolesNamed collections of permissions that can be assigned to principals
PrincipalsIdentities such as Accounts, API Keys, or external IAM users
ScopesOrganizational boundaries (organization, tenant, service) where permissions apply
Principal → Role → Permissions → API Access

Permission Scope

Roles are always assigned within a scope. Scopes define where permissions are effective and follow a hierarchical structure.

ScopeDescription
OrganizationAccess across all tenants in an organization
TenantAccess limited to one tenant
Sub-tenantAccess limited to nested tenant structures
ServiceAccess limited to a specific service instance

Hierarchical Inheritance

Permissions propagate top-down through the resource hierarchy:

Organization (waltid)
    │
    ├── Tenant A (waltid.tenantA)
    │   ├── Issuer Service (waltid.tenantA.issuer1)
    │   └── KMS Service (waltid.tenantA.kms1)
    │
    └── Tenant B (waltid.tenantB)
        └── Verifier Service (waltid.tenantB.verifier1)

A permission granted at waltid (organization level) automatically applies to all resources below it. A permission granted at waltid.tenantA applies only to Tenant A and its services.

Allow and Deny Rules

Allow Rules (ADD)

Grant permissions to perform specific operations:

{
  "target": "waltid.tenantA",
  "action": "issuer-credential-issue",
  "operation": "ADD"
}

Deny Rules (REMOVE)

Explicitly revoke permissions, overriding any allow rules:

{
  "target": "waltid.tenantA.issuer1",
  "action": "issuer-credential-issue",
  "operation": "REMOVE"
}

Deny always wins. Even if a user has ALL permissions at the organization level, a deny rule on a specific resource will block access to that operation.

User Types

The Enterprise Stack recognizes different user types with different permission models:

User TypePermission Model
Super AdminBypasses all permission checks (full access)
Regular UserPermissions evaluated via allow/deny trees
Anonymous UserNo permissions granted (public endpoints only)

Example Role Configurations

Organization Administrator

Full access to manage the entire organization:

{
  "name": "Organization Admin",
  "permissions": [
    {
      "target": "waltid",
      "action": "all",
      "operation": "ADD"
    }
  ]
}

Tenant Administrator

Full access within a specific tenant:

{
  "name": "Tenant A Admin",
  "permissions": [
    {
      "target": "waltid.tenantA",
      "action": "all",
      "operation": "ADD"
    }
  ]
}

Issuer Operator

Limited to issuing credentials with a specific issuer service:

{
  "name": "Issuer Operator",
  "permissions": [
    {
      "target": "waltid.tenantA.issuer1",
      "action": "issuer-credential-issue",
      "operation": "ADD"
    },
    {
      "target": "waltid.tenantA.issuer1",
      "action": "issuer-session-view",
      "operation": "ADD"
    }
  ]
}

Restricted Admin

Full access with specific operations blocked:

{
  "name": "Restricted Admin",
  "permissions": [
    {
      "target": "waltid.tenantA",
      "action": "all",
      "operation": "ADD"
    },
    {
      "target": "waltid.tenantA",
      "action": "delete-resource-recursive",
      "operation": "REMOVE"
    }
  ]
}

Auditor (Read-Only)

View-only access for compliance and monitoring:

{
  "name": "Auditor",
  "permissions": [
    {
      "target": "waltid",
      "action": "view-events",
      "operation": "ADD"
    },
    {
      "target": "waltid",
      "action": "view-resource-tree",
      "operation": "ADD"
    }
  ]
}

Authorization Flow Example

Scenario: User requests to issue a credential

  1. Authentication: User authenticates with account credentials
  2. Identity Resolution: System identifies user as user@example.org
  3. Role Loading: System loads assigned roles (e.g., "Issuer Operator")
  4. Permission Check:
    • Required permission: issuer-credential-issue on waltid.tenantA.issuer1
    • Check allow tree: Found match via role grant
    • Check deny tree: No matching deny rule
  5. Result: Request allowed, credential issued

If the user had a deny rule or no matching allow rule, the request would return 403 Forbidden.

Best Practices

  1. Principle of Least Privilege: Grant only the permissions necessary for each role
  2. Use Scopes Appropriately: Assign permissions at the narrowest scope needed
  3. Leverage Deny Rules: Use deny rules to create exceptions in broad permission grants
  4. Regular Audits: Review role assignments and permissions periodically
  5. Separate Duties: Create distinct roles for different responsibilities (issuing vs. verification)
Last updated on May 6, 2026