Skip to content

Latest commit

 

History

History
326 lines (230 loc) · 8.96 KB

File metadata and controls

326 lines (230 loc) · 8.96 KB

Authentication

Manage users, API keys, JWT tokens, and role-based access control.

Overview

duh supports two authentication methods:

  1. JWT Bearer tokens -- for users who log in with email and password
  2. API keys -- for programmatic access via the X-API-Key header

Both methods are checked by the API middleware. If no API keys exist in the database and no JWT is provided, the API runs in open mode (no authentication required).

User management

Register a user (API)

curl -X POST http://localhost:8080/api/auth/register \
  -H "Content-Type: application/json" \
  -d '{
    "email": "alice@example.com",
    "password": "strong-password-here",
    "display_name": "Alice"
  }'

Response:

{
  "access_token": "eyJhbGciOiJIUzI1NiIs...",
  "token_type": "bearer",
  "user_id": "a1b2c3d4-...",
  "role": "contributor"
}

New users are assigned the contributor role by default. Registration can be disabled in config after your initial users are created.

!!! warning "Disable registration in production" After creating your admin user, set registration_enabled = false in config.toml to prevent unauthorized signups.

Create a user (CLI)

The CLI lets you create users with a specific role, including admin:

duh user-create \
  --email admin@example.com \
  --password 'strong-password' \
  --name "Admin User" \
  --role admin

Available roles: admin, contributor, viewer.

List users (CLI)

duh user-list

Output:

  a1b2c3d4  admin@example.com  Admin User  role=admin  active
  e5f6a7b8  alice@example.com  Alice       role=contributor  active

Log in

curl -X POST http://localhost:8080/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "email": "alice@example.com",
    "password": "strong-password-here"
  }'

Response:

{
  "access_token": "eyJhbGciOiJIUzI1NiIs...",
  "token_type": "bearer",
  "user_id": "a1b2c3d4-...",
  "role": "contributor"
}

Get current user

curl http://localhost:8080/api/auth/me \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

Response:

{
  "id": "a1b2c3d4-...",
  "email": "alice@example.com",
  "display_name": "Alice",
  "role": "contributor",
  "is_active": true
}

JWT tokens

Using tokens

Include the token in the Authorization header:

curl http://localhost:8080/api/threads \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

Token details

  • Algorithm: HS256
  • Payload: sub (user ID), exp (expiry), iat (issued at)
  • Default expiry: 24 hours (configurable)

Tokens are validated on every request by the API key middleware. An expired or invalid token returns HTTP 401.

Token expiry configuration

[auth]
token_expiry_hours = 24

Set a shorter expiry for higher security. Users will need to call /api/auth/login again after the token expires.

API keys

API keys provide a simpler authentication method for scripts and integrations. They are passed via the X-API-Key header.

Using API keys

curl http://localhost:8080/api/threads \
  -H "X-API-Key: duh_abc123..."

How API keys work

  • Keys are stored as SHA-256 hashes in the database (the raw key is never stored)
  • Keys can be revoked by setting a revoked_at timestamp
  • Keys can optionally be linked to a user via user_id

!!! note "API key CLI" API key management is available through the database. A dedicated duh key create CLI command is planned for a future release.

Exempt paths

The following paths do not require authentication:

Path Purpose
/api/health Basic health check
/api/health/detailed Detailed health check
/api/metrics Prometheus metrics
/api/auth/register User registration
/api/auth/login User login
/docs OpenAPI documentation
/openapi.json OpenAPI spec
/redoc ReDoc documentation
/api/share/* Shared content

All other /api/ and /ws/ paths require either a JWT token or API key.

Roles and RBAC

duh uses a hierarchical role system: admin > contributor > viewer.

Role permissions

Capability Viewer Contributor Admin
Read threads and decisions Yes Yes Yes
Create consensus queries No Yes Yes
Create threads No Yes Yes
Manage users No No Yes
Full API access No No Yes

How RBAC works

Endpoints use the require_role dependency to enforce minimum role levels:

  • require_viewer -- any authenticated user
  • require_contributor -- contributors and admins
  • require_admin -- admins only

A user with a higher role automatically passes lower role checks. For example, an admin can access all contributor endpoints.

Example: role-protected requests

As a viewer (read-only access):

# List threads -- works for viewers
curl http://localhost:8080/api/threads \
  -H "Authorization: Bearer $VIEWER_TOKEN"

# Create a query -- fails with 403
curl -X POST http://localhost:8080/api/ask \
  -H "Authorization: Bearer $VIEWER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"question": "test"}'
# {"detail": "Requires contributor role"}

As a contributor (create and view):

# Create a consensus query -- works for contributors
curl -X POST http://localhost:8080/api/ask \
  -H "Authorization: Bearer $CONTRIBUTOR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"question": "What are the trade-offs of microservices?"}'

As an admin (full access):

# List users -- admin only
curl http://localhost:8080/api/auth/me \
  -H "Authorization: Bearer $ADMIN_TOKEN"

Configuration

All authentication settings live in the [auth] section of config.toml:

[auth]
jwt_secret = ""                # REQUIRED in production -- set via env or config
token_expiry_hours = 24        # how long JWT tokens remain valid
registration_enabled = true    # set to false after creating your admin user

Environment variable override

Set DUH_JWT_SECRET as an environment variable instead of putting it in the config file:

export DUH_JWT_SECRET=$(openssl rand -hex 32)

!!! warning "Never commit your JWT secret" Use environment variables or a secrets manager for the JWT secret. Never check it into version control.

Rate limiting

Rate limits apply per identity. The middleware identifies callers in this priority order:

  1. User ID (from JWT token)
  2. API key ID (from X-API-Key header)
  3. IP address (fallback)

Configure rate limits in config.toml:

[api]
rate_limit = 60          # requests per minute
rate_limit_window = 60   # window in seconds

When the limit is exceeded, the API returns HTTP 429 with a Retry-After header.

Every response includes rate limit headers:

X-RateLimit-Limit: 60
X-RateLimit-Remaining: 57
X-RateLimit-Key: user:a1b2c3d4-...

Web UI Authentication

The web UI integrates with the backend authentication system. It detects whether auth is required and adapts accordingly.

Dev mode (no auth required)

When no API keys or users exist in the database, the API runs in open mode. The web UI detects this via GET /api/auth/status and automatically logs in as a guest user. No login page is shown.

This is the default behavior when you first run duh serve -- you can start using the web UI immediately without setting up users.

Production mode (auth required)

Once you create a user or API key, the web UI requires authentication:

  1. Redirect to login: All routes except /share/:id redirect to /login if the user is not authenticated
  2. Login form: Enter email and password to receive a JWT token
  3. Token storage: The JWT token is stored in localStorage (key: duh_token)
  4. Auto-injection: The API client automatically includes the token in all requests via the Authorization: Bearer header
  5. WebSocket auth: The token is included in the initial WebSocket handshake message
  6. Session expiry: On 401 responses, the stored token is cleared and the user is redirected to login

User menu

When authenticated, the top bar shows the user's display name and role badge. Clicking it reveals a dropdown with the user's email and a sign-out button.

Registration

The login page includes a toggle to switch between "Sign In" and "Create Account" modes. Registration can be disabled server-side by setting registration_enabled = false in config.toml.

Security recommendations

  1. Generate a strong JWT secret: openssl rand -hex 32
  2. Disable registration after creating your first admin user
  3. Use HTTPS -- never expose the API over plain HTTP
  4. Rotate API keys periodically and revoke unused ones
  5. Set short token expiry for high-security environments
  6. Restrict CORS origins to your actual domain

Next steps