HEXALGON SSO is the browser-facing OIDC broker in the Hexalgon auth stack. It owns authentication, brokered SSO sessions, MFA, consent, upstream federation, and downstream token issuance. Tenant-local authorization still belongs in HEX IAM.
- OIDC authorization code flow with PKCE
- Local username/password login
- TOTP MFA for local accounts
- Persistent user consent with grant and deny flows
- Refresh tokens with rotation and replay rejection
- Audit logging with searchable endpoints
- Upstream OIDC federation for social and enterprise login
- Database-backed asymmetric signing keys with multi-key JWKS output
- Admin-authenticated upstream provider management
- SQLite for development and PostgreSQL for production
- SQL migrations via yoyo-migrations
Public OIDC and browser endpoints:
GET /.well-known/openid-configurationGET /oidc/jwksGET /oidc/authorizePOST /oidc/loginPOST /oidc/mfa/totpPOST /oidc/consent/grantPOST /oidc/consent/denyPOST /oidc/tokenPOST /oidc/logoutGET /oidc/userinfoGET /oidc/upstream/startGET /oidc/upstream/callback/{provider_id}
Admin endpoints:
POST /admin/loginPOST /admin/logoutGET /admin/providersPOST /admin/providersPUT /admin/providers/{provider_id}DELETE /admin/providers/{provider_id}GET /oidc/audit/logsGET /oidc/audit/logs/count
Development bootstrap endpoints, gated by ENABLE_DEV_ENDPOINTS=true:
POST /dev/usersPOST /dev/clientsPOST /dev/providersPOST /dev/mfa/totp/enroll
Local browser flow:
- Client starts at
/oidc/authorize - User signs in at
/oidc/login - If MFA is enabled, broker pauses at
/oidc/mfa/totp - If consent is missing or scope set changed, broker pauses at consent
- Broker returns an authorization code
- Client exchanges code at
/oidc/token
Refresh flow:
- Client sends
grant_type=refresh_token - Existing refresh token is validated
- Old token is revoked
- New access token, ID token, and refresh token are issued
Token signing stays behind the existing TokenSignerPort, but the concrete adapter is now asymmetric and database-backed.
- Default algorithm:
RS256 - Supported algorithms in the signer:
RS256,ES256 - Primary signing key is stored in
signing_keys - Old active keys remain available in JWKS until expiry
- JWKS now returns multiple keys
- Legacy PEM files are imported once if present, then signing moves to the database-backed key store
Relevant settings:
SIGNING_KEY_ALGORITHM=RS256SIGNING_KEY_ROTATION_DAYS=90
The broker can seed one admin user on startup:
ADMIN_BOOTSTRAP_USERNAMEADMIN_BOOTSTRAP_PASSWORDADMIN_BOOTSTRAP_PERMISSIONS=manage_providers,view_audit_logs
If bootstrap credentials are empty, no admin is seeded.
Important environment variables:
APP_BASE_URL=http://localhost:8000
ISSUER=http://localhost:8000
DATABASE_BACKEND=sqlite
POSTGRES_DSN=postgresql://postgres:postgres@localhost:5432/hexalgon_sso
DATA_DIR=.data
ACCESS_TOKEN_TTL_SECONDS=3600
ID_TOKEN_TTL_SECONDS=3600
REFRESH_TOKEN_TTL_SECONDS=2592000
SIGNING_KEY_ALGORITHM=RS256
SIGNING_KEY_ROTATION_DAYS=90
SESSION_COOKIE_NAME=hex_sso_session
ADMIN_SESSION_COOKIE_NAME=hex_sso_admin_session
ENABLE_DEV_ENDPOINTS=false
ENABLE_LOCAL_LOGIN=trueSecrets encrypted at rest:
- downstream OIDC client secrets
- upstream provider client secrets
- user TOTP secrets
- private signing keys stored in the database
Encryption uses Fernet with either:
ENCRYPTION_KEY, orDATA_DIR/enc.key
Install dependencies and apply migrations:
poetry install
poetry run python -m app.database.run_migrations apply
poetry run uvicorn app.main:app --reloadUseful test command:
poetry run pytest -qThe test harness now clears persistent SQLite test data between runs, so repeated local test execution should stay stable.