Authentication Setup and Usage Guide¶
This guide provides comprehensive instructions for setting up and using authentication in Orcheo. Authentication protects your workflow orchestration platform by ensuring that only authorized users and services can access your workflows, data, and API endpoints.
Table of Contents¶
- Overview
- Authentication Modes
- Service Token Authentication
- JWT Authentication
- WebSocket Authentication
- Authorization and Scopes
- Rate Limiting
- Security Best Practices
- Troubleshooting
Overview¶
Orcheo supports a flexible authentication system that can work in three modes:
- Disabled: No authentication required (development only)
- Optional: Authentication is validated when provided but not required
- Required: All requests must include valid credentials
The authentication system supports two primary methods:
- Service Tokens: Long-lived tokens for CLI, CI/CD, and service-to-service communication
- JWT Tokens: Standards-based JSON Web Tokens with symmetric (HS256) or asymmetric (RS256) signing
Authentication Modes¶
Setting the Authentication Mode¶
Configure authentication mode via the ORCHEO_AUTH_MODE environment variable:
# Disable authentication (development only - NOT for production)
export ORCHEO_AUTH_MODE=disabled
# Optional authentication (validates tokens when provided)
export ORCHEO_AUTH_MODE=optional
# Required authentication (all requests must be authenticated)
export ORCHEO_AUTH_MODE=required
Default behavior: When ORCHEO_AUTH_MODE is not set, authentication defaults to optional but will automatically enforce authentication if any credentials are configured (JWT secrets, JWKS URLs, or service tokens).
Recommended Settings by Environment¶
| Environment | Mode | Rationale |
|---|---|---|
| Local Development | optional or disabled |
Easier testing without credentials |
| Staging | required |
Validate authentication before production |
| Production | required |
Protect all endpoints and data |
Service Token Authentication¶
Service tokens are ideal for CLI usage, CI/CD pipelines, and automated service integrations. They are long-lived, can be scoped to specific workspaces, and support rotation and revocation.
Bootstrap Token for Initial Setup¶
When first deploying Orcheo with authentication enabled, you need a way to create your first service token without already having credentials. The bootstrap service token solves this "chicken and egg" problem.
What is a Bootstrap Token?¶
A bootstrap token is a special service token configured via the ORCHEO_AUTH_BOOTSTRAP_SERVICE_TOKEN environment variable. Unlike regular service tokens:
- Not stored in the database: Read directly from the environment
- Full admin access by default: Can create other tokens and manage all resources (scopes are configurable)
- Optional expiration: Set
ORCHEO_AUTH_BOOTSTRAP_TOKEN_EXPIRES_ATto enforce an automated cut-off - Intended for initial setup only: Should be removed after creating persistent tokens
Setting Up a Bootstrap Token¶
-
Generate a secure random token:
-
Configure the environment:
Optional: setexport ORCHEO_AUTH_BOOTSTRAP_SERVICE_TOKEN="your-secure-random-token" export ORCHEO_AUTH_MODE=requiredORCHEO_AUTH_BOOTSTRAP_TOKEN_EXPIRES_AT(ISO 8601 or UNIX epoch) to automatically disable the bootstrap token after a deadline, for example2024-05-01T12:00:00Z.
⚠️ Security Note: Bootstrap tokens should be treated as root credentials. Store them in secure secret management systems, not in plain text files.
-
Start the server:
-
Create your first persistent token:
-
Update your environment to use the persistent token:
-
Restart the server without the bootstrap token for production use.
Bootstrap Token Scopes¶
By default, the bootstrap token grants all admin scopes. You can restrict this with ORCHEO_AUTH_BOOTSTRAP_TOKEN_SCOPES:
Bootstrap Token Expiration¶
To prevent long-lived secrets, set ORCHEO_AUTH_BOOTSTRAP_TOKEN_EXPIRES_AT to either an ISO 8601 timestamp (2024-05-01T12:00:00Z) or a UNIX epoch (1714564800). Once the timestamp passes, Orcheo rejects the bootstrap token with auth.token_expired and records a failed authentication event. Renew the value before it expires if you still need the bootstrap token.
Security Considerations¶
- Use only during initial setup: The bootstrap token should be temporary
- Remove after creating persistent tokens: Don't leave it configured in production
- Rotate if exposed: Generate a new random token if the bootstrap token is compromised
- Monitor usage: Check logs for bootstrap token authentication events
- Use secret managers: Store the bootstrap token in AWS Secrets Manager, Vault, etc.
Bootstrap Token vs. Disabled Auth Mode¶
| Approach | Use Case | Security |
|---|---|---|
| Bootstrap Token | Production deployments with AUTH_MODE=required |
✅ Secure - authentication always enforced |
| Disabled Auth Mode | Local development only | ⚠️ Insecure - no authentication at all |
Recommendation: Always use the bootstrap token approach for production deployments.
Managing Service Tokens¶
All service token management is done through the CLI or API. Tokens are stored in the configured service-token repository with SHA256-hashed secrets.
Creating a Service Token¶
# Create a basic service token
orcheo token create
# Create a token with a custom identifier
orcheo token create --id my-ci-token
# Create a token with specific scopes
orcheo token create --id backend-service \
--scope workflows:read \
--scope workflows:execute
# Create a token with workspace access restrictions
orcheo token create --id analytics-service \
--workspace ws-analytics \
--workspace ws-reports
# Create a token that expires in 30 days (2,592,000 seconds)
orcheo token create --id temp-token --expires-in 2592000
Important: The token secret is displayed only once during creation. Store it securely in a password manager or secrets vault.
Example output:
Service token created successfully!
ID: my-ci-token
Secret: VGhpc0lzQW5FeGFtcGxlU2VjcmV0VG9rZW5TdHJpbmc=
⚠ Store this secret securely. It will not be shown again.
Scopes: workflows:read, workflows:execute
Workspaces: ws-analytics
Expires: 2025-12-03 15:30:00 UTC
Listing Service Tokens¶
This displays a table with token IDs, scopes, workspaces, issuance dates, expiration dates, and status.
Viewing Token Details¶
Rotating Service Tokens¶
Token rotation generates a new secret while keeping the old token valid for a grace period:
# Rotate with default 5-minute overlap
orcheo token rotate my-ci-token
# Rotate with 30-minute overlap
orcheo token rotate my-ci-token --overlap 1800
# Rotate with custom expiration for new token
orcheo token rotate my-ci-token --overlap 600 --expires-in 7776000
Rotation workflow:
1. Run orcheo token rotate to generate a new token
2. Update your services/CI systems with the new token during the overlap period
3. The old token automatically expires after the overlap period
Revoking Service Tokens¶
Immediately invalidate a token:
Revoked tokens cannot be used for authentication and the action is logged in the audit trail.
Using Service Tokens¶
With the CLI¶
Store your service token in an environment variable:
export ORCHEO_SERVICE_TOKEN="your-token-secret-here"
# Now CLI commands will authenticate automatically
orcheo workflow list
orcheo workflow run my-workflow
Alternatively, configure it in your CLI profile at ~/.config/orcheo/cli.toml:
With the Python SDK¶
import os
from orcheo_sdk import OrcheoClient
# Using environment variable
client = OrcheoClient(
api_url="https://orcheo.example.com",
token=os.environ["ORCHEO_SERVICE_TOKEN"]
)
# Or pass directly (not recommended for production)
client = OrcheoClient(
api_url="https://orcheo.example.com",
token="your-token-secret-here"
)
# Use the client
workflows = client.list_workflows()
With HTTP Requests¶
Include the token in the Authorization header:
In CI/CD Pipelines¶
GitHub Actions:
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Deploy workflow
env:
ORCHEO_SERVICE_TOKEN: ${{ secrets.ORCHEO_SERVICE_TOKEN }}
run: |
orcheo workflow upload deployment.py
GitLab CI:
deploy:
script:
- orcheo workflow upload deployment.py
variables:
ORCHEO_SERVICE_TOKEN: $ORCHEO_SERVICE_TOKEN
Service Token Configuration¶
The service token backend is controlled by:
# Use the Postgres-backed repository
export ORCHEO_AUTH_SERVICE_TOKEN_BACKEND=postgres
export ORCHEO_POSTGRES_DSN=postgresql://user:pass@host:5432/orcheo
If not specified, tokens stay in the in-memory repository for the life of the process.
JWT Authentication¶
JWT authentication is designed for interactive user sessions, IdP integration, and federated authentication scenarios.
Symmetric Key (HS256) Authentication¶
Use a shared secret for signing and validating tokens:
# Set a strong secret key (minimum 32 bytes recommended)
export ORCHEO_AUTH_JWT_SECRET="your-very-long-and-secure-secret-key-here"
# Restrict to HS256 algorithm only (optional, defaults to RS256,HS256)
export ORCHEO_AUTH_ALLOWED_ALGORITHMS="HS256"
# Optional: Enforce audience validation
export ORCHEO_AUTH_AUDIENCE="orcheo-api"
# Optional: Enforce issuer validation
export ORCHEO_AUTH_ISSUER="orcheo-auth-service"
Generating a Secure Secret¶
Asymmetric Key (RS256) Authentication with JWKS¶
For production deployments with an Identity Provider (IdP) like Auth0, Okta, or Keycloak:
# Configure JWKS URL for fetching public keys
export ORCHEO_AUTH_JWKS_URL="https://your-idp.com/.well-known/jwks.json"
# Set cache TTL for JWKS (seconds)
export ORCHEO_AUTH_JWKS_CACHE_TTL=300
# Set timeout for JWKS fetches (seconds)
export ORCHEO_AUTH_JWKS_TIMEOUT=5.0
# Restrict to RS256 algorithm
export ORCHEO_AUTH_ALLOWED_ALGORITHMS="RS256"
# Validate audience claim
export ORCHEO_AUTH_AUDIENCE="orcheo-production"
# Validate issuer claim
export ORCHEO_AUTH_ISSUER="https://your-idp.com/"
Static JWKS Configuration¶
For environments where dynamic JWKS fetching isn't available:
# Inline JWKS as JSON
export ORCHEO_AUTH_JWKS_STATIC='{
"keys": [
{
"kty": "RSA",
"kid": "key-id-1",
"use": "sig",
"alg": "RS256",
"n": "base64-encoded-modulus",
"e": "AQAB"
}
]
}'
Or use ORCHEO_AUTH_JWKS as an alternative environment variable name.
JWT Token Claims¶
Orcheo extracts identity information from JWT claims:
| Claim | Purpose |
|---|---|
sub |
Subject identifier (user ID) |
jti |
Token ID for tracking and revocation |
iat |
Issued at timestamp |
exp |
Expiration timestamp |
aud |
Audience (validated if configured) |
iss |
Issuer (validated if configured) |
scope / scopes / scp |
Space or comma-delimited scopes |
workspace_ids / workspaces / workspace_id / workspace |
Authorized workspace IDs |
type / typ / token_use |
Token type (user, service, client) |
Custom nested claims under orcheo object:
{
"sub": "user-123",
"orcheo": {
"scopes": ["workflows:read", "workflows:execute"],
"workspace_ids": ["ws-production", "ws-staging"]
}
}
Using JWT Tokens¶
Include the JWT in the Authorization header:
curl -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..." \
https://orcheo.example.com/api/workflows
With the Python SDK:
WebSocket Authentication¶
WebSocket connections support authentication via:
1. Authorization Header (Preferred)¶
const ws = new WebSocket('wss://orcheo.example.com/ws/workflow', {
headers: {
'Authorization': 'Bearer YOUR_TOKEN_HERE'
}
});
2. Query Parameter¶
Or using access_token:
Note: Query parameter authentication is less secure than header-based authentication and should only be used when headers aren't supported by your WebSocket client.
Authorization and Scopes¶
Understanding Scopes¶
Scopes define fine-grained permissions for authenticated identities:
| Scope | Description |
|---|---|
workflows:read |
List and view workflow definitions |
workflows:write |
Create, update, and delete workflows |
workflows:execute |
Trigger workflow executions |
vault:read |
Access credential vault (read-only) |
vault:write |
Manage credentials in vault |
admin:tokens:read |
View service tokens |
admin:tokens:write |
Create, rotate, and revoke service tokens |
chatkit:session |
Issue short-lived ChatKit session JWTs via POST /api/chatkit/session |
Checking Scopes in Code¶
from fastapi import Depends
from orcheo_backend.app.authentication import (
RequestContext,
require_scopes,
get_request_context
)
# Require specific scopes for an endpoint
@app.get("/api/workflows")
async def list_workflows(
context: RequestContext = Depends(require_scopes("workflows:read"))
):
# This endpoint requires workflows:read scope
return {"workflows": [...]}
# Or manually check scopes
@app.post("/api/workflows")
async def create_workflow(
context: RequestContext = Depends(get_request_context)
):
if not context.has_scope("workflows:write"):
raise HTTPException(status_code=403, detail="Missing workflows:write scope")
# Create workflow...
Workspace-Based Authorization¶
Restrict token access to specific workspaces:
from orcheo_backend.app.authentication import (
require_workspace_access,
ensure_workspace_access
)
# Using a dependency
@app.get("/api/workspaces/{workspace_id}")
async def get_workspace(
workspace_id: str,
context: RequestContext = Depends(require_workspace_access("ws-production"))
):
# Only tokens with ws-production access can call this
return {"workspace": workspace_id}
# Manual checking
from orcheo_backend.app.authentication import get_authorization_policy
@app.get("/api/workspaces/{workspace_id}")
async def get_workspace(
workspace_id: str,
policy: AuthorizationPolicy = Depends(get_authorization_policy)
):
policy.require_workspace(workspace_id)
# Continue with authorized access
Rate Limiting¶
Protect against brute-force attacks with per-IP and per-identity rate limiting:
# Maximum authentication failures per IP address (0 disables)
export ORCHEO_AUTH_RATE_LIMIT_IP=10
# Maximum failures per authenticated identity (0 disables)
export ORCHEO_AUTH_RATE_LIMIT_IDENTITY=5
# Sliding window interval in seconds
export ORCHEO_AUTH_RATE_LIMIT_INTERVAL=60
Example: With ORCHEO_AUTH_RATE_LIMIT_IP=10 and ORCHEO_AUTH_RATE_LIMIT_INTERVAL=60, an IP address that fails authentication 10 times within 60 seconds will be blocked with a 429 Too Many Requests response.
Rate Limit Responses¶
When rate limited, the API returns:
Headers include:
Security Best Practices¶
Service Token Management¶
- Use descriptive identifiers: Name tokens by their purpose (e.g.,
github-actions-deploy,analytics-service) - Apply least privilege: Only grant necessary scopes and workspace access
- Set expiration dates: Use
--expires-infor temporary access - Rotate regularly: Implement 30-60 day rotation schedules for long-lived tokens
- Revoke immediately on compromise: Use
orcheo token revokeif a token is exposed - Store securely: Use password managers (1Password, LastPass) or secret managers (AWS Secrets Manager, HashiCorp Vault)
- Never commit to git: Add token values to
.gitignoreand use environment variables
JWT Configuration¶
- Use strong secrets: Minimum 32 bytes for HS256, generated randomly
- Prefer asymmetric signing: Use RS256 with JWKS for production deployments
- Validate claims: Always configure
ORCHEO_AUTH_AUDIENCEandORCHEO_AUTH_ISSUER - Restrict algorithms: Use
ORCHEO_AUTH_ALLOWED_ALGORITHMSto prevent algorithm confusion attacks - Enable JWKS caching: Reduce external dependencies and improve performance
- Monitor token expiration: Implement token refresh flows for user sessions
Network and Transport Security¶
- Always use HTTPS/TLS: Never send tokens over unencrypted connections
- Configure CORS properly: Restrict origins in production
- Use secure WebSocket (WSS): Encrypt WebSocket traffic
- Set proper headers: Enable HSTS, X-Content-Type-Options, X-Frame-Options
Operational Security¶
- Enable rate limiting: Always configure rate limits in production
- Monitor authentication logs: Track failed authentication attempts
- Review token usage: Regularly audit service tokens with
orcheo token list - Implement alerting: Set up alerts for unusual authentication patterns
- Document token ownership: Maintain a registry of which tokens are used where
Development vs. Production¶
| Setting | Development | Production |
|---|---|---|
ORCHEO_AUTH_MODE |
optional or disabled |
required |
ORCHEO_AUTH_JWT_SECRET |
Simple test secret | Strong 64-byte random secret |
| Rate limiting | Disabled (0) |
Enabled (10-20 attempts) |
| JWKS | Static test keys | Production IdP JWKS URL |
| Token expiration | Long or none | Regular rotation schedule |
Troubleshooting¶
Common Errors¶
"Missing bearer token"¶
Cause: No Authorization header provided or token is empty.
Solution:
# Ensure token is set
echo $ORCHEO_SERVICE_TOKEN
# Check API call includes Authorization header
curl -v -H "Authorization: Bearer $ORCHEO_SERVICE_TOKEN" \
https://orcheo.example.com/api/workflows
"Invalid bearer token"¶
Possible causes: - Token is malformed - Token doesn't match any service token - JWT signature validation failed - Token has been revoked
Solution:
# For service tokens, verify it exists and isn't revoked
orcheo token list
# For JWTs, check:
# 1. JWT secret matches
# 2. JWKS URL is accessible
# 3. Token hasn't expired
# 4. Algorithm is allowed
"Bearer token has expired"¶
Cause: JWT exp claim or service token expiration date has passed.
Solution: - For service tokens: Create a new token or rotate the existing one - For JWTs: Implement token refresh flow or reauthenticate
"Missing required scopes: workflows:write"¶
Cause: Token doesn't have the required scope.
Solution:
# Create a new token with the required scope
orcheo token create --id new-token --scope workflows:write
# Or add scope when rotating
orcheo token rotate old-token --scope workflows:write
"Workspace access denied"¶
Cause: Token isn't authorized for the requested workspace.
Solution:
# Create token with workspace access
orcheo token create --id workspace-token \
--workspace ws-production
"Too many authentication attempts"¶
Cause: Rate limit exceeded.
Solution:
- Wait for the rate limit window to expire (check Retry-After header)
- Fix authentication credentials to avoid repeated failures
- Contact administrator if you believe you're being blocked incorrectly
Debugging Authentication¶
Enable Debug Logging¶
export ORCHEO_LOG_LEVEL=DEBUG
export ORCHEO_LOG_SENSITIVE_DEBUG=1
# Start Orcheo backend
orcheo server start
This logs detailed authentication events, token validation steps, and scope checks.
Check Authentication Status¶
Use the request context in endpoints:
@app.get("/debug/auth")
async def debug_auth(
context: RequestContext = Depends(get_request_context)
):
return {
"authenticated": context.is_authenticated,
"subject": context.subject,
"identity_type": context.identity_type,
"scopes": list(context.scopes),
"workspace_ids": list(context.workspace_ids),
"token_id": context.token_id
}
Verify JWKS Configuration¶
Test Token Locally¶
import jwt
token = "your-jwt-token"
secret = "your-secret-key"
try:
claims = jwt.decode(
token,
secret,
algorithms=["HS256"],
audience="orcheo-api"
)
print("Token valid:", claims)
except jwt.ExpiredSignatureError:
print("Token expired")
except jwt.InvalidTokenError as e:
print("Token invalid:", e)
Getting Help¶
If you continue to experience authentication issues:
- Check the environment variables documentation
- Review server logs for detailed error messages
- Verify your configuration matches the authentication mode
- Consult the authentication design document for architecture details
- Open an issue on GitHub with logs and configuration details (redact secrets!)
Related Documentation¶
- Environment Variables Reference - Complete list of authentication-related environment variables
- Authentication Design - Architecture and design decisions
- Deployment Guide - Production deployment considerations
- Security Review - Security audit findings and mitigations