Authorization (Authz)¶
Zitadel says who a user is; authz says what they can do. Authz is a
ConnectRPC service (platform/authz) that stores every role grant in the acl
table of the environment's database. Nexus calls it on every request that needs
a permission check.
See Dev → Authz → Model for the schema and the evaluation rules. This page is the operator's view.
Roles¶
Defined in nexus (internal/authz/authz.go, RoleSeeds) and written to authz
by SeedRoles on every nexus start.
| Id | Role | Target | Grants |
|---|---|---|---|
| 1 | ORG_OWNER |
portal:org |
Every action on the org: devices, console, audit, managing people |
| 7 | ORG_MEMBER |
portal:org |
Read and write the org's gateways. No console, no audit, cannot grant or revoke |
| 2 | DEVICE_OWNER |
portal:device |
Read, write and transfer one device |
| 3 | PLATFORM_ADMIN |
portal:system |
Every action platform-wide, including the admin pages |
| 4 | SUPPORT |
portal:org or portal:device |
Read, write, console and audit, without being a member |
| 5 | SUPPORT_ADMIN |
as SUPPORT, plus portal:system |
Support, plus device:manage (assigning devices out of the holding zone) |
| 6 | PARTNER |
portal:segment |
Read and write one managed segment, across customers. No console |
Before granting anything:
ORG_OWNERincludes a root console on every device in the org. It is the org wildcard, not a "manage the team" role. GiveORG_MEMBERto people who operate gateways but should not have a shell or manage access.SUPPORT_ADMINis not a platform admin. Platform admin is specifically the wildcard action onportal:system;SUPPORT_ADMINholds onlydevice:managethere and does not see the admin pages.- Support roles are not membership. Only
ORG_OWNERandORG_MEMBERappear in an org's member list. Support grants authorise work on a customer's resources without showing up in that customer's Members tab. PARTNERhas no org or device rows. It is granted per segment withPortalGatewayService.GrantSegmentAccess, by platform admins only; the portal has no screen for it.
Who can grant what¶
Enforced in nexus (requireGrantAuthority), for granting and revoking alike:
| Grant | Who may give or remove it |
|---|---|
Anything on portal:system, and any SUPPORT, SUPPORT_ADMIN or PLATFORM_ADMIN grant |
Platform admins only |
ORG_OWNER / ORG_MEMBER / DEVICE_OWNER on an org |
Platform admins, and holders of the org wildcard (ORG_OWNER) |
| A role on a device | Platform admins, holders of write or transfer on that device, and owners of the org the device belongs to |
Inviting (creating a Zitadel account with PortalUsersService.Invite) is
allowed only to a caller who could then grant something: a platform admin, an
org owner, or a holder of write/transfer on a device. ORG_MEMBER, PARTNER
and users with no grant are refused.
Creating an organization is platform-admin only
(PortalOrgService.CreateOrganization). The org is created with owner system
and no grants; its first owner is invited from the Team page.
Granting and revoking¶
Use the portal: Team for people in an organization, Admin > Organizations for creating one. The step-by-step is in Onboarding a customer from the portal.
The portal calls PortalOrgService.AssignPermission and RevokePermission.
Going through the API also applies validateRoleTarget, which refuses a role
paired with a target it has no actions for (for example DEVICE_OWNER on an
organization). Such a row written directly into acl is stored and listed, and
authorises nothing, because evaluation joins acl to role_actions on
(role_id, target).
The first platform admin¶
The one grant that needs SQL, because nobody can grant it yet. Run it against the environment's database:
-- subject_id is the raw Zitadel sub, not an email or a local user id.
INSERT INTO acl (subject_id, role_id, target, target_id)
VALUES ('<zitadel_sub>', 3, 'portal:system', '*')
ON CONFLICT DO NOTHING;
target_id must be *. After that, further platform admins are granted from
the portal (Team > My access > Add Permission, role
Platform admin).
"No organization yet" on screen¶
The user is signed in and holds no grant. Signing in provisions nothing: there
is no personal organization and no automatic ORG_OWNER. Org access is an acl
row keyed on the raw Zitadel sub; it is not derived from Zitadel's own
organizations or roles. Grant the user a role (see above), then have them sign
out and back in.
Why not Zitadel's own roles?¶
Zitadel's role model (per user × project × org) does not map onto per-device or per-segment grants. Keeping the ACL in the environment's own database lets an authorization decision happen without a round trip to Zitadel on every RPC.