Single Sign-On (SSO)¶
AegisAI supports enterprise login through external Identity Providers (IdP) via OpenID Connect (OIDC).
Supported Providers¶
| Provider | Protocol | Configuration |
|---|---|---|
| Keycloak | OIDC | Self-hosted, full control |
| Okta | OIDC | Cloud IdP |
| Google Workspace | OIDC | Small teams |
| Microsoft Entra ID | OIDC | Corporate Azure AD / Entra |
SAML 2.0
SAML is not implemented yet. All listed providers support OIDC, which covers the standard corporate SSO flow.
Authentication Flow¶
sequenceDiagram
participant User
participant FE as Frontend
participant API as AegisAI API
participant IdP as Keycloak/Okta/...
User->>FE: Click "Sign in with SSO"
FE->>API: GET /auth/sso/login?provider=keycloak
API->>IdP: Redirect (authorization code + state)
User->>IdP: Enter credentials
IdP->>API: GET /auth/sso/callback?code=...&state=...
API->>IdP: Exchange code for tokens
API->>API: Verify id_token (JWKS), provision user
API->>FE: Redirect /login?sso_code=...
FE->>API: POST /auth/sso/exchange
API-->>FE: JWT + api_key + user
FE->>FE: Store session, navigate to dashboard
Security properties:
- state and nonce protect against CSRF and replay
- id_token signature verified via IdP JWKS
- JWT is not passed in the URL — a one-time exchange code (Redis, 120s TTL) is used instead
Enable SSO¶
1. Global settings¶
SSO_PUBLIC_BASE_URL=https://aegisai.example.com
SSO_FRONTEND_URL=https://aegisai.example.com
SSO_CALLBACK_URL=https://aegisai.example.com/api/v1/auth/sso/callback
SSO_DEFAULT_PROVIDER=keycloak
SSO_AUTO_PROVISION=true
SSO_SYNC_ROLES=true
| Variable | Description |
|---|---|
SSO_PUBLIC_BASE_URL |
Public URL of the site (used for default callback) |
SSO_FRONTEND_URL |
Frontend origin for post-login redirect |
SSO_CALLBACK_URL |
OIDC redirect URI registered in IdP |
SSO_AUTO_PROVISION |
Create users on first SSO login |
SSO_SYNC_ROLES |
Update role from IdP groups on each login |
SSO_ROLE_MAPPING_JSON |
Custom group → role mapping (JSON) |
2. Keycloak¶
KEYCLOAK_ENABLED=true
KEYCLOAK_CLIENT_ID=aegisai
KEYCLOAK_CLIENT_SECRET=your-client-secret
KEYCLOAK_ISSUER=https://keycloak.example.com/realms/master
In Keycloak admin:
- Create client
aegisai(confidential, standard flow) - Valid redirect URI:
https://aegisai.example.com/api/v1/auth/sso/callback - Add mappers for
email,groups, orrealm_access.rolesas needed
3. Okta¶
OKTA_ENABLED=true
OKTA_CLIENT_ID=your-client-id
OKTA_CLIENT_SECRET=your-client-secret
OKTA_ISSUER=https://your-org.okta.com/oauth2/default
Register the same callback URL in the Okta application settings.
4. Google Workspace¶
GOOGLE_SSO_ENABLED=true
GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=your-client-secret
Authorized redirect URI: https://aegisai.example.com/api/v1/auth/sso/callback
5. Microsoft Entra ID¶
MICROSOFT_SSO_ENABLED=true
MICROSOFT_TENANT_ID=your-tenant-id
MICROSOFT_CLIENT_ID=your-application-id
MICROSOFT_CLIENT_SECRET=your-client-secret
Redirect URI (Web): https://aegisai.example.com/api/v1/auth/sso/callback
Role Mapping¶
Default mapping from IdP groups/roles to AegisAI roles:
| AegisAI Role | Example IdP groups |
|---|---|
super_admin |
super_admin, aegis-super-admin |
admin |
admin, Admins, Administrator |
security_analyst |
SecurityAnalysts, security_analyst |
developer |
Developers, developer |
viewer |
default if no match |
Custom mapping:
Claims read from: groups, roles, Keycloak realm_access.roles, and resource_access.
User Provisioning¶
On first SSO login AegisAI:
- Finds user by
(auth_provider, external_subject)or links by email - Creates user with
password_hash = NULL(password login disabled) - Assigns role from IdP groups
- Auto-binds to the sole tenant when unambiguous (same as password login)
Set SSO_AUTO_PROVISION=false to allow SSO only for pre-created users.
API Endpoints¶
| Method | Endpoint | Description |
|---|---|---|
GET |
/auth/sso/providers |
List enabled IdP (public) |
GET |
/auth/sso/login?provider=... |
Start OIDC flow (redirect) |
GET |
/auth/sso/callback |
IdP callback (redirect) |
POST |
/auth/sso/exchange |
Exchange one-time code for JWT |
List providers¶
Exchange code (frontend)¶
curl -X POST https://aegisai.example.com/api/v1/auth/sso/exchange \
-H "Content-Type: application/json" \
-d '{"code": "<sso_code from redirect>"}'
Database Migration¶
SSO requires migration 006_sso_fields:
Adds auth_provider, external_subject, and nullable password_hash on users.
Troubleshooting¶
| Issue | Solution |
|---|---|
| No SSO buttons on login | Check provider *_ENABLED=true and client_id set; call /auth/sso/providers |
invalid_state |
Clock skew, expired flow (>10 min), or cookies blocked — retry login |
redirect_uri mismatch |
Callback URL in IdP must exactly match SSO_CALLBACK_URL |
User created as viewer |
Add group mappers in IdP; configure SSO_ROLE_MAPPING_JSON |
| SSO user cannot use password | Expected — SSO users have no local password |
Related Documentation¶
- Users — user management
- Roles — RBAC matrix
- Deployment — production env vars
- Troubleshooting — login issues