Users & Roles
Every person who accesses oikapi has a user account with an email and password. Users authenticate via session-based login or API keys.
Onboarding users
Section titled “Onboarding users”Invitations are the normal way to add someone. The invitee sets their own password, so no administrator ever handles it, and the roles you assign are applied when they register.
# Email an invitation, with roles pre-assignedoi admin user invite send user@example.com -r member
# Or generate a shareable link — useful for onboarding a groupoi admin user invite link -r member --max-uses 10
oi admin user invite listoi admin user invite revoke <invite-id>Invitations expire; pass --expires 48h to change the window. A link with
--max-uses 0 is unlimited.
Creating a user directly
Section titled “Creating a user directly”Direct creation exists for the cases invitations do not cover — seeding an instance, scripted provisioning, or a service account that will never check email. It requires you to set an initial password, which is why it is not the default path.
oi admin user create user@example.com -p 'Pass123@' -r member --name 'Jane Doe'If -p is omitted, the CLI prompts for it interactively.
Users can also self-register through the API when your instance allows it:
POST /api/auth/register{ "email": "user@example.com", "password": "Pass123!", "name": "Jane Doe"}Listing users
Section titled “Listing users”oi admin user listoi admin user get user@example.comPromoting to admin
Section titled “Promoting to admin”Grant or remove the admin role:
oi admin user role promote user@example.comoi admin user role demote user@example.comDeactivating users
Section titled “Deactivating users”Deleting a user deactivates their account (soft delete). Their audit history is preserved and the account can be restored.
oi admin user delete user@example.comRoles define what a user can do. Every user is assigned one or more roles.
Built-in roles
Section titled “Built-in roles”| Role | Description |
|---|---|
superuser | Platform superuser — full access including system tables, cache management, signing keys |
admin | Day-to-day administrator — bypasses permission checks for non-system operations |
member | Baseline role for authenticated users — grants read access to essential system tables |
visitor | External user with zero base access — sees only what explicit grants allow |
Creating custom roles
Section titled “Creating custom roles”Every custom role must belong to an application:
POST /api/apps/system/tables/roles/records{ "name": "sales_manager", "display_name": "Sales Manager", "description": "Can manage all CRM and Sales data", "application": "crm"}Assigning roles to users
Section titled “Assigning roles to users”Via CLI:
oi admin user role add user@example.com sales_manageroi admin user role remove user@example.com sales_managerVia API:
POST /api/users/{user-id}/roles{ "role_id": "<role-uuid>"}Role strategy
Section titled “Role strategy”Start with a few broad roles (e.g., manager, employee, viewer) and use application-level permissions to grant access. Add table-level overrides only where needed.
Single Sign-On (SSO)
Section titled “Single Sign-On (SSO)”oikapi supports enterprise single sign-on so your users authenticate with your existing identity provider (IdP) instead of a local password. Two protocols are supported:
- OIDC (OpenID Connect) — e.g. Okta, Microsoft Entra ID, Google Workspace, Auth0, Keycloak
- SAML 2.0 — for IdPs that offer SAML but not OIDC
Both protocols share the same just-in-time (JIT) provisioning path, so users are created and linked identically regardless of which one you configure.
Enabling SSO
Section titled “Enabling SSO”SSO is gated at two levels:
- Server config — the operator sets
security.sso.enabled: trueand supplies a 32-byte state-encryption key. In production the key must come from the environment (OIKAPI_SECURITY_SSO_STATE_ENCRYPTION_KEY), base64-encoded; the server refuses to start SSO with the built-in dev default. - License — SSO login is a licensed feature (
FeatureSSO); the login and SAML endpoints are feature-gated.
Once enabled, you configure one or more providers as records in the auth_providers system table.
Configuring an OIDC provider
Section titled “Configuring an OIDC provider”Create an auth provider record. The key fields:
POST /api/apps/system/tables/auth_providers/records{ "name": "okta-prod", "display_name": "Okta", "provider_type": "oidc", "enabled": true, "client_id": "<oidc-client-id>", "client_secret": "<oidc-client-secret>", "issuer_url": "https://your-org.okta.com", "scopes": "openid email profile", "allowed_domains": "example.com,example.org", "auto_create_users": true, "default_roles": "member", "claim_mapping_email": "email", "claim_mapping_name": "name", "icon": "KeyIcon", "button_color": "#4285F4"}| Field | Purpose |
|---|---|
name | Unique provider slug used in login URLs (e.g. okta-prod) |
display_name | Label shown on the login button |
issuer_url | OIDC issuer; discovery (/.well-known/openid-configuration) and JWKS are fetched from here |
client_id / client_secret | OIDC client credentials from your IdP |
scopes | Space-separated; defaults to openid email profile |
allowed_domains | Optional comma-separated allowlist of email domains; logins from other domains are rejected |
auto_create_users | When true, first-time users are provisioned automatically (JIT); when false, only pre-existing accounts may sign in |
default_roles | Comma-separated roles granted to auto-created users (defaults to member) |
claim_mapping_email / claim_mapping_name | ID-token claims to read for email/name (default email / name) |
claim_mappings | Optional JSON map of extra IdP claims to user metadata fields (e.g. {"groups": "metadata.groups"}) |
icon / button_color | Cosmetics for the login button |
Configure the redirect/callback URI in your IdP to:
https://<your-host>/api/auth/sso/<name>/callbackConfiguring a SAML 2.0 provider
Section titled “Configuring a SAML 2.0 provider”Create the provider with "provider_type": "saml". oikapi acts as the SAML Service Provider (SP). The SP endpoints for a provider named okta-saml are:
| Endpoint | Purpose |
|---|---|
GET /api/auth/saml/okta-saml/login | Start SP-initiated login (redirect to IdP) |
GET /api/auth/saml/okta-saml/metadata | SP metadata document to import into your IdP |
POST /api/auth/saml/okta-saml/acs | Assertion Consumer Service — where the IdP posts the signed assertion |
Register the ACS URL and SP entity ID (the metadata URL) in your IdP, and provide the IdP’s signing certificate (PEM) and SSO URL in the provider record. Assertion signatures are validated fail-closed — an unsigned or improperly signed assertion is rejected.
By default only SP-initiated login is accepted: an assertion that does not correspond to a login request oikapi issued is rejected (defends against login-CSRF). IdP-initiated (unsolicited) login can be opted into per provider only when your IdP requires it.
Consumed assertion IDs are recorded so a captured, still-in-window assertion cannot be replayed to mint a duplicate session. In multi-pod deployments this single-use guard is backed by ValKey so it holds across pods.
Login endpoints and flow
Section titled “Login endpoints and flow”| Endpoint | Auth | Purpose |
|---|---|---|
GET /api/auth/providers | Public | List enabled providers (name, display name, icon, color) for the login page |
GET /api/auth/sso/{provider}/login | Public (rate-limited) | Begin OIDC login |
GET /api/auth/sso/{provider}/callback | Public | OIDC redirect callback |
POST /api/auth/sso/{provider}/backchannel-logout | Public | OIDC back-channel logout |
POST /api/auth/test-connection | Authenticated | Validate an OIDC issuer’s discovery document |
Login and SAML endpoints are rate-limited (10 requests/minute per client IP).
How users are provisioned and linked
Section titled “How users are provisioned and linked”On a successful login, oikapi resolves the user in this order:
- Existing SSO user — matched by provider + IdP subject/NameID. Disabled accounts (
is_active = false) are refused; SSO cannot silently re-enable a user your IdP deactivated. - Existing account, same provider, verified email — an account already belonging to this provider is re-linked by email.
- Auto-create — if
auto_create_usersis enabled and the IdP asserts a verified email, a new account is created with the provider’sdefault_roles.
To protect against account takeover, oikapi will not auto-link an SSO identity to a pre-existing local or different-provider account matched only by email. Those users must sign in normally and link the provider from account settings. Unverified IdP emails are never auto-linked or auto-provisioned.
SCIM provisioning
Section titled “SCIM provisioning”oikapi implements SCIM 2.0 so your IdP can provision, update, and deprovision users and groups automatically — no manual account management as people join, move, or leave.
The base URL is:
https://<your-host>/api/scim/v2Authentication
Section titled “Authentication”Point your IdP at the SCIM base URL and authenticate with a bearer token minted in oikapi. Tokens are created and managed by a superuser (via session or API key — a SCIM bearer token itself cannot mint or list further tokens):
POST /api/scim/v2/Tokens{ "name": "okta-provisioning", "expires_in_days": 365}The response returns the plaintext token once — store it in your IdP immediately. Tokens always expire: expires_in_days defaults to 365 and is capped at 730 (2 years). Manage tokens with:
GET /api/scim/v2/TokensDELETE /api/scim/v2/Tokens/{id}The bearer path is rate-limited (30 requests/minute per client IP).
Users (/Users)
Section titled “Users (/Users)”Standard SCIM 2.0 User operations are supported:
| Method | Path | Purpose |
|---|---|---|
GET | /api/scim/v2/Users | List/filter users |
GET | /api/scim/v2/Users/{id} | Fetch one user |
POST | /api/scim/v2/Users | Provision a user |
PUT | /api/scim/v2/Users/{id} | Replace a user |
PATCH | /api/scim/v2/Users/{id} | Partial update (e.g. flip active) |
DELETE | /api/scim/v2/Users/{id} | Deprovision |
userName maps to the account email and externalId to the IdP’s stable user ID. Filtering supports userName eq "..." and externalId eq "..." for IdP reconciliation (the filters Okta and Entra use).
When a user is disabled (active: false) or deleted, oikapi immediately revokes access — cached auth profile, live sessions, and API keys are invalidated. Deactivation and deletion are guarded by the last-admin recoverability invariant: a request that would remove the final recoverable admin is rejected with 409 Conflict.
Groups (/Groups)
Section titled “Groups (/Groups)”| Method | Path | Purpose |
|---|---|---|
GET | /api/scim/v2/Groups | List/filter groups |
GET | /api/scim/v2/Groups/{id} | Fetch one group with members |
POST | /api/scim/v2/Groups | Create a group |
PATCH | /api/scim/v2/Groups/{id} | Add/remove members or rename |
DELETE | /api/scim/v2/Groups/{id} | Delete a group |
SCIM groups are provisioned as oikapi teams (tagged as SCIM-managed). PATCH handles membership changes; group filtering supports displayName eq "...". Membership changes invalidate affected users’ cached auth profiles so effective access updates immediately.
All SCIM user and group mutations are recorded as attributable security audit events.