OIDC & Issuer Trust
KalamDB supports one external OpenID Connect provider per server process, configured under [auth.oidc].
Use this page when you want the Admin UI, the CLI, or direct bearer-token clients to authenticate through a standards-compliant OIDC issuer such as Dex, Keycloak, Okta, Auth0, Entra ID, Google, or Firebase.
There is no active [oauth] or [oauth.providers.*] matrix anymore. The current model is:
[auth.local]for local username/password login[auth.oidc]for one external OIDC providerauth.jwt_trusted_issuersfor the issuer allow-list used by bearer-token validation
Token Routing Model
KalamDB inspects every bearer token before signature verification to decide how to validate it:
Token alg | Token iss | Validation Path |
|---|---|---|
| HS256 | kalamdb | Internal: verified using auth.jwt_secret |
| RS256, RS384, RS512, PS256, PS384, PS512, ES256, ES384 | Any trusted issuer | External: verified via OIDC discovery + provider JWKS |
ES512 is not supported.
This means external tokens from supported OIDC providers are validated natively through the bearer-token pipeline.
server.toml Settings
Notes:
[auth]is the canonical section name.[authentication]still deserializes as a compatibility alias for local files, but new docs and examples use[auth].auth.oidc.issuerandauth.oidc.client_idare required whenauth.oidc.enabled = true.auth.oidc.scopesmust includeopenid.auth.oidc.audienceis optional and defaults toclient_idwhen omitted.jwt_trusted_issuersis a comma-separated string, not an array.- Keep
kalamdbinjwt_trusted_issuersso local KalamDB-issued tokens continue to verify. - Legacy
[oauth]and[oauth.providers.*]sections are no longer supported.
Environment Variable Overrides
Boolean env vars accept "true", "1", or "yes" (case-insensitive).
Environment variables override server.toml values.
OIDC Configuration Matrix
server.toml key | Environment variable | Required when OIDC enabled | Notes |
|---|---|---|---|
auth.jwt_secret | KALAMDB_JWT_SECRET | Yes | Used for KalamDB-issued HS256 access and refresh tokens |
auth.jwt_trusted_issuers | KALAMDB_JWT_TRUSTED_ISSUERS | Yes | Include kalamdb plus the configured OIDC issuer |
auth.local.enabled | KALAMDB_AUTH_LOCAL_ENABLED | No | Disable local username/password auth after OIDC migration if desired |
auth.local.bcrypt_cost | — | No | Password hash work factor for local users |
auth.local.min_password_length | — | No | Minimum accepted local password length |
auth.local.max_password_length | — | No | Maximum accepted local password length |
auth.local.enforce_password_complexity | — | No | Require uppercase, lowercase, digit, and special characters |
auth.oidc.enabled | KALAMDB_AUTH_OIDC_ENABLED | Yes | Enables the single external OIDC provider |
auth.oidc.display_name | KALAMDB_AUTH_OIDC_DISPLAY_NAME | No | Human-facing label in UI/CLI, for example Dex or Company SSO |
auth.oidc.issuer | KALAMDB_AUTH_OIDC_ISSUER | Yes | Must start with http:// or https:// |
auth.oidc.client_id | KALAMDB_AUTH_OIDC_CLIENT_ID | Yes | Used for browser and device flows and default audience validation |
auth.oidc.client_secret | KALAMDB_AUTH_OIDC_CLIENT_SECRET | No | Needed for confidential clients or some brokered device flows |
auth.oidc.scopes | KALAMDB_AUTH_OIDC_SCOPES | No | CSV env value; must include openid |
auth.oidc.device_authorization_endpoint | KALAMDB_AUTH_OIDC_DEVICE_AUTHORIZATION_ENDPOINT | No | Optional override when discovery omits device flow |
auth.oidc.broker_device_flow_enabled | KALAMDB_AUTH_OIDC_BROKER_DEVICE_FLOW_ENABLED | No | Enables KalamDB-brokered device flow endpoints |
auth.oidc.auto_provision | KALAMDB_AUTH_OIDC_AUTO_PROVISION | No | Allows absent regular OIDC subjects when default role is user; creates rows for elevated default roles |
auth.oidc.default_role | KALAMDB_AUTH_OIDC_DEFAULT_ROLE | No | Default role for auto-provisioned users |
auth.oidc.audience | KALAMDB_AUTH_OIDC_AUDIENCE | No | Explicit audience override; defaults to client_id |
Browser And Device Endpoints
Clients discover the current auth surface from:
KalamDB exposes these OIDC-facing endpoints:
| Endpoint | Used by | Purpose |
|---|---|---|
POST /v1/api/auth/oidc/exchange-code | Admin UI and CLI browser login | Exchange authorization code and PKCE verifier for KalamDB tokens |
POST /v1/api/auth/oidc/exchange-token | CLI direct device login | Exchange provider ID token for KalamDB tokens |
POST /v1/api/auth/oidc/device/start | CLI brokered device login | Start KalamDB-brokered device flow |
POST /v1/api/auth/oidc/device/poll | CLI brokered device login | Poll for brokered device completion |
Redirect URIs to register at the provider:
- Admin UI:
https://YOUR_KALAMDB_ORIGIN/ui/oauth/callback - CLI browser login:
http://127.0.0.1:8787/callback
OIDC Discovery Flow
When KalamDB encounters an external token (non-HS256):
- Extracts
algandkidfrom the JWT header (unverified). - Extracts
issfrom the JWT payload (unverified). - Confirms
issis injwt_trusted_issuers. - Fetches
{issuer_url}/.well-known/openid-configuration. - Extracts
jwks_urifrom the discovery response. - Fetches the JWKS endpoint and caches keys by
kid. - Matches the token
kidto a cached signing key. - Validates signature, issuer, expiry, and audience (if configured).
JWKS Caching Behavior
- Keys are cached per-issuer in memory, indexed by
kid. - Cache-miss refresh: if a token’s
kidis not found, KalamDB automatically re-fetches the JWKS endpoint. - No TTL-based eviction: cache refreshes only on miss — keys remain cached until an unknown
kidtriggers a refresh. - Keys without a
kidfield are silently discarded from the cache. - Key rotation at the provider is handled automatically (new
kid→ cache miss → refresh).
JWT Claims Structure
KalamDB reads these claims from external tokens:
| Claim | Type | Required | Notes |
|---|---|---|---|
sub | string | Yes | KalamDB user_id; must be a valid UserId |
iss | string | Yes | Must match a trusted issuer |
exp | number | Yes | Expiry (Unix timestamp, seconds) |
iat | number | Yes | Issued-at (Unix timestamp, seconds) |
username / preferred_username | string | No | Informational only; not used as the KalamDB user_id |
email | string | No | User email |
role | string | No | KalamDB role: user, service, dba, system |
token_type | string | No | "access" or "refresh" |
Deployment Checklist
- Confirm the exact issuer URL. Trailing slashes matter.
- Register the Admin UI and CLI redirect URIs at the provider.
- Set
auth.oidc.issuer,auth.oidc.client_id, andauth.jwt_trusted_issuers. - Keep
auth.local.enabled = trueduring bootstrap and migration. - Test
GET /v1/api/auth/login-options. - Test Admin UI browser login and at least one CLI OIDC mode.
- Disable local auth later only after OIDC access is verified.
Troubleshooting
| Symptom | Likely Cause | Fix |
|---|---|---|
401 Unauthorized on external token | Issuer not in jwt_trusted_issuers | Add exact issuer URL |
auth.oidc.issuer is required on startup | OIDC enabled without issuer | Set auth.oidc.issuer |
auth.oidc.client_id is required on startup | OIDC enabled without client ID | Set auth.oidc.client_id |
auth.oidc.scopes must include the 'openid' scope | Missing required scope | Add openid to auth.oidc.scopes |
MissingKid error | Token missing kid header | Provider must include kid in JWT header |
KeyNotFound after JWKS refresh | Key rotated and kid no longer present | Check provider JWKS, ensure key is published |
DiscoveryFailed | Cannot reach /.well-known/openid-configuration | Check network, DNS, and issuer URL format |
JwtValidationFailed: ExpiredSignature | Token expired | Get fresh token from provider |
| User not found after auth | auth.oidc.auto_provision is false or elevated roles are explicit | Enable auto-provision or pre-create the user with CREATE USER '<sub>' WITH OIDC ... |