Authentication
Built-in JWT authentication with Argon2id hashing, refresh tokens, and server-side session management. The platform natively supports OpenID Connect (OIDC) Single Sign-On for integration with enterprise identity providers.
Tokens
HS256-signed JWTs.
| Token | Lifetime | Purpose |
|---|---|---|
| Access token | 15 minutes | Sent as Authorization: Bearer <token> on every request. |
| Refresh token | 30 days | Used to get a new access token without re-entering credentials. |
Security
| Property | Value |
|---|---|
| Hashing | Argon2id (OWASP-recommended) |
| JWT signing | HS256 |
| JWT secret | ROOTCX_JWT_SECRET env var, or auto-generated in config/jwt.key |
| Session store | Server-side in PostgreSQL. Allows immediate logout. |
| Password storage | Only the Argon2id hash is stored. Plaintext is never persisted. |
Using Studio
Manage users through the Auth panel in Studio, where you can view registered users and reset passwords.
Using Code
Registration
POST /api/v1/auth/register
{ "email": "alice@example.com", "password": "Str0ngPass!x", "displayName": "Alice Martin" }
Password requirements: minimum 10 characters, at least one uppercase letter, one lowercase letter, and one digit.
admin role with global permissions (*).Login
POST /api/v1/auth/login
{ "email": "alice@example.com", "password": "Str0ngPass!x" }
Returns an access token, a refresh token, and the user object:
{
"accessToken": "eyJ...",
"refreshToken": "eyJ...",
"expiresIn": 900,
"user": {
"id": "01926b3e-...",
"email": "alice@example.com",
"displayName": "Alice Martin",
"createdAt": "2024-12-01T10:00:00+00:00"
}
}
Token Refresh
POST /api/v1/auth/refresh
{ "refreshToken": "..." }
Returns { "accessToken": "...", "expiresIn": 900 }.
Logout
POST /api/v1/auth/logout
{ "refreshToken": "..." }
Invalidates the session server-side. Access tokens already issued remain valid until they expire (max 15 minutes).
See the REST API Reference for all auth endpoints.
OIDC Single Sign-On
RootCX includes a full OIDC Relying Party for integrating with identity providers (Azure AD, Okta, Google Workspace, Auth0, etc.).
Configuring a Provider
Add a provider via the API (admin only):
POST /api/v1/auth/oidc/providers
{
"id": "okta",
"displayName": "Okta SSO",
"issuerUrl": "https://dev-123456.okta.com",
"clientId": "0oa1b2c3d4...",
"clientSecret": "secret...",
"autoRegister": true,
"defaultRole": "admin"
}
| Field | Required | Default | Description |
|---|---|---|---|
id |
Yes | -- | Unique identifier for this provider. |
displayName |
Yes | -- | Shown on the login screen. |
issuerUrl |
Yes | -- | OIDC issuer URL (must be HTTPS, except localhost). |
clientId |
Yes | -- | OAuth 2.0 client ID from your provider. |
clientSecret |
No | -- | OAuth 2.0 client secret. Encrypted in the vault, never stored in plaintext. |
scopes |
No | ["openid", "email", "profile"] |
OAuth scopes to request. |
autoRegister |
No | true |
Auto-create users on first OIDC login. |
defaultRole |
No | "admin" |
Role assigned to new users. |
roleClaim |
No | -- | JWT claim name to extract role from ID token (e.g. "role"). If the extracted role exists in RBAC, it overrides defaultRole. |
enabled |
No | true |
Whether provider appears on login screen. |
To list or delete providers:
GET /api/v1/auth/oidc/providers -- List enabled providers (public, no auth required)
DELETE /api/v1/auth/oidc/providers/{id} -- Delete provider (admin only)
How Users Log In
Once a provider is configured, it appears automatically on the login screen (via AuthGate or the GET /api/v1/auth/mode endpoint).
Browser flow: The user clicks the provider button, gets redirected to the identity provider, authenticates, and is redirected back. RootCX handles the OIDC exchange (Authorization Code + PKCE) and creates a session automatically.
Server-to-server flow: If your backend already has a valid OIDC id_token, exchange it directly:
POST /api/v1/auth/oidc/token-exchange
{ "providerId": "okta", "idToken": "eyJhbGciOiJSUzI1..." }
Returns { "accessToken": "eyJ...", "expiresIn": 900 }.
What Happens on First Login
When a user authenticates via OIDC for the first time:
- If a user with the same email already exists (from password registration), the OIDC identity is linked to that account. No duplicate account created.
- If
autoRegisteris true, a new user is created and assigned thedefaultRole(or the role extracted fromroleClaim). - If
autoRegisteris false, the login is rejected with 403.
Disabling Password Login
Set ROOTCX_DISABLE_PASSWORD_LOGIN=true (or 1) to force SSO-only authentication. Password login and registration are blocked. A first-user bypass allows initial setup when no users exist yet.
Auto-Provisioning via Environment Variables
Set these to seed an OIDC provider automatically on first boot (useful for cloud/Docker deployments):
| Variable | Description |
|---|---|
ROOTCX_OIDC_ISSUER |
OIDC issuer URL. Seeds a provider with id "rootcx" on first boot. |
ROOTCX_OIDC_CLIENT_ID |
Client ID (required if issuer set). |
ROOTCX_OIDC_CLIENT_SECRET |
Client secret (required if issuer set, encrypted in vault). |
ROOTCX_PUBLIC_URL |
Public URL for OIDC callbacks (e.g. https://core.example.com). Falls back to ROOTCX_URL. |