Skip to content

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:

  1. Create client aegisai (confidential, standard flow)
  2. Valid redirect URI: https://aegisai.example.com/api/v1/auth/sso/callback
  3. Add mappers for email, groups, or realm_access.roles as 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:

SSO_ROLE_MAPPING_JSON={"admin":["Corp-Admins","IT-Admins"],"super_admin":["Platform-Admins"]}

Claims read from: groups, roles, Keycloak realm_access.roles, and resource_access.

User Provisioning

On first SSO login AegisAI:

  1. Finds user by (auth_provider, external_subject) or links by email
  2. Creates user with password_hash = NULL (password login disabled)
  3. Assigns role from IdP groups
  4. 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

curl https://aegisai.example.com/api/v1/auth/sso/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:

docker compose exec app poetry run alembic upgrade head

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