Access control — Model & Cascade
Wakastart's 2 rights vectors: adminLevel and appRights (resource.action rights), and enabled-feature checks.
Access control — Model & Cascade
Access control is two-sided: the backend always blocks (source of authority), the frontend hides (UX). Never rely on the frontend alone to secure an action.
Key concepts
- adminLevel: hierarchical level, determines the scope of CRUD operations.
- appRights: fine-grained rights on specific resources (
users.create,infra.deploy…), derived from profiles. The corresponding JWT claim is calledroles(formatresource.action); on the/meside, they appear underprofile.rights(an array of{code, name, description, category}objects, not a plain array of strings). - features: cascade activation across App ∧ Network ∧ Customer. If a feature is disabled at any level, it is unavailable.
- Profile: a set of
AppRights assigned to a user. A user can have several profiles.
A former 3rd vector,
wakaRoles(cross-cutting business roles such as CONFIG/EXPLOIT/DPO/AUDIT/BILLING/CYBER), has been decommissioned on the platform side. Rights checks now rely solely onadminLeveland theresource.actioncodes coming from profiles — ignore any mention ofwakaRolesin older code or documentation.
The 2 rights vectors
1. adminLevel — hierarchical scope
Controls who can see and modify what based on their position in the multi-tenant hierarchy.
textWakaAdmin → Everything (whole platform, all Partners) OwnerAdmin → Everything (Partner owner) AppsAdmin → Applications + Profiles + Rights (within their Network) NetworkAdmin → Networks + Customers + Apps (within their Network) CustomerAdmin → Customers + Users + Teams (within their Customer) User → Their own profile only None → No admin access
Hierarchy rule: a NetworkAdmin implicitly holds the rights of CustomerAdmin and User. The backend check uses an ordinal comparison.
2. appRights — fine-grained application rights
Rights of type {resource}.{action} accumulated from the user's profiles.
Format: resource.action or resource.sub-resource.action
Examples:
textpartners.read users.create infra.deploy partners.write users.update infra.services.scale apps.read users.delete audit.read apps.create teams.read billing.manage apps.delete teams.members.add api-keys.read profiles.read teams.members.remove api-keys.create profiles.assign invitations.create api-keys.revoke profiles.rights.write invitations.read media.upload
Feature cascade
A feature must be enabled at three levels to be available:
Loading diagram…
The /me payload does not expose a precomputed features field. Check whether a feature is enabled via the dedicated endpoint: GET /api/config/app-features/check/{customerWid}/{featureCode}.
Concrete example:
typescript// If the "invitations" feature is disabled at any level → hide the entire module const { data } = await api.get(`/api/config/app-features/check/${customerWid}/invitations`); const hasInvitations = data.enabled;
Combined check via /token/authorize
For complex checks (admin level + role + right) in a single call:
httpPOST /api/token/authorize x-api-key: sk_live_... Content-Type: application/json { "token": "eyJ...", "checks": { "adminLevel": { "requiredLevel": "CustomerAdmin" }, "roles": { "roles": ["audit.read", "billing.manage"], "mode": "any" }, "appRights": { "rights": ["users.create", "users.update"], "mode": "all" } } }
json{ "authorized": true, "details": { "adminLevel": { "hasLevel": true, "actualLevel": "NetworkAdmin" }, "roles": { "hasRoles": true, "matchedRoles": ["audit.read"] }, "appRights": { "hasRights": true, "missingRights": [] } } }
Best practices
- Principle of least privilege: only grant the rights strictly necessary in profiles.
- Backend = authority: always verify rights on the server side. The frontend only hides.
- Fine granularity: prefer
users.createoverusers.*— wildcards do not exist in the model. - Features first: before checking a right, check whether the feature is enabled.
Common pitfalls
- Confusing three neighboring concepts:
adminLevel(ADMIN,TEAM,INVITE,USER,EXTERN,NONE) is the administrative scope over the hierarchy;teamLevel(ADMIN,MANAGER,MEMBER,CONTRIBUTOR,VIEWER,NONE) is the role within a specific team —MEMBERbelongs toteamLevel, notadminLevel. - Empty
appRights= no access: a user with no profile assigned hasappRights: []and sees nothing. - Disabled feature → 403: if
GET /api/config/app-features/check/{customerWid}/{featureCode}returnsenabled: falseand your code calls the API anyway, you will get a403 Feature disabled. - Cumulative rights: if a user has two profiles, their
appRightsare the union of both.
Go further
- Guards & hooks implementation: full frontend and backend code
- Feature flags and cascade: AppFeature, NetworkAppFeature, CustomerAppFeature
- /me user context: source of rights