Skip to main content
Version: Latest (v0.0.1)

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.

note

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, and Using o2idctl for how to log in.

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.

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 credentialsusers:read from users:update from users:update-credentials, applications:read from applications: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.