WakaStart
Authentication

User context — /me payload structure

Complete structure of the /me payload: identity, access levels, teams, application rights and features.

Version v1.05 min read

User context — /me payload structure

/me is your frontend source of truth for everything related to the identity and rights of the current user. Call it once after login, and store the result in a React context.

Why this step matters

The Keycloak JWT contains minimal identity (sub, email, admin levels). But to build a conditional UI (hide/show buttons based on rights, display the correct organization name, etc.), you need the complete payload that only the /me API returns.

/me aggregates data from multiple sources (Keycloak claims, config DB, profiles, teams) into a single call.

Key concepts

  • adminLevel: the user's hierarchical admin level (WakaAdmin → None).
  • appRights: list of resource.action-type application rights accumulated from profiles. Details of the actual shape (profile.rights, {code, name, description, category} objects) in the access control model.
  • features: activation checked in cascade across App ∧ Network ∧ Customer via a dedicated endpoint (see access control model) — not a precomputed field of /me.
  • profiles: WakaStart profiles assigned to the user (determines appRights).
  • teams: teams the user is a member of, with their level and rights within each team.

Call

http
GET /api/me Authorization: Bearer <keycloak_access_token> x-enriched-token: <wakastart_token>

Both headers are required. The response is the same whether the caller uses a Bearer JWT or an API key.


Full response

json
{ "id": "550e8400-e29b-41d4-a716-446655440000", "wid": "WKST05", "email": "john@acme.com", "firstName": "John", "lastName": "Doe", "keycloakId": "kc-550e8400-e29b-41d4-a716-446655440000", "isActive": true, "adminLevel": "CustomerAdmin", "hdsRoles": [], "customer": { "id": "uuid", "wid": "ACM001", "name": "Acme Corp", "subdomain": "acme" }, "partner": { "id": "uuid", "wid": "PTR001", "name": "Wakastellar" }, "network": { "id": "uuid", "wid": "NET001", "name": "Production" }, "teams": [ { "id": "uuid", "wid": "TEAM01", "name": "DevOps", "teamLevel": "ADMIN", "appRights": "rw" } ], "profiles": [ { "id": "uuid", "wid": "PRF001", "name": "Administrateur" } ], "appRights": [ "partners.read", "users.read", "users.write", "users.create", "users.update", "users.delete", "apps.read", "apps.create", "teams.read", "teams.members.add", "infra.read", "infra.deploy", "audit.read", "profiles.assign", "api-keys.read", "api-keys.create", "api-keys.revoke" ], "lastLoginAt": "2026-05-19T10:30:00.000Z", "createdAt": "2026-01-15T08:00:00.000Z" }

Description of each field

Identity

FieldTypeDescription
idUUIDInternal database identifier
widstringShort identifier (6 chars) for URLs
emailstringUser's email (lowercase)
firstName / lastNamestringFirst name / last name
keycloakIdstringKeycloak UUID (JWT sub)
isActivebooleanActive account. If false, enrichment fails

Access levels

FieldTypeDescription
adminLevelstringHierarchical admin level (see table below)

The userLevel field is no longer returned by /me — it has been removed from the payload (MeResponseDto no longer carries it). It is no longer used to gate invitation creation (see Invitations API — Creation); access control relies exclusively on adminLevel and appRights (profiles).

adminLevel hierarchy:

text
WakaAdmin → Full platform access (Wakastellar super-admin) OwnerAdmin → Full platform access (partner owner) AppsAdmin → Applications, profiles, rights NetworkAdmin → Networks, customers, apps CustomerAdmin → Customers, users, teams User → Basic access (own profile) None → No admin access

Hierarchical: a NetworkAdmin also holds the rights of CustomerAdmin and User.

Multi-tenant context

FieldTypeDescription
customerobjectCustomer the user belongs to
partnerobjectParent partner of the customer
networkobjectParent network of the customer

HDS roles

FieldTypeDescription
hdsRolesstring[]HDS (Healthcare Data Security) roles: HDS_ADMIN, HDS_PATIENT, etc.

The wakaRoles field (cross-cutting business roles CONFIG/EXPLOIT/DPO/AUDIT/BILLING/CYBER) has been decommissioned on the platform side — do not rely on it, even if it still appears in older code or documentation.

Teams

appRights within the team context is a free-form field encoded on the team (distinct from the user's global appRights).

Profiles and application rights

  • profiles: assigned profiles. A profile = a set of AppRights in the DB.
  • appRights: deduplicated and merged array of all rights coming from all profiles. Format: {resource}.{action}.
  • features: this is NOT a precomputed field of /me — check cascade activation (App ∧ Network ∧ Customer) via GET /api/config/app-features/check/{customerWid}/{featureCode}. Example feature codes: invitations, antivirus, hds.

What's in /me vs in the JWT

[!WARNING] The enriched JWT (wakaToken) is signed and must fit under the 4 KB cookie limit. Several claims that used to be present have been removed from it and are now available only via /me: hdsRoles, team rights (wgrl), and profiles. The wakaRoles concept itself has been decommissioned (neither in the JWT nor in /me). If your code still reads this information directly from the decoded JWT instead of via /me, it is reading stale or missing values.

DataEnriched JWT (wakaToken)/me (API)
Identity (sub, email, keycloakId, given_name, family_name)yesyes
adminLevel (wadl)yesyes
userLevel (wusl)no (ws-serv-token no longer emits this claim in the wakaToken)no (removed from the /me payload)
Partner/Network/Customer/App WIDs (pid/nid/cid/aid)yesyes (full objects)
User WID (uid)no (absent from the signed JWT)yes
Theme, language, Keycloak org, environment (wthm/wlng/woid/wenv)yes—
hdsRolesnoyes
Team rights (wgrl)noyes (decoded, arrays)
Profilesnoyes
appRights (full list)noyes
lastLoginAtnoyes

The JWT contains identity, access levels and multi-tenant context — enough to route and perform a first coarse check. Everything related to team rights and profiles lives exclusively in /me; feature activation is checked via a dedicated endpoint (see above): call /me systematically before building a conditional UI or a guard based on these fields.


Go further