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