Users
A user is a human identity — the account someone signs into an application with, authenticating through the login portal along the way. A user belongs to exactly one tenant.
Managing users requires a role with the users:read/users:create/
users:update/users:update-credentials/users:delete scopes — see
Using o2idctl for how to log in, and Managing Roles
for how to create and assign roles.
A freshly created user has no RBAC permissions of its own — creating a user only establishes who they are, not what they can do. Assign a role to grant them access — a role's scopes aren't limited to O2ID's own management API; a role can just as well carry custom scopes for your own APIs (see Resource Servers).
User types
A user belongs to a user type, which defines its schema — what
attributes exist, which are required, and which one is the login
identifier. Every tenant has a built-in user type (key user) whose
login field is username, with email as an optional attribute; it
can't be deleted, and it's what a user gets when --user-type is omitted
on users create. Define a custom type when you need different or
additional attributes — for example, logging in by email instead of a
separate username, or requiring a department on every user.
Creating a user type
A schema is a list of fields, each declared with a repeatable --field key:type[:required][:opt1,opt2] flag: type is one of string,
number, bool, select, email, or username; required marks the
field mandatory; and the comma-separated list at the end is only used by
select, to declare its allowed values. Exactly one field must use the
username type and be required — --username-field names which one, and
that field is what O2ID resolves as the login identifier for users of this
type. A field named email must be of type email.
o2idctl user-types create --key employee --username-field email \
--field email:email:required --field department:string
This creates an employee type where a user logs in with their email
address (declared --username-field email), and may optionally set a
department attribute.
Listing and inspecting user types
list includes the built-in user type alongside any custom ones;
<key> in get is the type's own key (e.g. employee), not a generated
ID:
o2idctl user-types list
o2idctl user-types get <key>
Updating a user type
--field, if passed at all, replaces the entire schema rather than
adding to it — a call that omits the username field fails validation, so
re-list every field you want to keep, not just the one you're adding or
changing. --username-field can't be changed once a type is created.
o2idctl user-types update employee \
--field email:email:required --field department:string:required
This keeps the existing email login field as-is and makes department
required going forward — existing users of the type aren't retroactively
validated against the new schema.
Deleting a user type
Deletion is rejected if any user is still assigned this type — move or
recreate those users under a different type first. The built-in user
type can never be deleted:
o2idctl user-types delete <key>
Creating a user
A user's fields are set through repeatable --attribute key=value flags
rather than fixed flags like --email — which attributes are valid, and
which are required, comes from the user's type. --user-type selects a
non-default type; --display-name is optional and purely for display, and
doesn't affect authentication:
o2idctl users create --attribute username=alice \
--attribute email=alice@example.com --display-name Alice --password secret
For the built-in type, username is required and unique within the
tenant, and email is optional.
To create a user of a custom type instead, pass --user-type with that
type's key and attributes matching its schema:
o2idctl users create --user-type employee \
--attribute email=alice@example.com --attribute department=engineering \
--display-name Alice --password secret
--user-type defaults to the built-in user type when omitted.
Listing users
Both list and get omit the password hash entirely from their
responses — it's never serialized over the API:
o2idctl users list
Inspecting a user
<id> here is the user's internal id (from create or list), not
their username:
o2idctl users get <id>
Updating a user's profile
--display-name and --user-type are independently optional — omit
either to leave it unchanged:
o2idctl users update <id> --display-name "Alice Updated"
--attribute, if passed at all, replaces the entire attribute set
rather than merging one field into it — include every attribute you want
to keep, not just the one you're changing. This also means a user's login
identifier (the username field) can be changed this way, subject to
staying unique within the tenant:
o2idctl users update <id> --attribute username=alice \
--attribute email=alice@newdomain.com
Setting a user's password
Password changes go through a separate, dedicated endpoint rather than the general profile update above:
o2idctl users set-password <id> --password new-password
This is a deliberate split: users:update (display name, and anything
else non-sensitive added to the profile later) and
users:update-credentials (the password itself) are two different scopes,
so you can grant a support role the ability to fix a user's display name
without also handing it the power to take over their account by resetting
their password. Grant users:update-credentials narrowly.
A password change takes effect immediately — the very next login must use the new password — but it does not revoke anything already issued: existing sessions and access/refresh tokens keep working until they expire on their own. If a credential is compromised, changing the password stops further logins with the old one, but doesn't retroactively invalidate a live session an attacker may already hold.
Passing an empty --password here (rather than omitting the flag) turns
the user passwordless, clearing any previously-set password.
Deleting a user
o2idctl users delete <id>
Deletion is immediate and has no undo. It removes the user row itself, but doesn't cascade to everything that ever referenced it — existing sessions, access/refresh tokens, and consent grants aren't retroactively revoked, the same way deleting an application doesn't revoke tokens already issued through it: token validation (introspection) checks the token store, not that the user still exists, so they remain valid until they expire naturally.
See the CLI Reference for the full users command
list, and the API Reference for the underlying /users API.