Skip to main content

Overview

Codex-LB supports multi-layered authentication for the admin dashboard:
  • Password authentication - bcrypt-hashed password stored in database
  • TOTP (Time-based One-Time Password) - Optional 2FA using authenticator apps
  • Session management - 12-hour encrypted session cookies
  • Rate limiting - Protection against brute-force attacks
By default, authentication is disabled for easy first-time setup. Once configured, all /api/* endpoints (except auth endpoints) require valid authentication.
API key authentication (for proxy endpoints) and dashboard authentication (for admin endpoints) are separate systems. See API Keys for proxy authentication.

Password Authentication

Initial Setup

On first launch, the dashboard is open (no authentication required). Set up password protection:
  1. Navigate to SettingsSecurity
  2. Click Setup Password
  3. Enter a strong password (minimum 8 characters)
  4. Click Save
From app/modules/dashboard_auth/service.py:210-213:
Password is hashed using bcrypt:
Password setup is a one-time operation. Once configured, you must use the password change flow to update it.

Logging In

Once password is configured, the dashboard requires authentication:
  1. Navigate to dashboard URL
  2. Enter password in login form
  3. Click Sign In
  4. Receive 12-hour session cookie
API endpoint:
Response:
Sets cookie: codex_lb_dashboard_session (httponly, secure, samesite=lax, 12h expiry)

Session Management

Sessions are encrypted using Fernet (symmetric encryption):
Session payload:
  • exp: Expiration timestamp
  • pw: Password verified (boolean)
  • tv: TOTP verified (boolean)
From app/modules/dashboard_auth/service.py:18-19:

Changing Password

Requirements: Valid authenticated session
From app/modules/dashboard_auth/service.py:222-224:

Removing Password

To return to unauthenticated mode:
From app/modules/dashboard_auth/service.py:226-228:
Removing password also disables TOTP and clears the secret. This returns the system to completely unauthenticated mode.

TOTP Two-Factor Authentication

Prerequisites

TOTP requires an active password session. You cannot enable TOTP without first configuring password auth.

Setting Up TOTP

  1. Log in with password
  2. Navigate to SettingsSecurityTwo-Factor Authentication
  3. Click Enable TOTP
  4. Scan QR code with authenticator app (Google Authenticator, Authy, 1Password, etc.)
  5. Enter 6-digit code from app
  6. Click Verify
API flow: Step 1: Start setup
Response:
From app/modules/dashboard_auth/service.py:246-256:
Step 2: Confirm setup
Verifies the code and stores encrypted secret:
TOTP uses standard 6-digit codes, 30-second time steps, SHA-1 algorithm, and window=1 (accepts codes from previous/current/next time step for clock skew tolerance).

Logging In with TOTP

Once TOTP is configured and totp_required_on_login is enabled:
  1. Enter password → Creates session with pw=true, tv=false
  2. Dashboard shows TOTP prompt
  3. Enter 6-digit code from authenticator app
  4. Upgrades session to pw=true, tv=true
TOTP verification:
From app/modules/dashboard_auth/service.py:270-287:
Replay protection: The totp_last_verified_step field prevents reusing codes. Each time step is a 30-second counter, and the system tracks the most recent verified step.

Disabling TOTP

Requirements: TOTP-verified session
From app/modules/dashboard_auth/service.py:289-306:

Authentication Guard

Scope

Authentication is enforced on all /api/* routes except:
  • /api/dashboard-auth/* (auth endpoints themselves)
  • /api/codex/usage (uses separate bearer caller identity validation)

Guard Logic

From openspec/specs/admin-auth/spec.md:84-96:
Authentication required condition: the system SHALL evaluate password_hash and totp_required_on_login together to determine whether authentication is required. When password_hash is NULL and totp_required_on_login is false, the guard MUST allow all requests (unauthenticated mode). When either password_hash is set or totp_required_on_login is true, the guard MUST require a valid session.
Session validation steps when requires_auth is true:
  1. Valid session cookie must be present (else 401)
  2. If password_hash is not NULL, session must have password_verified=true
  3. If totp_required_on_login is true, session must have totp_verified=true
From app/modules/dashboard_auth/service.py:187-208:

Session State Endpoint

Check current authentication state:
Response (unauthenticated mode):
Response (password set, not logged in):
Response (logged in with password, TOTP pending):
Response (fully authenticated with TOTP):

Rate Limiting

Both password and TOTP login attempts are rate-limited:
Limits: 8 failures per 60-second window Rate limit logic (from app/modules/dashboard_auth/service.py:143-178):
On rate limit breach:
  • HTTP Status: 429 Too Many Requests
  • Header: Retry-After: {seconds}
  • Error: {"error": {"code": "rate_limit_exceeded", ...}}
Rate limit state is stored in-memory and resets on server restart. For production deployments with multiple instances, consider implementing distributed rate limiting.

Settings Cache

To avoid per-request database queries, settings are cached: From openspec/specs/admin-auth/spec.md:179-192:
The system SHALL cache DashboardSettings in memory with a TTL of 5 seconds to avoid per-request DB queries in the auth guard. The cache MUST be invalidated immediately when settings are modified via the settings API or password/TOTP management endpoints.
Cache behavior:
  • TTL: 5 seconds
  • Invalidation: Immediate on setting changes
  • Benefit: Reduces DB load on high-traffic dashboards

Logout

Clears session cookie (server-side no-op since sessions are stateless/encrypted). From app/modules/dashboard_auth/service.py:308-309:

Security Best Practices

Strong Passwords

  • Minimum 8 characters (enforced)
  • Recommended: 12+ characters with mixed case, numbers, symbols
  • Use a password manager

Enable TOTP

Always enable TOTP for production deployments:
  1. Setup password
  2. Configure TOTP
  3. Enable SettingsRequire TOTP on Login
TOTP adds a second factor even if password is compromised. Highly recommended for production.

Network Security

  • HTTPS: Always run behind HTTPS in production
  • Firewall: Restrict dashboard access to trusted IPs
  • Reverse proxy: Use nginx/Caddy for TLS termination

Session Security

Sessions use Fernet encryption with a key derived from environment:
Environment variables:
If ENCRYPTION_KEY changes, all existing sessions are invalidated. Users must re-login.

Troubleshooting

Locked Out (Forgot Password)

If you forget the password: Option 1: Reset via database
This returns to unauthenticated mode. Option 2: Set new password hash
Then update database:

TOTP Code Not Working

Causes:
  • Clock skew between server and authenticator app
  • Wrong secret scanned
  • Code already used (replay protection)
Solutions:
  1. Ensure server and device clocks are synced (NTP)
  2. Re-scan QR code in authenticator app
  3. Wait for next 30-second window

Rate Limited

Symptom: 429 Too Many Requests Cause: 8 failed login attempts in 60 seconds Solution: Wait for the Retry-After duration (shown in error response)

Session Expired

Symptom: Dashboard redirects to login after 12 hours Cause: Normal session TTL behavior Solution: Log in again. Consider increasing _SESSION_TTL_SECONDS in code if needed.

Technical Reference

Key source files:
  • app/modules/dashboard_auth/service.py - Auth business logic
  • app/modules/dashboard_auth/repository.py - Database operations
  • app/modules/dashboard_auth/schemas.py - API schemas
  • app/core/auth/totp.py - TOTP implementation
  • app/core/crypto.py - Encryption utilities
  • openspec/specs/admin-auth/spec.md - Detailed specification