Configure dashboard authentication
This guide covers configuring authentication for the Omnia dashboard. Choose the mode that fits your deployment:
| Mode | Best For | Complexity |
|---|---|---|
| Anonymous | Development, demos | Minimal |
| Proxy | Existing OAuth2 Proxy setup | Low |
| OAuth | Standalone deployments with IdP | Medium |
| Builtin | Small teams without IdP | Medium |
For agent endpoint authentication (JWT/Istio), see Configure Agent Authentication.
Prerequisites
Section titled “Prerequisites”- Omnia dashboard deployed
- Access to environment configuration
- (For OAuth) Identity provider credentials
Where these settings go
Section titled “Where these settings go”The examples below show the OMNIA_* environment variables the dashboard container
reads. You don’t set these by hand — the Helm chart renders them from dashboard.auth.*
and dashboard.oauth.* values into the dashboard’s ConfigMap (non-secret settings) and wires
credentials in from a Kubernetes Secret. So you configure auth at install/upgrade time in
your values file:
helm upgrade --install omnia altaira/omnia -f values.yamlEach env var maps to a value:
| Environment variable | Helm value |
|---|---|
OMNIA_AUTH_MODE |
dashboard.auth.mode |
OMNIA_AUTH_ROLE_ADMIN_GROUPS |
dashboard.auth.roleMapping.adminGroups (list) |
OMNIA_AUTH_ROLE_EDITOR_GROUPS |
dashboard.auth.roleMapping.editorGroups (list) |
OMNIA_OAUTH_PROVIDER |
dashboard.oauth.provider |
OMNIA_OAUTH_ISSUER_URL |
dashboard.oauth.issuerUrl |
OMNIA_OAUTH_SCOPES |
dashboard.oauth.scopes |
OMNIA_OAUTH_AZURE_TENANT_ID |
dashboard.oauth.azureTenantId |
OMNIA_OAUTH_CLAIM_USERNAME / _EMAIL / _GROUPS |
dashboard.oauth.claims.username / .email / .groups |
Credentials come from a Secret, never values.yaml. Create a Kubernetes Secret and reference it — the chart mounts its keys as env:
dashboard.oauth.existingSecret→ a Secret with keysOMNIA_OAUTH_CLIENT_IDandOMNIA_OAUTH_CLIENT_SECRETdashboard.auth.existingSessionSecret→ a Secret with keyOMNIA_SESSION_SECRET(the session cookie signing key)
A complete OAuth values block (Cognito) looks like:
dashboard: auth: mode: oauth existingSessionSecret: omnia-dashboard-oauth # holds OMNIA_SESSION_SECRET roleMapping: adminGroups: [omnia-admins] # platform admins oauth: provider: generic existingSecret: omnia-dashboard-oauth # holds client id + secret issuerUrl: https://cognito-idp.<region>.amazonaws.com/<userPoolId> scopes: "openid,email,profile" claims: username: cognito:username groups: cognito:groupsFor Entra ID, swap oauth.provider: azure + oauth.azureTenantId, and use the default
groups claim (object-ID GUIDs). The mode-by-mode sections below show the individual
settings; combine them under dashboard.auth / dashboard.oauth as above.
Anonymous mode
Section titled “Anonymous mode”The default mode with no authentication. All users can access the dashboard as viewers.
Enable anonymous mode
Section titled “Enable anonymous mode”Set the auth mode environment variable:
OMNIA_AUTH_MODE=anonymousConfigure anonymous role
Section titled “Configure anonymous role”By default, anonymous users get the viewer role. To change this:
OMNIA_AUTH_ANONYMOUS_ROLE=editor # or adminProxy mode
Section titled “Proxy mode”Delegates authentication to a reverse proxy that handles OAuth/OIDC.
How it works
Section titled “How it works”- Reverse proxy (OAuth2 Proxy, Authelia, etc.) handles authentication
- Proxy forwards user info in HTTP headers
- Dashboard reads headers and creates session
Enable proxy mode
Section titled “Enable proxy mode”OMNIA_AUTH_MODE=proxyConfigure header names
Section titled “Configure header names”Match your proxy’s header configuration:
# Default header names (OAuth2 Proxy compatible)OMNIA_AUTH_PROXY_HEADER_USER=X-Forwarded-UserOMNIA_AUTH_PROXY_HEADER_EMAIL=X-Forwarded-EmailOMNIA_AUTH_PROXY_HEADER_GROUPS=X-Forwarded-GroupsOMNIA_AUTH_PROXY_HEADER_DISPLAY_NAME=X-Forwarded-Preferred-UsernameConfigure role mapping
Section titled “Configure role mapping”Map identity provider groups to dashboard roles:
# Comma-separated list of groups that get admin roleOMNIA_AUTH_ROLE_ADMIN_GROUPS=omnia-admins,platform-admins
# Comma-separated list of groups that get editor roleOMNIA_AUTH_ROLE_EDITOR_GROUPS=omnia-editors,developers
# Users not in any group get viewer roleOAuth2 proxy example
Section titled “OAuth2 proxy example”Example OAuth2 Proxy configuration for Google:
# oauth2-proxy values.yamlconfig: clientID: "YOUR_CLIENT_ID" clientSecret: "YOUR_CLIENT_SECRET" cookieSecret: "YOUR_COOKIE_SECRET"
extraArgs: provider: google email-domain: "*" pass-user-headers: true set-xauthrequest: true
ingress: enabled: true hosts: - dashboard.example.comNginx ingress annotation:
nginx.ingress.kubernetes.io/auth-url: "http://oauth2-proxy.auth.svc.cluster.local/oauth2/auth"nginx.ingress.kubernetes.io/auth-signin: "https://dashboard.example.com/oauth2/start?rd=$escaped_request_uri"nginx.ingress.kubernetes.io/auth-response-headers: "X-Auth-Request-User,X-Auth-Request-Email,X-Auth-Request-Groups"Authelia example
Section titled “Authelia example”Authelia configuration:
# authelia configuration.ymlaccess_control: default_policy: deny rules: - domain: dashboard.example.com policy: two_factor subject: - "group:omnia-users"Traefik middleware:
apiVersion: traefik.containo.us/v1alpha1kind: Middlewaremetadata: name: autheliaspec: forwardAuth: address: http://authelia.auth.svc.cluster.local/api/verify?rd=https://auth.example.com authResponseHeaders: - Remote-User - Remote-Email - Remote-GroupsUpdate dashboard headers to match:
OMNIA_AUTH_PROXY_HEADER_USER=Remote-UserOMNIA_AUTH_PROXY_HEADER_EMAIL=Remote-EmailOMNIA_AUTH_PROXY_HEADER_GROUPS=Remote-GroupsOAuth mode
Section titled “OAuth mode”Direct OAuth 2.0 / OpenID Connect integration with identity providers.
How it works
Section titled “How it works”- User clicks “Sign in” on login page
- Dashboard redirects to identity provider with PKCE
- User authenticates with IdP
- IdP redirects back with authorization code
- Dashboard exchanges code for tokens
- Dashboard creates session from ID token claims
Enable OAuth mode
Section titled “Enable OAuth mode”OMNIA_AUTH_MODE=oauthRequired configuration
Section titled “Required configuration”All OAuth deployments need:
# Base URL for callbacks (no trailing slash)OMNIA_BASE_URL=https://dashboard.example.com
# OAuth client credentialsOMNIA_OAUTH_CLIENT_ID=your-client-idOMNIA_OAUTH_CLIENT_SECRET=your-client-secret
# Or mount secret from file (K8s Secret)# OMNIA_OAUTH_CLIENT_SECRET_FILE=/etc/omnia/oauth/client-secretProvider configuration
Section titled “Provider configuration”Generic OIDC
Section titled “Generic OIDC”For any OIDC-compliant provider:
OMNIA_OAUTH_PROVIDER=genericOMNIA_OAUTH_ISSUER_URL=https://auth.example.comOMNIA_OAUTH_SCOPES=openid,profile,email,groupsOMNIA_OAUTH_PROVIDER=googleOMNIA_OAUTH_CLIENT_ID=xxx.apps.googleusercontent.comOMNIA_OAUTH_CLIENT_SECRET=GOCSPX-xxxCreate credentials at Google Cloud Console:
- Create OAuth 2.0 Client ID
- Set authorized redirect URI:
https://dashboard.example.com/api/auth/callback - Copy client ID and secret
Azure AD / entra ID
Section titled “Azure AD / entra ID”OMNIA_OAUTH_PROVIDER=azureOMNIA_OAUTH_AZURE_TENANT_ID=your-tenant-idOMNIA_OAUTH_CLIENT_ID=your-application-idOMNIA_OAUTH_CLIENT_SECRET=your-client-secretConfigure in Azure Portal:
- Register application in App registrations
- Add redirect URI:
https://dashboard.example.com/api/auth/callback - Create client secret
- Configure API permissions:
openid,profile,email - (Optional) Add
GroupMember.Read.Allfor group claims
OMNIA_OAUTH_PROVIDER=oktaOMNIA_OAUTH_OKTA_DOMAIN=your-domain.okta.comOMNIA_OAUTH_CLIENT_ID=your-client-idOMNIA_OAUTH_CLIENT_SECRET=your-client-secretConfigure in Okta Admin:
- Create OIDC Web Application
- Set sign-in redirect URI:
https://dashboard.example.com/api/auth/callback - Set sign-out redirect URI:
https://dashboard.example.com/login - Assign users/groups to application
GitHub
Section titled “GitHub”OMNIA_OAUTH_PROVIDER=githubOMNIA_OAUTH_CLIENT_ID=your-client-idOMNIA_OAUTH_CLIENT_SECRET=your-client-secretAWS Cognito
Section titled “AWS Cognito”Cognito is OIDC-compliant, so use the generic provider pointed at your user pool’s issuer URL:
OMNIA_OAUTH_PROVIDER=genericOMNIA_OAUTH_ISSUER_URL=https://cognito-idp.<region>.amazonaws.com/<userPoolId>OMNIA_OAUTH_CLIENT_ID=your-app-client-idOMNIA_OAUTH_CLIENT_SECRET=your-app-client-secret
# Cognito uses non-standard claim names — map them explicitly:OMNIA_OAUTH_CLAIM_USERNAME=cognito:usernameOMNIA_OAUTH_CLAIM_GROUPS=cognito:groups
# Cognito has no "groups" scope; the generic provider requests one by default,# which Cognito rejects. Override the scope list:OMNIA_OAUTH_SCOPES=openid,email,profileConfigure the user pool (console or Terraform):
- Create an app client whose callback URL is
https://dashboard.example.com/api/auth/callback. - Create groups (e.g.
omnia-admins) and add users:aws cognito-idp admin-add-user-to-group --user-pool-id <id> --username <user> --group-name omnia-admins. - Cognito emits the group name in the
cognito:groupsclaim — that’s the value you reference in role mappings and workspaceroleBindings.
Claim mapping
Section titled “Claim mapping”Map OIDC claims to user fields:
# Which claim contains the usernameOMNIA_OAUTH_CLAIM_USERNAME=preferred_username
# Which claim contains the emailOMNIA_OAUTH_CLAIM_EMAIL=email
# Which claim contains the display nameOMNIA_OAUTH_CLAIM_DISPLAY_NAME=name
# Which claim contains groups (supports nested paths)OMNIA_OAUTH_CLAIM_GROUPS=groupsFor Entra ID / Azure AD, the default groups claim carries group object IDs (GUIDs), not names:
OMNIA_OAUTH_CLAIM_GROUPS=groups # values are group object-ID GUIDsEnable the claim in the app registration’s Token configuration → Add groups claim. See Use IdP groups for workspace access for how those GUIDs map to workspace roles.
Role mapping
Section titled “Role mapping”Same as proxy mode:
OMNIA_AUTH_ROLE_ADMIN_GROUPS=omnia-adminsOMNIA_AUTH_ROLE_EDITOR_GROUPS=omnia-editorsUse IdP groups for workspace access
Section titled “Use IdP groups for workspace access”Authenticating a user is only half the story — you also decide what they can see and do. Omnia uses IdP groups at two independent layers, and getting a user productively into a workspace usually means setting both.
Two layers
Section titled “Two layers”| Layer | Configured by | Grants |
|---|---|---|
| Platform role (dashboard-wide) | OMNIA_AUTH_ROLE_ADMIN_GROUPS / OMNIA_AUTH_ROLE_EDITOR_GROUPS |
admin / editor / viewer across the whole dashboard. |
| Workspace role (per-workspace) | the Workspace’s spec.roleBindings (and directGrants) |
owner / editor / viewer on that specific workspace — the role that actually lets a user work with its agents, sessions, and data. |
Match the group identifier your IdP actually emits
Section titled “Match the group identifier your IdP actually emits”roleBindings[].groups (and the OMNIA_AUTH_ROLE_* lists) are compared against the user’s
group claim by exact, case-sensitive string equality. The value you must list is whatever
your IdP puts in the claim — which differs by provider:
| IdP | Group claim | Value to list in roleBindings.groups |
|---|---|---|
| AWS Cognito | cognito:groups |
the group name, e.g. omnia-admins |
| Entra ID / Azure AD | groups |
the group object ID (GUID), e.g. c16e8ed8-544e-489c-bbb6-9b027822ddc7 |
| Okta / generic OIDC | groups (or your OMNIA_OAUTH_CLAIM_GROUPS) |
whatever the claim carries (usually the name) |
End-to-end example
Section titled “End-to-end example”Map a Cognito group omnia-admins to both a dashboard admin and owner on a workspace:
# Dashboard (helm values / env): OMNIA_AUTH_ROLE_ADMIN_GROUPS=omnia-admins---apiVersion: omnia.altairalabs.ai/v1alpha1kind: Workspacemetadata: name: customer-supportspec: displayName: "Customer Support" namespace: name: omnia-customer-support create: true roleBindings: - groups: ["omnia-admins"] # Cognito group NAME (use the object-ID GUID for Entra) role: ownerFor the full workspace access model — editor/viewer, directGrants, and ServiceAccount
bindings — see
Manage workspaces → Configure access control.
Builtin mode
Section titled “Builtin mode”Self-contained authentication with a local user database. No external identity provider required.
How it works
Section titled “How it works”- Users register or are seeded by admin
- Credentials stored in SQLite (default) or PostgreSQL
- Passwords hashed with bcrypt (12 rounds)
- Sessions managed via encrypted cookies
Enable builtin mode
Section titled “Enable builtin mode”OMNIA_AUTH_MODE=builtinStorage backend
Section titled “Storage backend”SQLite (default)
Section titled “SQLite (default)”Zero-configuration; suited to single-instance deployments:
OMNIA_BUILTIN_STORE_TYPE=sqliteOMNIA_BUILTIN_SQLITE_PATH=./data/omnia-users.dbPostgreSQL
Section titled “PostgreSQL”For multi-instance production deployments:
OMNIA_BUILTIN_STORE_TYPE=postgresqlOMNIA_BUILTIN_POSTGRES_URL=postgresql://user:password@localhost:5432/omniaInitial admin user
Section titled “Initial admin user”On first run, an admin user is automatically created:
OMNIA_BUILTIN_ADMIN_USERNAME=adminOMNIA_BUILTIN_ADMIN_EMAIL=admin@example.comOMNIA_BUILTIN_ADMIN_PASSWORD=changeme123User registration
Section titled “User registration”Control whether new users can sign up:
# Allow public signup (default: false)OMNIA_BUILTIN_ALLOW_SIGNUP=true
# Require email verification (default: false)OMNIA_BUILTIN_VERIFY_EMAIL=truePassword policy
Section titled “Password policy”Configure password requirements:
# Minimum password length (default: 8)OMNIA_BUILTIN_MIN_PASSWORD_LENGTH=12Account security
Section titled “Account security”Protect against brute force attacks:
# Failed attempts before lockout (default: 5)OMNIA_BUILTIN_MAX_FAILED_ATTEMPTS=5
# Lockout duration in seconds (default: 900 = 15 minutes)OMNIA_BUILTIN_LOCKOUT_DURATION=900Password reset
Section titled “Password reset”Configure password reset tokens:
# Token expiration in seconds (default: 3600 = 1 hour)OMNIA_BUILTIN_RESET_TOKEN_EXPIRATION=3600Email verification
Section titled “Email verification”For deployments requiring verified emails:
OMNIA_BUILTIN_VERIFY_EMAIL=true
# Token expiration in seconds (default: 86400 = 24 hours)OMNIA_BUILTIN_VERIFICATION_TOKEN_EXPIRATION=86400Kubernetes example
Section titled “Kubernetes example”dashboard: env: OMNIA_AUTH_MODE: builtin OMNIA_BUILTIN_STORE_TYPE: postgresql OMNIA_BUILTIN_ALLOW_SIGNUP: "false"
envFrom: - secretRef: name: dashboard-builtin-secretapiVersion: v1kind: Secretmetadata: name: dashboard-builtin-secrettype: OpaquestringData: OMNIA_SESSION_SECRET: "your-32-char-secret-here" OMNIA_BUILTIN_POSTGRES_URL: "postgresql://user:pass@postgres:5432/omnia" OMNIA_BUILTIN_ADMIN_PASSWORD: "initial-admin-password"Session configuration
Section titled “Session configuration”Configure session behavior for all modes:
# Session encryption secret (required in production!)# Generate with: openssl rand -base64 32OMNIA_SESSION_SECRET=your-32-character-secret-here
# Cookie nameOMNIA_SESSION_COOKIE_NAME=omnia_session
# Session lifetime in seconds (default: 24 hours)OMNIA_SESSION_TTL=86400Session cookie attributes
Section titled “Session cookie attributes”The dashboard issues its session cookie with HttpOnly + SameSite=Lax, and Secure when NODE_ENV=production. The clearing Set-Cookie issued on logout / invalid-session paths carries the same attributes, so a transient MITM observing the clear cannot downgrade or leak the cookie value.
Security response headers
Section titled “Security response headers”Every dashboard response carries a defence-in-depth header baseline:
| Header | Default | Override |
|---|---|---|
Strict-Transport-Security |
max-age=63072000; includeSubDomains; preload |
— |
Content-Security-Policy |
default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https:; font-src 'self' data:; connect-src 'self' ws: wss:; media-src 'self' blob: data:; frame-ancestors 'none'; base-uri 'self'; form-action 'self' |
OMNIA_CSP_POLICY env var |
X-Frame-Options |
DENY |
— |
X-Content-Type-Options |
nosniff |
— |
Referrer-Policy |
strict-origin-when-cross-origin |
— |
Permissions-Policy |
camera=(), microphone=(), geolocation=(), payment=() |
— |
The default CSP includes 'unsafe-inline' and 'unsafe-eval' because Next.js uses inline scripts for hydration and runtime-evaluated chunks. Operators with a strict policy can override by setting OMNIA_CSP_POLICY in the dashboard environment — the value replaces the default wholesale.
The x-powered-by: Next.js header is disabled via poweredByHeader: false in next.config.ts, so the runtime + framework version are not advertised in responses.
API keys
Section titled “API keys”Enable programmatic access for scripts and CI/CD:
# Enable API key authenticationOMNIA_AUTH_API_KEYS_ENABLED=true
# Maximum keys per user (default: 10)OMNIA_AUTH_API_KEYS_MAX_PER_USER=10
# Default expiration in days (default: 90, 0 = never)OMNIA_AUTH_API_KEYS_DEFAULT_EXPIRATION=90Generate API key
Section titled “Generate API key”Users can generate keys from Settings > API Keys, or use the CLI:
cd dashboardnode scripts/generate-api-key.mjs \ --name "CI Pipeline" \ --user-id "user-123" \ --role "editor" \ --expires-in-days 30Use API key
Section titled “Use API key”Include the key in requests:
# Using Authorization headercurl -H "Authorization: Bearer omnia_sk_abc123..." \ https://dashboard.example.com/api/agents
# Or using X-API-Key headercurl -H "X-API-Key: omnia_sk_abc123..." \ https://dashboard.example.com/api/agentsKubernetes deployment
Section titled “Kubernetes deployment”Using Helm values
Section titled “Using Helm values”dashboard: env: OMNIA_AUTH_MODE: oauth OMNIA_OAUTH_PROVIDER: google OMNIA_BASE_URL: https://dashboard.example.com
envFrom: - secretRef: name: dashboard-oauth-secretOAuth secret
Section titled “OAuth secret”apiVersion: v1kind: Secretmetadata: name: dashboard-oauth-secrettype: OpaquestringData: OMNIA_SESSION_SECRET: "your-32-char-secret-here" OMNIA_OAUTH_CLIENT_ID: "your-client-id" OMNIA_OAUTH_CLIENT_SECRET: "your-client-secret"File-mounted secret
Section titled “File-mounted secret”For client secret rotation without pod restart:
apiVersion: v1kind: Secretmetadata: name: oauth-client-secrettype: OpaquestringData: client-secret: "your-client-secret"---# In deploymentvolumes: - name: oauth-secret secret: secretName: oauth-client-secretvolumeMounts: - name: oauth-secret mountPath: /etc/omnia/oauth readOnly: trueenv: - name: OMNIA_OAUTH_CLIENT_SECRET_FILE value: /etc/omnia/oauth/client-secretTroubleshooting
Section titled “Troubleshooting”OAuth login fails
Section titled “OAuth login fails”Symptom: Redirect to IdP works, but callback fails
Check:
- Callback URL matches exactly:
https://dashboard.example.com/api/auth/callback OMNIA_BASE_URLhas no trailing slash- Client secret is correct
- Required scopes are configured in IdP
Session not persisting
Section titled “Session not persisting”Symptom: User logged out after page refresh
Check:
OMNIA_SESSION_SECRETis set- Cookie is being set (check browser dev tools)
- No proxy stripping cookies
Groups not working
Section titled “Groups not working”Symptom: User authenticated but has wrong role
Check:
- Groups claim is present in ID token (use jwt.io to decode)
OMNIA_OAUTH_CLAIM_GROUPSmatches your IdP’s claim name- Group names match
OMNIA_AUTH_ROLE_*_GROUPSexactly
Proxy headers not received
Section titled “Proxy headers not received”Symptom: Proxy mode shows anonymous user
Check:
- Proxy is configured to forward headers
- Header names match configuration
- No intermediate proxy stripping headers
- Test with:
curl -vto see headers
Builtin login fails
Section titled “Builtin login fails”Symptom: Correct credentials rejected
Check:
- User exists in database
- Account not locked (check failed login count)
- Email verified (if
OMNIA_BUILTIN_VERIFY_EMAIL=true) - Database file/connection accessible
Account locked out
Section titled “Account locked out”Symptom: Login fails with “Account locked”
Fix:
- Wait for lockout duration (default: 15 minutes)
- Or manually reset in database:
UPDATE users SET failed_login_attempts = 0, locked_until = NULL WHERE email = 'user@example.com';
Password reset not working
Section titled “Password reset not working”Symptom: No reset email received
Check:
- Email service configured (or check console logs for token)
- Token not expired (
OMNIA_BUILTIN_RESET_TOKEN_EXPIRATION) - User exists with that email
Next steps
Section titled “Next steps”- Authentication Architecture - Understand the full auth model
- Configure Agent Authentication - Secure agent endpoints
- Dashboard Auth Reference - Complete configuration reference
- Manage Workspaces - Set up team isolation and access control