Skip to main content

Authentication

Codex supports multiple authentication methods for different use cases.

Authentication Methods

JWT Token

Primary method for web interface and API clients:

# Login
curl -X POST http://localhost:8080/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"user","password":"pass"}'

Response:

{
"token": "eyJhbGciOiJIUzI1NiIs...",
"expires_at": "2024-01-16T10:00:00Z"
}

Use the token:

curl -H "Authorization: Bearer $TOKEN" \
http://localhost:8080/api/v1/libraries

Token properties:

  • Default expiry: 24 hours (configurable)
  • Stateless (no server-side storage)
  • Contains user ID and permissions

Refresh Token

Login responses include a long-lived refresh token alongside the short-lived access token:

{
"token": "eyJhbGciOiJIUzI1NiIs...",
"expires_at": "2024-01-16T10:00:00Z",
"refresh_token": "rt_...",
"refresh_token_expires_at": "2024-02-15T10:00:00Z"
}

Exchange a refresh token for a new access token (and a new refresh token):

curl -X POST http://localhost:8080/api/v1/auth/refresh \
-H "Content-Type: application/json" \
-d '{"refresh_token":"rt_..."}'

Properties:

  • Enabled by default. Disable with auth.refresh_token_enabled: false in the config file.
  • Configurable expiry. Default 30 days, set with auth.refresh_token_expiry_days.
  • Hashed at rest. Only the SHA-256 of each token is stored.
  • Rotated on every refresh. The previous token is marked used; the response carries a new one.
  • Theft detection. Reusing an already-rotated refresh token revokes every active token in the same family as a defensive measure.
  • Daily cleanup. Expired refresh tokens are pruned by a scheduled job; logout revokes the active token.

The web client uses this flow automatically: any 401 from the API triggers a transparent /auth/refresh exchange and the original request is retried with the new access token. API and CLI clients that want the same behaviour can implement the same one-shot retry, or rely on access-token expiry and re-login.

API Key

For automation and service accounts:

curl -H "Authorization: Bearer codex_key_here" \
http://localhost:8080/api/v1/libraries

See API Keys for details.

HTTP Basic Auth

For simple clients and OPDS:

curl -u "username:password" \
http://localhost:8080/api/v1/libraries

OIDC / Single Sign-On

For enterprise and homelab SSO via external identity providers (Authentik, Keycloak, etc.):

  • Users click an OIDC provider button on the login page
  • They authenticate at the external IdP
  • Codex creates or links their account automatically

See OIDC / Single Sign-On for setup instructions.

Email Verification

Optional email verification can be enabled:

auth:
email_confirmation_required: true

Verification Flow

  1. User registers
  2. Verification email sent
  3. User clicks verification link
  4. Account activated

Email Configuration

application:
base_url: https://codex.example.com # Used for verification links

email:
smtp_host: smtp.example.com
smtp_port: 587
smtp_username: noreply@example.com
smtp_password: smtp-password
smtp_from_email: noreply@example.com
smtp_from_name: Codex
verification_token_expiry_hours: 24
# verification_url_base: https://codex.example.com # Optional override, falls back to application.base_url

Resend Verification

curl -X POST http://localhost:8080/api/v1/auth/resend-verification \
-H "Content-Type: application/json" \
-d '{"email":"user@example.com"}'

Password Security

Password Hashing

Passwords are hashed using Argon2id with configurable parameters:

auth:
argon2_memory_cost: 19456 # 19 MB
argon2_time_cost: 2 # Iterations
argon2_parallelism: 1 # Threads

Password Requirements

Default requirements:

  • Minimum 8 characters
  • Recommended: mix of letters, numbers, symbols

Password Reset

Currently, password reset is admin-managed:

# Admin updates user password
curl -X PUT http://localhost:8080/api/v1/users/{id} \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"password":"new-password"}'

JWT Configuration

auth:
jwt_secret: "your-secret-key" # Required, use strong random value
jwt_expiry_hours: 24 # Token lifetime

Generate a secure secret:

openssl rand -base64 32

Security Best Practices

  1. Use HTTPS: Always use TLS in production
  2. Strong JWT secret: Use cryptographically random values
  3. Token expiry: Set appropriate expiry times
  4. Secure cookies: Use HttpOnly and Secure flags

Troubleshooting

Login Failed

  1. Check username/password case sensitivity
  2. Verify user account exists
  3. Check email verification status (if enabled)
  4. Review server logs for errors

Token Expired

  1. Re-authenticate to get new token
  2. Consider longer expiry if needed
  3. Implement token refresh in your client

Invalid Token

  1. Verify token is complete (not truncated)
  2. Check JWT secret hasn't changed
  3. Verify server clock is accurate