WakaStart
Security

Access control — Model & Cascade

Wakastart's 2 rights vectors: adminLevel and appRights (resource.action rights), and enabled-feature checks.

Version v1.03 min read

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 called roles (format resource.action); on the /me side, they appear under profile.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 on adminLevel and the resource.action codes coming from profiles — ignore any mention of wakaRoles in 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.

text
WakaAdmin → 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:

text
partners.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:

http
POST /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.create over users.* — 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 — MEMBER belongs to teamLevel, not adminLevel.
  • Empty appRights = no access: a user with no profile assigned has appRights: [] and sees nothing.
  • Disabled feature → 403: if GET /api/config/app-features/check/{customerWid}/{featureCode} returns enabled: false and your code calls the API anyway, you will get a 403 Feature disabled.
  • Cumulative rights: if a user has two profiles, their appRights are the union of both.

Go further