Authentication
What you'll accomplish: Set up API key authentication or OIDC/SSO for your Hoziron deployment.
Authentication modes
Hoziron supports three modes, configured via [auth].mode:
| Mode | Use case |
|---|---|
disabled (default) | Local dev, single-user deployments |
local | API key-based auth with RBAC |
oidc | Enterprise SSO (Azure AD, Keycloak, Okta, Auth0) |
Local API key authentication
Enable
[auth]
mode = "local"
[auth.rate_limit]
base_backoff_secs = 1
max_backoff_secs = 300
max_failed_attempts = 10
Create your first key
When no keys exist yet, the first POST /auth/keys request is allowed without authentication (bootstrap bypass):
hoziron auth create-key --role admin --name "platform-admin"
The key is shown once — save it securely. Format: hzk_Ab3xYz...
Manage keys
hoziron auth create-key --role operator --name "ci-bot" --expires-in 90d
hoziron auth list-keys
hoziron auth revoke-key <key-id>
hoziron auth rotate-key <key-id>
How it works
Key security details:
- Keys are 32 bytes of cryptographic randomness, base64url-encoded with
hzk_prefix - Only argon2id hashes are stored — the raw key is never persisted
- Validation uses constant-time comparison across all keys (prevents timing attacks)
- The last admin key cannot be revoked (prevents lockout)
OIDC / SSO configuration
For enterprise SSO with Azure AD, Keycloak, Okta, or Auth0:
[auth]
mode = "oidc"
allow_local_service_keys = true # API keys alongside OIDC for CI/CD
[auth.oidc]
issuer = "https://login.microsoftonline.com/{tenant}/v2.0"
audience = "api://hoziron-platform"
jwks_uri = "" # Auto-discovered if blank
role_claim = "roles" # Supports dot-path: "realm_access.roles"
allowed_algorithms = ["RS256", "ES256"]
jwks_cache_ttl_secs = 3600
[auth.oidc.role_mapping]
"HozironAdmins" = "admin"
"PlatformOps" = "operator"
"Developers" = "developer"
"ReadOnly" = "viewer"
Role mapping
The role_claim field supports dot-path traversal for nested claims (Keycloak pattern):
role_claim = "realm_access.roles"
When multiple IdP roles match, highest-privilege wins (admin > operator > developer > viewer > service).
Hybrid mode
With allow_local_service_keys = true, OIDC falls back to local API key validation when JWT validation fails. This enables:
- CI/CD pipelines using API keys alongside human SSO
- Break-glass access for emergencies
Brute force protection
Per-IP exponential backoff on failed attempts:
| Parameter | Default | Config key |
|---|---|---|
| Base backoff | 1 second | auth.rate_limit.base_backoff_secs |
| Max backoff | 300 seconds (5 min) | auth.rate_limit.max_backoff_secs |
| Max failures tracked | 10 | auth.rate_limit.max_failed_attempts |
Rate limit state is in-memory, per-IP, cleared on successful auth.
Endpoints always accessible
Regardless of auth configuration:
| Endpoint | Reason |
|---|---|
GET /health | Orchestrator probes must always work |
GET /metrics | Prometheus scraping |
POST /auth/keys (first key only) | Bootstrap — create first key |
Next steps
Related: