Overview
Implement API key authentication for server endpoints. This provides a simpler alternative to JWT tokens for programmatic API access while maintaining security.
Background
The current server uses JWT-based authentication via FastAPI Users. For Phase 2, we need to support:
- API keys for programmatic/automation access (CI/CD, scripts, integrations)
- JWT tokens retained for web UI sessions
API keys are preferred for server-to-server communication because:
- No expiration/refresh complexity
- Easier to rotate
- Can be scoped to specific permissions
- Works better with webhooks and integrations
Deliverables
1. API Key Model
class APIKey(BaseModel):
id: str
name: str # Human-readable name (e.g., "CI Pipeline", "Slack Bot")
key_hash: str # bcrypt hash of the actual key
prefix: str # First 8 chars for identification (e.g., "cf_live_")
scopes: list[str] # ["read", "write", "admin"]
created_at: datetime
last_used_at: datetime | None
expires_at: datetime | None # Optional expiration
is_active: bool
2. Key Generation
# Format: cf_{environment}_{random_32_chars}
# Examples:
# cf_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
# cf_test_x9y8z7w6v5u4t3s2r1q0p9o8n7m6l5k4
def generate_api_key(environment: str = "live") -> tuple[str, str]:
"""Returns (full_key, key_hash) - full_key shown once, hash stored."""
random_part = secrets.token_hex(16)
full_key = f"cf_{environment}_{random_part}"
key_hash = bcrypt.hash(full_key)
return full_key, key_hash
3. Authentication Middleware
from fastapi import Security, HTTPException
from fastapi.security import APIKeyHeader
api_key_header = APIKeyHeader(name="X-API-Key", auto_error=False)
async def get_api_key(
api_key: str = Security(api_key_header),
) -> APIKey | None:
if not api_key:
return None
# Look up by prefix for efficiency
prefix = api_key[:12] # "cf_live_" + first 4 of random
key_record = await db.get_api_key_by_prefix(prefix)
if key_record and bcrypt.verify(api_key, key_record.key_hash):
await db.update_last_used(key_record.id)
return key_record
return None
async def require_auth(
api_key: APIKey | None = Depends(get_api_key),
jwt_user: User | None = Depends(get_current_user_optional),
):
"""Accept either API key or JWT token."""
if api_key:
return {"type": "api_key", "principal": api_key}
if jwt_user:
return {"type": "jwt", "principal": jwt_user}
raise HTTPException(status_code=401, detail="Authentication required")
4. CLI Commands
# Create API key
cf auth api-key create --name "CI Pipeline" --scopes read,write
# Output: Your API key (shown once): cf_live_a1b2c3d4...
# List API keys
cf auth api-key list
# Output:
# ID NAME SCOPES LAST USED
# cf_live_a1 CI Pipeline read,write 2 hours ago
# cf_live_x9 Slack Bot read Never
# Revoke API key
cf auth api-key revoke cf_live_a1
# Rotate API key (revoke old, create new with same name/scopes)
cf auth api-key rotate cf_live_a1
5. Server Endpoints
@router.post("/api/auth/api-keys")
async def create_api_key(name: str, scopes: list[str], auth = Depends(require_auth)):
"""Create a new API key (requires admin or JWT auth)."""
@router.get("/api/auth/api-keys")
async def list_api_keys(auth = Depends(require_auth)):
"""List API keys (hashes not included)."""
@router.delete("/api/auth/api-keys/{key_id}")
async def revoke_api_key(key_id: str, auth = Depends(require_auth)):
"""Revoke an API key."""
6. Scope Definitions
| Scope |
Permissions |
read |
List tasks, view status, stream output |
write |
Create tasks, answer blockers, execute |
admin |
Manage API keys, delete projects |
Acceptance Criteria
Security Considerations
Related Issues
References
docs/authentication.md - Current auth documentation
- Stripe API keys as reference design
Overview
Implement API key authentication for server endpoints. This provides a simpler alternative to JWT tokens for programmatic API access while maintaining security.
Background
The current server uses JWT-based authentication via FastAPI Users. For Phase 2, we need to support:
API keys are preferred for server-to-server communication because:
Deliverables
1. API Key Model
2. Key Generation
3. Authentication Middleware
4. CLI Commands
5. Server Endpoints
6. Scope Definitions
readwriteadminAcceptance Criteria
X-API-KeyheaderSecurity Considerations
Related Issues
References
docs/authentication.md- Current auth documentation