Skip to main content
Version: 0.1.0

Authentication Setup

RaisinDB provides a pluggable authentication system where each tenant can mix and match authentication strategies — local passwords, magic links, OIDC providers, API keys — while sharing a unified session and token infrastructure.

Architecture Overview

┌─────────────┐  ┌─────────────┐  ┌─────────────┐
│ Local │ │ OIDC │ │ Magic Link │ ...
│ Strategy │ │ Strategy │ │ Strategy │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
└────────────────┼────────────────┘

┌───────────────────────┐
│ AuthStrategyRegistry │
└───────────┬───────────┘

┌───────────────────────┐
│ AuthService │
│ (JWT + Sessions) │
└───────────────────────┘

Every authentication attempt follows the same flow:

  1. The transport layer receives credentials and wraps them in an AuthCredentials variant
  2. The AuthStrategyRegistry routes the credentials to the matching strategy
  3. The strategy validates the credentials and returns a verified Identity
  4. AuthService creates or updates a Session, then issues JWT access and refresh tokens

Adding a new authentication method requires only a new strategy implementation — the session, token, and middleware infrastructure stays untouched.

Available Strategies

StrategyUse CaseCredential Type
LocalTraditional email + password loginUsernamePassword
Magic LinkPasswordless email authenticationMagicLinkToken
OIDCGoogle, Okta, Keycloak, Azure ADOAuth2Code
API KeyMachine-to-machine accessApiKey
One-Time TokenEmail verification, password reset, invitationsOneTimeToken

Local Strategy (Username + Password)

The local strategy authenticates users with email and password, using bcrypt for password hashing.

Setup

Local authentication is available by default. Users register via the REST API:

POST /auth/register
Content-Type: application/json

{
"email": "alice@example.com",
"password": "SecureP@ssw0rd!",
"display_name": "Alice"
}

Login

POST /auth/login
Content-Type: application/json

{
"email": "alice@example.com",
"password": "SecureP@ssw0rd!"
}

Response:

{
"access_token": "eyJhbG...",
"refresh_token": "eyJhbG...",
"token_type": "Bearer",
"expires_in": 3600
}

Password Policy

Configure password requirements per tenant:

password_policy:
min_length: 8
max_length: 128
require_uppercase: true
require_lowercase: true
require_digit: true
require_special: true

Passwordless authentication via email. The user requests a magic link, receives it by email, and clicking the link completes authentication without setting a password.

Setup

Enable magic links in your tenant configuration:

magic_link:
enabled: true
expiration_minutes: 15

Flow

# 1. Request a magic link
POST /auth/magic-link
{ "email": "alice@example.com" }

# 2. User receives email with link containing a one-time token
# 3. User clicks link, which hits the verify endpoint
GET /auth/magic-link/verify?token=<one-time-token>

# 4. Response includes access + refresh tokens

OIDC Strategy (Google, Okta, Azure AD, Keycloak)

OpenID Connect integration supports enterprise identity providers.

Supported Providers

  • Google — Google Workspace and consumer accounts
  • Okta — Enterprise identity provider
  • Keycloak — Open-source identity management
  • Azure AD — Microsoft identity platform

Configuration

Configure OIDC providers in your tenant settings:

oidc:
providers:
google:
client_id: "your-google-client-id"
client_secret: "your-google-client-secret"
redirect_uri: "https://yourapp.com/auth/oidc/google/callback"

okta:
client_id: "your-okta-client-id"
client_secret: "your-okta-client-secret"
issuer: "https://your-org.okta.com"
redirect_uri: "https://yourapp.com/auth/oidc/okta/callback"

azure:
client_id: "your-azure-client-id"
client_secret: "your-azure-client-secret"
tenant_id: "your-azure-tenant-id"
redirect_uri: "https://yourapp.com/auth/oidc/azure/callback"

keycloak:
client_id: "your-keycloak-client-id"
client_secret: "your-keycloak-client-secret"
issuer: "https://keycloak.yourorg.com/realms/yourrealm"
redirect_uri: "https://yourapp.com/auth/oidc/keycloak/callback"

Flow

# 1. Start the OIDC flow (redirects user to provider)
GET /auth/oidc/google

# 2. User authenticates with Google
# 3. Google redirects back to your callback URL
GET /auth/oidc/google/callback?code=<auth-code>&state=<state>

# 4. RaisinDB exchanges the code for tokens, resolves identity
# 5. Response includes access + refresh tokens

On successful callback, RaisinDB resolves the provider's user ID to a local Identity. If no matching identity exists, a new one is created and linked to the provider.

Identity Linking

A single identity can be linked to multiple providers. For example, a user might authenticate with both Google OIDC and a local password — both resolve to the same identity.

Each link is tracked by provider and external ID using a namespaced format (oidc:google, oidc:okta) so multiple OIDC providers coexist without collision.

API Key Strategy

Long-lived credentials for machine-to-machine access. API keys bypass the interactive login flow, making them suitable for CI/CD pipelines, scripts, and service integrations.

# Authenticate with an API key
POST /auth/login
{
"api_key": "raisin_key_abc123..."
}

One-Time Token Strategy

Short-lived, single-use tokens for specific purposes:

  • Email verification — confirm a user's email address
  • Password reset — allow a user to set a new password
  • Workspace invitations — invite a user to join a workspace

These tokens expire quickly and can only be used once.

Session Management

Every successful authentication creates a server-side session. Sessions are the ground truth for whether a user is logged in — even if a JWT hasn't expired, the middleware checks that the session hasn't been revoked.

JWT Token Pair

Authentication produces two tokens:

TokenLifetimePurpose
Access token1 hourShort-lived JWT for API requests. Validated on every request.
Refresh token30 daysLong-lived token for obtaining new access tokens without re-authentication.

Token Claims

The access token carries:

ClaimPurpose
subIdentity ID
emailUser's email
tenant_idTenant scope
sidSession ID
auth_strategyWhich strategy produced this token (local, oidc:google, etc.)
auth_timeWhen the user last actively authenticated (for sudo mode)
global_flagsTenant-wide flags (is_tenant_admin, email_verified, must_change_password)
homeUser's home path in the repository

Workspace permissions are not stored in the JWT. They are resolved per-request via an LRU cache, keeping tokens small and permissions always fresh. See Roles and Permissions.

Refresh Token Rotation

By default, each use of a refresh token issues a new refresh token and invalidates the old one. If a previously-used refresh token is presented again (indicating theft), the entire token family is revoked, forcing re-authentication on all devices.

# Refresh an access token
POST /auth/refresh
{ "refresh_token": "eyJhbG..." }

Session Limits

The max_sessions_per_user setting (default: 10) caps concurrent sessions. When the limit is reached, the oldest session is revoked.

Listing and Revoking Sessions

# List all sessions for the current user
GET /auth/sessions

# Revoke a specific session
DELETE /auth/sessions/{session_id}

# Logout (revoke current session)
POST /auth/logout

Tenant-Level Configuration

Each tenant configures authentication independently. The full configuration:

auth:
session_duration_hours: 24
refresh_token_duration_days: 30
max_sessions_per_user: 10
sudo_threshold_seconds: 300
rotate_refresh_tokens: true
revoke_on_reuse_detection: true
audit_enabled: true
anonymous_enabled: false

password_policy:
min_length: 8
max_length: 128
require_uppercase: true
require_lowercase: true
require_digit: true
require_special: true

magic_link:
enabled: true
expiration_minutes: 15

rate_limiting:
max_attempts_per_minute: 10
lockout_duration_minutes: 30
lockout_threshold: 5

Rate Limiting

Authentication attempts are rate-limited per tenant to prevent brute-force attacks:

  • max_attempts_per_minute — throttle for authentication attempts
  • lockout_threshold — number of failures before account lockout
  • lockout_duration_minutes — how long the lockout lasts

Auth Middleware

The HTTP transport uses two middleware variants:

  • require_auth — validates the JWT, checks the session is active, injects auth claims. Returns 401 if unauthenticated.
  • optional_auth — same validation when a token is present, but allows anonymous access if no token is provided. Used for public content endpoints.

Both support dual JWT validation — accepting tokens signed by either the tenant's user key or the admin key.

Admin Impersonation

Admins can impersonate users via the X-Raisin-Impersonate header for debugging. Impersonation tokens record the original admin's identity for audit purposes.

API Reference

MethodPathDescription
POST/auth/providersList available auth providers
POST/auth/registerRegister with local credentials
POST/auth/loginAuthenticate with credentials
POST/auth/magic-linkRequest a magic link
GET/auth/magic-link/verifyVerify a magic link token
GET/auth/oidc/{provider}Start OIDC flow
GET/auth/oidc/{provider}/callbackHandle OIDC callback
POST/auth/refreshRefresh an access token
POST/auth/logoutLogout and revoke session
GET/auth/sessionsList user sessions
DELETE/auth/sessions/{id}Revoke a session
GET/auth/meGet current identity

Next Steps