NodeDB integrates with external OIDC providers for JWT-based bearer authentication. Users authenticate once with an identity provider and receive a token usable across NodeDB without managing separate passwords.
OIDC is a delegated authentication protocol where:
- User authenticates with an external provider (Auth0, Okta, Keycloak, etc.)
- Provider issues a JWT bearer token
- User presents token to NodeDB in the
Authorization: Bearer <token>header - NodeDB validates the signature via the provider's JWKS, applies claim-mapping rules, and grants access
Supported on: Native protocol and HTTP only. Not on pgwire — the Postgres wire protocol cannot carry bearer tokens without a non-standard extension; pgwire stays SCRAM-SHA-256 only.
Create a provider configuration:
CREATE OIDC PROVIDER okta WITH (
issuer = 'https://dev-12345.okta.com/',
jwks_url = 'https://dev-12345.okta.com/.well-known/jwks.json',
audience = 'api://nodedb'
);Parameters:
| Parameter | Required | Description |
|---|---|---|
issuer |
Yes | Provider's issuer URL (e.g. https://accounts.google.com, https://your-domain.auth0.com/) |
jwks_url |
Yes | JWKS endpoint for signature validation (e.g. https://accounts.google.com/.well-known/jwks.json) |
audience |
Yes | Expected aud claim in the JWT (matches your app's audience with the provider) |
Permissions: CREATE OIDC PROVIDER requires Superuser or ClusterAdmin role.
Persistence: Provider config is stored in _system.oidc_providers and replicated via Raft.
Map JWT claims to NodeDB identity attributes. Define rules in the provider configuration:
CREATE OIDC PROVIDER okta WITH (
issuer = 'https://dev-12345.okta.com/',
jwks_url = 'https://dev-12345.okta.com/.well-known/jwks.json',
audience = 'api://nodedb',
claim_mapping = [
{ claim = 'email', value = 'alice@company.com', effect = { default_database = 'prod', add_databases = ['prod', 'staging'] } },
{ claim = 'email', value = 'bob@company.com', effect = { default_database = 'staging', add_databases = ['staging'] } },
{ claim = 'department', value = 'engineering', effect = { add_databases = ['prod', 'staging', 'dev'], add_roles = ['DatabaseEditor', 'ClusterAdmin'] } }
]
);Rule structure:
{ claim = <claim_name>, value = <claim_value>, effect = {
default_database = <DatabaseId | null>,
add_databases = [<DatabaseId>, ...],
add_roles = [<Role>, ...]
} }
How it works:
- If JWT contains claim
claim_namewith value matchingvalue, apply theeffect default_databasesets the user's default database for the sessionadd_databasesadds the databases to the user's accessible setadd_rolesadds roles to the authenticated identity- Multiple rules are OR-combined: if any rule matches, its effect applies
- Claim values support wildcards (
*) for matching any value of a claim
Example: wildcard for department
claim_mapping = [
{ claim = 'department', value = '*', effect = { add_databases = ['logging'] } }
]All users with a department claim get access to the logging database.
Modify claim mapping or issuer details:
ALTER OIDC PROVIDER okta SET (
claim_mapping = [
{ claim = 'email', value = 'alice@company.com', effect = { add_databases = ['analytics'] } }
]
);Changes take effect immediately for new authentications. Existing sessions retain their identity until the next request (same as role-change propagation).
View all configured providers:
SHOW OIDC PROVIDERS;Output columns:
| Column | Type | Description |
|---|---|---|
provider_name |
String | Name (e.g. 'okta') |
issuer |
String | Issuer URL |
jwks_url |
String | JWKS endpoint |
audience |
String | Expected audience claim |
claim_mapping_count |
i32 | Number of claim rules |
created_at |
Timestamp | Registration date |
DROP OIDC PROVIDER okta;Permissions: Superuser or ClusterAdmin.
Effect: Existing sessions tied to this provider are revoked at their next request boundary. New OIDC logins via this provider are rejected.
NodeDB caches JWKS locally to avoid repeated network roundtrips:
Cache behavior:
- Fetch on startup: When a provider is registered, JWKS is fetched once
- Refresh on
kidmiss: If a token'skid(key ID) is not in the cache, refresh the JWKS - TTL expiry: Cache expires after 1 hour; next validation triggers refresh
- Circuit breaker: If the provider is unreachable, use cached JWKS for up to 24 hours
Explicit reload:
ALTER OIDC PROVIDER okta SET RELOAD_JWKS;- Decode JWT header (check
alg,kid) - Look up provider by
issclaim - Fetch/cache JWKS from provider's
jwks_url - Validate signature using the key with matching
kid - Check
audclaim matches provider's configured audience - Check
exp(expiry) not in the past - Apply claim mapping rules
- Build ephemeral
AuthenticatedIdentity
Failure mode: Any step failure returns INVALID_CREDENTIALS (no detail leak).
Claim-mapping operations require Superuser or ClusterAdmin. Regular users cannot list or modify OIDC providers.
-- Regular user attempt
ALTER OIDC PROVIDER okta SET claim_mapping = [...];
-- Error: INSUFFICIENT_PRIVILEGE1. Provider registration (admin)
CREATE OIDC PROVIDER auth0 WITH (
issuer = 'https://your-domain.auth0.com/',
jwks_url = 'https://your-domain.auth0.com/.well-known/jwks.json',
audience = 'nodedb-api'
);2. Get token from provider
# Via Auth0's token endpoint
curl -X POST https://your-domain.auth0.com/oauth/token \
-H 'content-type: application/json' \
-d '{
"client_id": "your-client-id",
"client_secret": "your-client-secret",
"audience": "nodedb-api",
"grant_type": "client_credentials"
}'
# Returns: { "access_token": "eyJhbGc..." }3. Connect to NodeDB with token
Native protocol:
let identity = AuthenticationMethod::OidcBearer {
token: "eyJhbGc...".to_string(),
provider: "auth0".to_string(),
};
let session = client.authenticate(identity).await?;HTTP:
curl -H "Authorization: Bearer eyJhbGc..." \
http://localhost:6480/v1/query \
-d '{"sql": "SELECT * FROM users"}'4. Session bound to database(s) from claims
User's token contains sub: alice@company.com. If claim mapping includes:
{ claim = 'sub', value = 'alice@company.com', effect = { add_databases = ['prod'] } }
Then Alice's session is bound to the prod database. She cannot query other databases.
| Feature | Password (SCRAM) | OIDC Bearer |
|---|---|---|
| Credential storage | NodeDB (hashed) | External provider |
| Password change | ALTER USER … SET PASSWORD |
Provider's self-service |
| MFA | N/A | Provider-enforced |
| Session lifetime | Connection lifetime | Token expiry (usually 1h) |
| Refresh | Reconnect only | Token refresh endpoint |
| Protocol | pgwire, HTTP, native | HTTP, native only |
| Use case | Internal teams | Federated (enterprise, SaaS) |
OIDC authentication events appear in the audit log:
SHOW AUDIT WHERE event_type = 'AuthSuccess' AND provider = 'okta';Audit entry includes:
| Field | Value |
|---|---|
event_type |
AuthSuccess |
provider |
OIDC provider name |
jwt_subject |
JWT sub claim (e.g. user's email) |
auth_method |
OidcBearer |
timestamp |
Login time |
Failed OIDC attempts:
SHOW AUDIT WHERE event_type = 'AuthFailure' AND auth_method = 'OidcBearer';
-- Returns: invalid signature, missing kid, expired token, audience mismatch, etc.