Roles
A role is a named set of scopes — fine-grained permissions, such as
applications:read, users:update, or a custom scope defined on one of
your own resources. A user can hold
multiple roles at once; their effective permissions are the union of
scopes across every role they're assigned.
A user's role scopes are only half of what determines a token's actual permissions, though: the application they log in through also has its own allowed scopes, and a token can never carry more than the intersection of the two.
Managing roles requires a role with the roles:read/roles:create/
roles:update/roles:delete/roles:assign scopes — see the
API Reference for the scope each endpoint requires.
Creating a role
o2idctl roles create --name Support --scope applications:read
Repeat --scope to grant more than one scope. A couple of common starting
points:
# Read-only auditor: can inspect everything, change nothing
o2idctl roles create --name Auditor \
--scope users:read --scope applications:read --scope roles:read \
--scope agents:read --scope mandates:read --scope resourceservers:read
# Helpdesk: can look up and fix user profiles, but not reset passwords,
# assign roles, or touch anything else
o2idctl roles create --name Helpdesk --scope users:read --scope users:update
Scopes can be mixed and matched freely, including ones defined on your own resources:
o2idctl roles create --name "Support Agent" \
--scope users:read --scope read:messages --scope write:messages
See the API Reference — each endpoint's description names the scope it requires.
Listing and inspecting roles
o2idctl roles list
o2idctl roles get <roleId>
Updating a role
o2idctl roles update <roleId> --name "Support Updated" --scope applications:read --scope users:read
--name and --scope are both optional — omit --name to keep the current
name, and omit --scope entirely to keep the current scopes. Passing
--scope at all replaces the full scope list; it isn't merged with the
existing one — repeat --scope for every scope the role should hold after
the update, not just the ones you're adding.
Scope changes take effect on different schedules
Revoking a scope and granting one behave asymmetrically, because of how access tokens are checked: every management-API request is validated both against the scope baked into the caller's access token and a live lookup of the user's current role scopes.
- Removing a scope from a role takes effect immediately — the very
next request from anyone holding that role is rejected with
insufficient scope, even if they're using a token minted minutes ago. - Adding a scope to a role does not retroactively widen an already-issued token — the token's own baked-in scope was fixed at login time. The user must get a new token (typically by logging in again) before they can actually exercise the newly granted scope.
In short: assume a revocation is instant, but a grant requires a fresh login to take effect.
Assigning and unassigning roles
o2idctl users assign --user <userId> --role <roleId>
o2idctl users unassign --user <userId> --role <roleId>
Assigning a role a user already holds, or unassigning one they don't, is a no-op rather than an error. See Managing Users for everything else about the user side of this relationship.
You can never grant a scope you don't hold
Assigning a role requires the caller to already hold every scope that role
grants — including to themselves. A user holding only roles:assign cannot
assign a role carrying * (or any other scope they lack), whether the
target is another user or themselves; the request fails with 403 Forbidden. Only a caller who already holds * can assign a role that
includes it. The same check applies to updating a role:
adding a scope to a role requires the caller to already hold that scope,
though removing scopes is always allowed regardless of what the caller
holds.
Deleting a role
o2idctl roles delete <roleId>
A role can't be deleted while it's still assigned to any user — unassign it from every user first. This also means an admin can never accidentally delete the role granting their own access.
Designing roles: least privilege
Because authorization is scope-based, role design is worth being deliberate about:
- Start from zero, add scopes as needed — a new role has no scopes until you grant some; there's no implicit baseline access.
- Split read from write, and profile from credentials —
users:readfromusers:updatefromusers:update-credentials,applications:readfromapplications:delete, and so on. A role that only ever needs to look things up shouldn't also be able to change them. - Reserve
*for genuine full-access roles — a role scoped to everything today is easy to build with an explicit list, but*also grants everything added tomorrow. That's exactly right for a platform operator, and usually wrong for anything narrower. - Remember the application-side ceiling — a role granting broad access
is still capped by whichever application
the user logs in through. Don't compensate for an over-permissioned
application by under-permissioning every role instead; fix the
application's
allowedScopes.
See the CLI Reference for the full roles command
list, and the API Reference for the underlying /roles API.