Users
Manage user accounts, authentication, and role assignments in Mantis.
Overview
Section titled “Overview”User Model
Section titled “User Model”| Field | Type | Description |
|---|---|---|
id | UUID | Unique identifier |
email | String | Email address (unique platform-wide, case-insensitively) |
username | String | Display username (unique platform-wide, case-insensitively) |
password_hash | String | Argon2id hash (local auth only) |
display_name | String | Optional display name |
status | Enum | active, inactive, locked, pending_verification, pending_approval |
email_verified | Boolean | Email verification status |
tenant_id | UUID | Optional tenant scope |
last_login_at | DateTime | Last successful login |
created_at | DateTime | Account creation time |
updated_at | DateTime | Last update time (nullable) |
User Status
Section titled “User Status”| Status | Description |
|---|---|
active | Account is active and can log in |
inactive | Account is inactive (disabled by admin) |
locked | Account is locked (too many failed logins) |
pending_verification | Invited; waiting on the user to activate |
pending_approval | Provisioned by an identity provider, awaiting approval |
Only active can sign in.
API Operations
Section titled “API Operations”List Users
Section titled “List Users”curl -X GET \ -H "Authorization: Bearer $TOKEN" \ "https://api.mantis.local/api/v1/admin/users?page=1&per_page=20"Response:
{ "data": [ { "id": "019b937d-4862-8e07-ac3a-1b8589d1b807", "email": "alice@example.com", "username": "alice", "display_name": "Alice Smith", "status": "active", "email_verified": true, "role_count": 2, "tenant_id": null, "last_login_at": "2024-01-15T10:30:00Z", "created_at": "2024-01-01T08:00:00Z" } ], "meta": { "total": 50, "page": 1, "per_page": 20, "total_pages": 3, "has_next": true, "has_prev": false }}Get User Details
Section titled “Get User Details”curl -X GET \ -H "Authorization: Bearer $TOKEN" \ "https://api.mantis.local/api/v1/admin/users/$USER_ID"Response includes roles, permissions, and external identities:
{ "id": "019b937d-4862-8e07-ac3a-1b8589d1b807", "email": "alice@example.com", "username": "alice", "display_name": "Alice Smith", "status": "active", "email_verified": true, "roles": [ { "id": "019b937d-4862-8e07-ac3a-1b8589d1b807", "name": "operator", "description": "Can manage deployments and targets", "is_system": true } ], "permissions": [ "deployments:create", "deployments:read", "targets:create", "targets:read" ], "tenant_id": null, "tenant_name": null, "external_identities": [ { "provider_id": "019b937d-4862-8def-4567-001122334455", "provider_name": "Okta", "external_username": "alice.smith", "external_email": "alice.smith@company.com", "last_login_at": "2024-01-15T10:30:00Z" } ], "last_login_at": "2024-01-15T10:30:00Z", "created_at": "2024-01-01T08:00:00Z", "updated_at": "2024-01-10T14:00:00Z"}Create User
Section titled “Create User”Creating a user sends an invitation. The account is created pending_verification with
no usable password — the invitee sets their own through the emailed link, so an
administrator never handles it. The request carries no password:
curl -X POST \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "email": "bob@example.com", "username": "bob", "display_name": "Bob Jones", "tenant_id": null, "role_ids": ["019b937d-4862-84ae-9c2a-6ac230cc4c7e"] }' \ "https://api.mantis.local/api/v1/admin/users"The invitation needs a working platform mailer — see
Platform email. Links expire after 7 days;
POST /api/v1/admin/users/{id}/resend-invite issues a fresh one and invalidates the
previous link.
Update User
Section titled “Update User”curl -X PUT \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "display_name": "Alice S.", "status": "active", "email_verified": true }' \ "https://api.mantis.local/api/v1/admin/users/$USER_ID"Delete User
Section titled “Delete User”curl -X DELETE \ -H "Authorization: Bearer $TOKEN" \ "https://api.mantis.local/api/v1/admin/users/$USER_ID"Reset Password
Section titled “Reset Password”curl -X POST \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "new_password": "NewSecurePassword456!" }' \ "https://api.mantis.local/api/v1/admin/users/$USER_ID/password"An admin password reset is also the credential-containment action: it revokes the target user’s OAuth2 tokens, API keys and active sessions, not just the password. That is deliberate — an attacker holding a session or API key would otherwise keep access through a “lockout” reset — but it does mean resetting a password to help a user will break their running integrations.
Recovering an account locked out by 2FA
Section titled “Recovering an account locked out by 2FA”An account with a TOTP enrolment answers POST /auth/login with 200 and
requires_2fa: true — no access token. If the authenticator is gone, no password
reset recovers it: the second factor is checked after the password, so the account
stays unreachable and any non-interactive caller (a deploy, a script) fails on the
missing token rather than on a clear authentication error.
Clear the factor from the host, which needs database access rather than a login:
sudo -u mantis mantisctl user disable-two-factor --email admin@mantis.localIt is idempotent — an account with no enrolment reports zero. The user can enrol a new authenticator afterwards.
Deploys never run this for you: your second factor is yours to keep. A deploy only authenticates when it has just recreated the database, and a freshly seeded admin has no enrolment — so an operator’s 2FA never blocks a converge.
Role Management
Section titled “Role Management”List User Roles
Section titled “List User Roles”curl -X GET \ -H "Authorization: Bearer $TOKEN" \ "https://api.mantis.local/api/v1/admin/users/$USER_ID/roles"Assign Roles
Section titled “Assign Roles”curl -X POST \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "role_ids": ["019b937d-4862-8e07-ac3a-1b8589d1b807", "019b937d-4862-84ae-9c2a-6ac230cc4c7e"] }' \ "https://api.mantis.local/api/v1/admin/users/$USER_ID/roles"Remove Roles
Section titled “Remove Roles”curl -X POST \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "role_ids": ["019b937d-4862-8e07-ac3a-1b8589d1b807"] }' \ "https://api.mantis.local/api/v1/admin/users/$USER_ID/roles/remove"User Provisioning
Section titled “User Provisioning”Local Users
Section titled “Local Users”An administrator invites the user; the user sets their own password:
SSO/OIDC Users
Section titled “SSO/OIDC Users”Users can be provisioned automatically on first SSO login, provided the identity provider is scoped to a tenant and has auto-provisioning enabled (auto_create_users). System-wide (global) identity providers do not auto-provision new users (SEC-OIDC-02); link the provider to a tenant to enable this:
Tenant Assignment
Section titled “Tenant Assignment”Assigning Users to Tenants
Section titled “Assigning Users to Tenants”curl -X PUT \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "tenant_id": "019b937d-4862-8387-9a86-df16bd76ca99" }' \ "https://api.mantis.local/api/v1/admin/users/$USER_ID"Removing Tenant Assignment
Section titled “Removing Tenant Assignment”Set tenant_id to null to make user global:
curl -X PUT \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "tenant_id": null }' \ "https://api.mantis.local/api/v1/admin/users/$USER_ID"Password Policy
Section titled “Password Policy”Requirements
Section titled “Requirements”Password requirements are hardcoded and cannot be changed via configuration:
- Minimum 12 characters
- At least one uppercase letter
- At least one lowercase letter
- At least one digit
- At least one special character (
!@#$%^&*()_+-=[]{}|;:,.<>?)
Password Storage
Section titled “Password Storage”- Passwords hashed with Argon2id (library default parameters)
- Original passwords never stored
- Hash comparison for authentication
External Identities
Section titled “External Identities”Linked Accounts
Section titled “Linked Accounts”Users can have multiple external identities:
| Field | Description |
|---|---|
provider_id | Identity provider reference |
provider_name | Name of the identity provider |
external_username | Username from external provider |
external_email | Email from external provider |
last_login_at | Last SSO login via this provider |
Identity Linking
Section titled “Identity Linking”When a user logs in via SSO:
- System checks for an existing
external_identitymatch. - If found, updates last login and uses the linked user.
- If not found, creation/linking is gated, not automatic:
- Auto-provisioning a new user requires the IdP to be tenant-scoped (a system-wide / NULL-tenant provider cannot provision).
- Linking to an existing user by email additionally requires the provider’s
allow_email_linking = trueand the ID token asserting a verified email; otherwise it fails closed with403 Email is already associated with another account.
Unlink External Identity
Section titled “Unlink External Identity”Remove the link through the API, which also ends the account’s sessions; it needs
users:update and is audited as user.external_identity_unlinked. It also revokes the
roles the provider’s group mappings granted, in either role sync mode, which needs
roles:manage and the authority to confer each of them, as removing them by hand does:
curl -X DELETE \ -H "Authorization: Bearer $TOKEN" \ "$API_URL/api/v1/admin/users/$USER_ID/external-identities/$PROVIDER_ID"Audit Trail
Section titled “Audit Trail”All user operations are logged:
| Event | Category | Severity |
|---|---|---|
user.created | user_management | Info |
user.updated | user_management | Info |
user.deleted | user_management | Info |
user.password_reset | user_management | Security |
user.roles_assigned | authorization | Security |
user.roles_removed | authorization | Security |
Example audit entry:
{ "event_type": "user.created", "event_category": "user_management", "actor_type": "user", "actor_id": "019b937d-4862-8e07-ac3a-1b8589d1b801", "actor_name": "admin", "resource_type": "user", "resource_id": "019b937d-4862-8e07-ac3a-1b8589d1b809", "outcome": "success", "outcome_reason": null, "occurred_at": "2026-01-15T14:23:01Z"}Best Practices
Section titled “Best Practices”User Onboarding
Section titled “User Onboarding”- Use SSO when possible - Reduces password management overhead
- Assign minimum required roles - Follow least privilege principle
- Set tenant scope appropriately - Limit access to necessary resources
- Verify email addresses - Ensure contact information is accurate
User Offboarding
Section titled “User Offboarding”- Set status to inactive - the containment action: it blocks new logins and token refresh, and revokes the account’s live credentials
- Reset the user’s password - belt and braces; it performs the same revocation and stops the old password working if the account is ever reactivated
- Remove role assignments - clear all permissions
- Review owned resources - transfer ownership if needed
- Delete account - after transition period
Regular Audits
Section titled “Regular Audits”- Review active users quarterly
- Verify role assignments match current responsibilities
- Check for unused accounts (no recent login)
- Audit external identity links
Troubleshooting
Section titled “Troubleshooting”User Cannot Login
Section titled “User Cannot Login”-
Check account status:
SELECT status FROM users WHERE email = 'user@example.com'; -
Verify password (local auth):
- Reset password via admin API
- Check for caps lock / keyboard issues
-
Check external identity (SSO):
- Verify provider is configured
- Check role mappings exist
- Review IdP logs for errors
User Missing Permissions
Section titled “User Missing Permissions”-
List user’s current roles:
Terminal window curl -s -H "Authorization: Bearer $TOKEN" \https://api.mantis.local/api/v1/admin/users/$USER_ID/roles -
Check role permissions:
Terminal window curl -s -H "Authorization: Bearer $TOKEN" \https://api.mantis.local/api/v1/admin/roles/$ROLE_ID/permissions -
Assign missing role:
Terminal window curl -X POST \-H "Authorization: Bearer $TOKEN" \-H "Content-Type: application/json" \-d '{"role_ids": ["019b937d-4862-84ae-9c2a-6ac230cc4c7e"]}' \https://api.mantis.local/api/v1/admin/users/$USER_ID/roles
Next Steps
Section titled “Next Steps”- Roles - Role configuration
- Permissions - Permission reference
- Identity Providers - SSO setup
