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: falsein 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
- User registers
- Verification email sent
- User clicks verification link
- 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
- Use HTTPS: Always use TLS in production
- Strong JWT secret: Use cryptographically random values
- Token expiry: Set appropriate expiry times
- Secure cookies: Use HttpOnly and Secure flags
Troubleshooting
Login Failed
- Check username/password case sensitivity
- Verify user account exists
- Check email verification status (if enabled)
- Review server logs for errors
Token Expired
- Re-authenticate to get new token
- Consider longer expiry if needed
- Implement token refresh in your client
Invalid Token
- Verify token is complete (not truncated)
- Check JWT secret hasn't changed
- Verify server clock is accurate