Authentication

Secure your Rustberg deployment with multiple authentication methods.

Table of contents

  1. Overview
  2. API Key Authentication
    1. How It Works
    2. Security Properties
    3. Creating API Keys
      1. Programmatic (Recommended)
      2. CLI Tool
    4. Using API Keys
    5. Persistent API Key Storage
  3. JWT Authentication
    1. JWKS Configuration
    2. Token Requirements
    3. Example JWT Payload
    4. Provider Examples
      1. Auth0
      2. Keycloak
      3. Okta
  4. Chain Authentication
    1. Configuration
  5. Rate Limiting
    1. Per-Tenant Limits
  6. Security Best Practices
    1. API Keys
    2. JWT
    3. TLS
  7. Audit Logging
    1. Failed Authentication
  8. Troubleshooting
    1. “401 Unauthorized”
    2. “403 Forbidden”
    3. JWT Validation Failures
  9. Next Steps

Overview

Rustberg supports multiple authentication mechanisms:

Method Use Case Production Ready
API Keys Service-to-service, CI/CD ✅ Recommended
JWT/OIDC User authentication, SSO ✅ Ready
Chain Auth JWT with API Key fallback ✅ Ready

All authentication is required by default. Anonymous access must be explicitly enabled (not recommended).


API Key Authentication

How It Works

  1. Server generates API key with cryptographically secure random bytes
  2. Key is hashed using Argon2id (PHC winner, OWASP recommended)
  3. Client includes key in Authorization: Bearer <key> header
  4. Server verifies using constant-time comparison

Security Properties

Property Implementation
Hashing Argon2id (19 MiB memory, 2 iterations)
Timing Constant-time verification
Storage Prefix-indexed, PHC format hash
Leakage Dummy hash prevents user enumeration

Creating API Keys

use rustberg::auth::ApiKeyStore;

// Create store (in-memory or persistent)
let store = ApiKeyStore::new();

// Generate new API key
let (key_id, plaintext) = store.create_key(
    "spark-etl",           // name
    Some("tenant-123"),    // tenant_id
    vec!["data-writer"],   // roles
).await?;

// plaintext = "rustberg_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
// Store this securely! It cannot be retrieved later.

CLI Tool

# Generate API key (outputs to stdout)
./rustberg generate-key \
    --name "spark-etl" \
    --tenant "tenant-123" \
    --roles "data-writer,data-reader"

Using API Keys

# HTTP Header
curl -H "Authorization: Bearer rustberg_xxxxx" \
     http://localhost:8181/v1/namespaces

# Query parameter (not recommended)
curl "http://localhost:8181/v1/namespaces?token=rustberg_xxxxx"

Persistent API Key Storage

By default, API keys are stored in memory and lost on restart. For production:

use rustberg::App;

// Enable persistent API key storage
let (app, store) = App::builder()
    .with_storage_backend("s3://bucket/catalog")
    .with_encryption_key(encryption_key)  // Optional: encrypt at rest
    .build_with_persistent_api_key_auth_async()
    .await;

Or via configuration:

[storage]
object_store_url = "s3://bucket/catalog"

[auth]
persistent_api_keys = true
encryption_key_env = "RUSTBERG_MASTER_KEY"

JWT Authentication

JWKS Configuration

Rustberg validates JWTs against a JWKS (JSON Web Key Set) endpoint:

[auth.jwt]
jwks_url = "https://auth.example.com/.well-known/jwks.json"
issuer = "https://auth.example.com"
audience = "rustberg-catalog"

Token Requirements

Claim Required Description
iss Must match configured issuer
aud Must match configured audience
exp Token expiration time
sub User/service identifier
tenant_id ⚠️ Required for multi-tenant
roles ⚠️ Optional, for RBAC

Example JWT Payload

{
  "iss": "https://auth.example.com",
  "aud": "rustberg-catalog",
  "sub": "user@example.com",
  "exp": 1706313600,
  "tenant_id": "tenant-123",
  "roles": ["data-reader", "data-writer"]
}

Provider Examples

Auth0

[auth.jwt]
jwks_url = "https://your-tenant.auth0.com/.well-known/jwks.json"
issuer = "https://your-tenant.auth0.com/"
audience = "rustberg-api"

Keycloak

[auth.jwt]
jwks_url = "https://keycloak.example.com/realms/myrealm/protocol/openid-connect/certs"
issuer = "https://keycloak.example.com/realms/myrealm"
audience = "rustberg"

Okta

[auth.jwt]
jwks_url = "https://your-org.okta.com/oauth2/default/v1/keys"
issuer = "https://your-org.okta.com/oauth2/default"
audience = "rustberg"

Chain Authentication

Chain authentication tries multiple methods in order:

  1. JWT - Check Authorization: Bearer <jwt> header
  2. API Key - Fall back to Authorization: Bearer <api_key>

This enables:

  • User authentication via SSO (JWT)
  • Service accounts via API keys
  • Gradual migration between methods

Configuration

[auth]
chain_auth = true

[auth.jwt]
jwks_url = "https://auth.example.com/.well-known/jwks.json"
issuer = "https://auth.example.com"
audience = "rustberg"

[auth.api_key]
enabled = true

Rate Limiting

Protect against abuse with token bucket rate limiting:

[rate_limit]
enabled = true
requests_per_second = 100
burst_size = 200
trust_proxy_headers = false  # Set true behind load balancer

Per-Tenant Limits

[rate_limit]
enabled = true
default_rps = 100

[rate_limit.tenants]
"tenant-premium" = 1000
"tenant-basic" = 50

Security Best Practices

API Keys

  • Never commit API keys to source control
  • Rotate keys periodically (90 days recommended)
  • Use separate keys per service/environment
  • Monitor key usage via audit logs

JWT

  • Validate all claims (iss, aud, exp)
  • Use short-lived tokens (15 minutes recommended)
  • Enable token refresh flows
  • Monitor for unusual token patterns

TLS

Always use TLS in production. Rustberg warns when running without TLS.

# Enable TLS (recommended)
./rustberg --tls-cert /path/to/cert.pem --tls-key /path/to/key.pem

# Self-signed for development
./rustberg generate-cert --common-name localhost
./rustberg --tls-cert ./cert.pem --tls-key ./key.pem

Audit Logging

All authentication decisions are logged:

{
  "timestamp": "2026-01-24T12:00:00Z",
  "event": "auth_success",
  "principal": "spark-etl",
  "tenant_id": "tenant-123",
  "method": "api_key",
  "ip": "10.0.0.5",
  "request_id": "abc123"
}

Failed Authentication

{
  "timestamp": "2026-01-24T12:00:01Z",
  "event": "auth_failure",
  "reason": "invalid_token",
  "method": "jwt",
  "ip": "10.0.0.6",
  "request_id": "def456"
}

Secrets (tokens, keys) are never logged.


Troubleshooting

“401 Unauthorized”

  1. Verify the API key/JWT is correct
  2. Check the Authorization header format: Bearer <token>
  3. Ensure the token hasn’t expired
  4. Verify JWKS URL is accessible

“403 Forbidden”

Authentication succeeded but authorization failed:

  1. Check tenant isolation (correct tenant_id?)
  2. Verify roles/permissions in Cedar policies
  3. Check audit logs for the specific denial reason

JWT Validation Failures

# Debug JWT claims
echo 'eyJhbGc...' | cut -d. -f2 | base64 -d | jq

# Test JWKS endpoint
curl https://auth.example.com/.well-known/jwks.json | jq

Next Steps