Skip to main content
Version: Next

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.

note

Managing users requires a role with the users:read/users:create/ users:update/users:update-credentials/users:delete scopes — see 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.