Skip to Content
Getting StartedAuthentication & Bootstrap

Authentication & Bootstrap

This page covers the full path from first-time setup to production token usage, including Keycloak and Firebase integration.

For environment variables and server.toml options for issuer trust, see OIDC & Issuer Trust and Keycloak.

1. Auth Model

KalamDB uses:

  • username/password login at POST /v1/api/auth/login
  • JWT bearer tokens for SQL, files, topics, and WebSocket auth
  • role-based access (user, service, dba, system)

For most API routes, use:

Authorization: Bearer <access_token>

2. First-Time Server Setup

On a new server, check setup status first:

curl http://127.0.0.1:8080/v1/api/auth/status

When setup is required ("needs_setup": true), initialize root and first DBA:

curl -X POST http://127.0.0.1:8080/v1/api/auth/setup \ -H 'Content-Type: application/json' \ -d @- <<'JSON' { "username": "admin", "password": "AdminPass123!", "root_password": "RootPass123!", "email": "admin@example.com" } JSON

Important behavior:

  • setup is localhost-only unless auth.allow_remote_setup = true
  • setup does not return tokens; log in afterwards

3. Login And Get Tokens

curl -X POST http://127.0.0.1:8080/v1/api/auth/login \ -H 'Content-Type: application/json' \ -d '{"username":"admin","password":"AdminPass123!"}'

Response includes:

  • access_token
  • refresh_token
  • token expiry fields
  • user metadata

4. Refresh Token Flow

Use bearer or auth cookie:

curl -X POST http://127.0.0.1:8080/v1/api/auth/refresh \ -H "Authorization: Bearer <refresh_or_access_token>"

5. Use Token For SQL

curl -X POST http://127.0.0.1:8080/v1/api/sql \ -H "Authorization: Bearer <access_token>" \ -H 'Content-Type: application/json' \ -d '{"sql":"SELECT CURRENT_USER();"}'

6. Role Guidance

  • user: normal application users
  • service: background workers/automation
  • dba: database administration
  • system: highest-privilege internal operations

For worker services consuming topics, use service or higher.

7. External Identity Providers (Keycloak, Firebase, etc.)

How Bearer Token Routing Works

KalamDB inspects every incoming bearer token before signature verification:

  1. Extract alg from the JWT header (unverified).
  2. Extract iss from the JWT payload (unverified).
  3. Route to the correct validator:
    • HS256 with iss = "kalamdb" → internal shared-secret validation using auth.jwt_secret.
    • RS256, RS384, RS512, PS256, PS384, PS512, ES256, ES384 → external OIDC JWKS validation via issuer discovery.

This means KalamDB natively accepts tokens from external OIDC providers (Keycloak, Auth0, Google, Azure AD, etc.) without a bridge service, as long as:

  • The issuer is listed in auth.jwt_trusted_issuers.
  • The provider publishes a standard /.well-known/openid-configuration document.
  • The token is signed with one of the supported algorithms above.

Note: ES512 is not supported.

The simplest approach — KalamDB validates external tokens directly.

Step 1: Configure trusted issuer

[authentication] jwt_trusted_issuers = "https://keycloak.example.com/realms/myrealm" auto_create_users_from_provider = true

Or via environment variable:

export KALAMDB_JWT_TRUSTED_ISSUERS="https://keycloak.example.com/realms/myrealm" export KALAMDB_AUTH_AUTO_CREATE_USERS_FROM_PROVIDER=true

Keep kalamdb in the issuer list if you also use POST /v1/api/auth/login:

export KALAMDB_JWT_TRUSTED_ISSUERS="kalamdb,https://keycloak.example.com/realms/myrealm"

Step 2: Create mapped users in KalamDB

CREATE USER 'alice' WITH OAUTH '{"provider":"keycloak","subject":"keycloak-user-id"}' ROLE user EMAIL 'alice@example.com';

If auto_create_users_from_provider = true, users are auto-provisioned on first valid token.

Step 3: Use the provider token directly

# Get a token from your IdP (for example, Keycloak direct-access grant) KC_TOKEN=$(curl -s -X POST \ https://keycloak.example.com/realms/myrealm/protocol/openid-connect/token \ -d "client_id=my-app&grant_type=password&username=alice&password=secret" \ | jq -r .access_token) # Use it directly with KalamDB curl -X POST http://127.0.0.1:8080/v1/api/sql \ -H "Authorization: Bearer $KC_TOKEN" \ -H 'Content-Type: application/json' \ -d '{"sql":"SELECT CURRENT_USER();"}'

How OIDC Discovery Works

When KalamDB encounters an external token:

  1. Fetches {issuer_url}/.well-known/openid-configuration.
  2. Extracts the jwks_uri from the discovery document.
  3. Fetches the JWKS and caches keys by kid.
  4. Matches the token’s kid header to a cached key.
  5. On cache miss (unknown kid), automatically refreshes the JWKS cache.
  6. Validates signature, iss, exp, and aud (if client_id is configured).

JWKS caching: Keys are cached per-issuer in memory. There is no TTL-based eviction — cache refreshes only on kid miss. Keys without a kid field are discarded.

JWT Claims Used by KalamDB

ClaimTypeRequiredNotes
substringYesUser subject identifier
issstringYesMust match a trusted issuer
expnumberYesUnix timestamp (seconds)
iatnumberYesUnix timestamp (seconds)
username / preferred_usernamestringNoMaps to KalamDB username (serde alias)
emailstringNoUser email
rolestringNoKalamDB role (user, service, dba, system)
token_typestringNo"access" or "refresh"

For full OIDC configuration details, see OIDC & Issuer Trust. For Keycloak-specific setup, see Keycloak.

Option B: Token Exchange / Bridge Pattern

Use this when your IdP does not publish OIDC discovery, or you need custom claim mapping.

Bridge service validates provider tokens, then mints KalamDB-compatible HS256 tokens:

import { createRemoteJWKSet, jwtVerify } from 'jose'; import jwt from 'jsonwebtoken'; const keycloakJWKS = createRemoteJWKSet( new URL('https://keycloak.example.com/realms/myrealm/protocol/openid-connect/certs') ); async function exchangeKeycloakToken(idToken: string) { const { payload } = await jwtVerify(idToken, keycloakJWKS, { issuer: 'https://keycloak.example.com/realms/myrealm', audience: 'my-client-id', }); return jwt.sign( { sub: String(payload.sub), iss: 'kalamdb-keycloak-bridge', username: 'alice', role: 'user', email: payload.email, token_type: 'access', }, process.env.KALAMDB_JWT_SECRET!, { algorithm: 'HS256', expiresIn: '1h' } ); }

Firebase example (bridge required — Firebase does not use standard OIDC discovery):

import admin from 'firebase-admin'; import jwt from 'jsonwebtoken'; async function exchangeFirebaseToken(idToken: string) { const decoded = await admin.auth().verifyIdToken(idToken); return jwt.sign( { sub: decoded.uid, iss: 'kalamdb-firebase-bridge', username: 'mobile_alice', role: 'user', email: decoded.email, token_type: 'access', }, process.env.KALAMDB_JWT_SECRET!, { algorithm: 'HS256', expiresIn: '1h' } ); }

Use the exchanged token:

curl -X POST http://127.0.0.1:8080/v1/api/sql \ -H "Authorization: Bearer <exchanged_kalamdb_token>" \ -H 'Content-Type: application/json' \ -d '{"sql":"SELECT CURRENT_USER();"}'

The bridge issuer (e.g. kalamdb-keycloak-bridge) must appear in jwt_trusted_issuers.

8. Production Hardening

Before exposing KalamDB publicly:

  1. Set strong auth.jwt_secret (or KALAMDB_JWT_SECRET).
  2. Keep auth.allow_remote_setup = false.
  3. Set auth.cookie_secure = true behind HTTPS.
  4. Configure auth.jwt_trusted_issuers when using external IdPs.
  5. Tune rate_limit.max_auth_requests_per_ip_per_sec.

For full endpoint auth behavior, see HTTP API Reference.

Last updated on