This document describes the security features implemented in transcript-create and best practices for deployment.
- Authentication & Authorization
- API Key Management
- Session Security
- OAuth Security
- Rate Limiting
- Security Headers
- Audit Logging
- Configuration
- Best Practices
The API supports two authentication methods:
-
Session Cookies (recommended for web applications)
- Set after successful OAuth login
- HttpOnly, SameSite=lax, Secure (in production)
- Automatic expiration and refresh
-
API Keys (recommended for programmatic access)
- Bearer token authentication
- SHA-256 hashed storage
- Configurable expiration
- Per-user management
Three user roles are supported:
- user - Default role for authenticated users
- pro - Pro plan subscribers (inherits user permissions)
- admin - Administrators (inherits pro permissions)
from fastapi import Depends
from app.security import require_role, ROLE_ADMIN, ROLE_PRO
@router.get("/admin/dashboard")
def admin_dashboard(user=Depends(require_role(ROLE_ADMIN))):
"""Admin-only endpoint."""
return {"message": "Admin access granted"}
@router.get("/pro/feature")
def pro_feature(user=Depends(require_role(ROLE_PRO))):
"""Pro users and admins can access this."""
return {"message": "Pro feature"}Endpoint: POST /api-keys
curl -X POST https://api.example.com/api-keys \
-H "Cookie: tc_session=YOUR_SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "My API Key",
"expires_days": 365
}'Response:
{
"api_key": "tc_XxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXx",
"key": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"name": "My API Key",
"key_prefix": "tc_XxXxXx...",
"created_at": "2025-10-26T04:00:00Z",
"expires_at": "2026-10-26T04:00:00Z",
"last_used_at": null,
"revoked_at": null,
"scopes": null
}
}Include the API key in requests using either:
-
Authorization header:
curl https://api.example.com/jobs \ -H "Authorization: Bearer tc_YOUR_API_KEY" -
X-API-Key header:
curl https://api.example.com/jobs \ -H "X-API-Key: tc_YOUR_API_KEY"
curl https://api.example.com/api-keys \
-H "Cookie: tc_session=YOUR_SESSION_TOKEN"curl -X DELETE https://api.example.com/api-keys/KEY_ID \
-H "Cookie: tc_session=YOUR_SESSION_TOKEN"Sessions are configured with secure defaults:
# .env configuration
SESSION_EXPIRE_HOURS=24 # Session lifetime
SESSION_REFRESH_THRESHOLD_HOURS=12 # Auto-refresh threshold- httpOnly=true - Prevents JavaScript access (XSS protection)
- sameSite=lax - CSRF protection
- secure=true - HTTPS only (in production)
- maxAge - Configurable expiration
Sessions are automatically refreshed when:
- User makes an authenticated request
- Session age exceeds
SESSION_REFRESH_THRESHOLD_HOURS - Session is still valid (not expired)
This extends the session lifetime without requiring re-authentication.
OAuth flows use the state parameter to prevent CSRF attacks:
- Random state token generated before redirect
- State stored in secure session
- State validated on callback
- One-time use (cleared after validation)
Enable/disable state validation:
# .env
OAUTH_STATE_VALIDATION=trueNonces are generated and can be used for additional replay protection in OAuth flows.
# Google OAuth
OAUTH_GOOGLE_CLIENT_ID=your_client_id
OAUTH_GOOGLE_CLIENT_SECRET=your_secret
OAUTH_GOOGLE_REDIRECT_URI=https://api.example.com/auth/callback/google
# Twitch OAuth
OAUTH_TWITCH_CLIENT_ID=your_client_id
OAUTH_TWITCH_CLIENT_SECRET=your_secret
OAUTH_TWITCH_REDIRECT_URI=https://api.example.com/auth/callback/twitch# .env
ENABLE_RATE_LIMITING=true
MAX_LOGIN_ATTEMPTS=5
LOGIN_ATTEMPT_WINDOW_MINUTES=15- General endpoints: 100 requests/minute per IP
- Health checks: Unlimited
- Metrics: Unlimited
When rate limited, the API returns:
HTTP/1.1 429 Too Many Requests
Retry-After: 60
Content-Type: application/json
{
"error": "rate_limit_exceeded",
"message": "Too many requests. Please try again later."
}The following security headers are automatically added to all responses:
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Strict-Transport-Security: max-age=31536000 (production only)
Content-Security-Policy: default-src 'self'; ...
Permissions-Policy: geolocation=(), microphone=(), camera=()
Only enabled in production (when ENVIRONMENT=production):
Strict-Transport-Security: max-age=31536000; includeSubDomains
Security-relevant events are logged to the audit_logs table:
- Login success/failure
- Logout events
- API key creation/revocation
- Permission denied attempts
- Admin actions
- Rate limit violations
CREATE TABLE audit_logs (
id BIGSERIAL PRIMARY KEY,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
user_id UUID REFERENCES users(id),
action TEXT NOT NULL,
resource_type TEXT,
resource_id TEXT,
ip_address TEXT,
user_agent TEXT,
success BOOLEAN NOT NULL DEFAULT true,
details JSONB NOT NULL DEFAULT '{}'
);from app.audit import get_audit_logs
# Get recent logs for a user
logs = get_audit_logs(
db,
user_id=user_id,
limit=100,
offset=0
)Logs are retained for 90 days by default. Use the cleanup function:
from app.audit import cleanup_old_audit_logs
# Remove logs older than 90 days
deleted = cleanup_old_audit_logs(db, days_to_keep=90)# Security
ENVIRONMENT=production # development, staging, production
SESSION_SECRET=generate_secure_key # openssl rand -hex 32
ENABLE_RATE_LIMITING=true
SESSION_EXPIRE_HOURS=24
SESSION_REFRESH_THRESHOLD_HOURS=12
API_KEY_EXPIRE_DAYS=365
OAUTH_STATE_VALIDATION=true
# CORS
FRONTEND_ORIGIN=https://app.example.com
CORS_ALLOW_ORIGINS=https://admin.example.com,https://mobile.example.com
# Admin Access
ADMIN_EMAILS=admin@example.com,security@example.com- Set
ENVIRONMENT=production - Generate secure
SESSION_SECRET(min 32 bytes) - Enable
OAUTH_STATE_VALIDATION=true - Configure
ADMIN_EMAILSwith actual admin emails - Use HTTPS only (required for secure cookies)
- Set strong OAuth client secrets
- Enable
ENABLE_RATE_LIMITING=true - Configure proper CORS origins (don't use wildcards)
- Review and adjust session expiration times
- Set up regular audit log cleanup
-
Never commit secrets to Git
- Use
.envfiles (gitignored) - Use secret managers (AWS Secrets Manager, HashiCorp Vault, etc.)
- Use
-
Rotate secrets regularly
- OAuth client secrets: annually
- API keys: set expiration dates
- Session secrets: on security incidents
-
Use environment-specific secrets
- Different secrets for dev/staging/production
- Test mode keys in development
-
Treat API keys like passwords
- Never log full keys
- Never expose in client-side code
- Store securely (encrypted at rest)
-
Use expiration dates
- Set reasonable expiration periods
- Review and rotate regularly
-
Revoke unused keys
- Audit and remove inactive keys
- Revoke immediately if compromised
-
Configure appropriate expiration
- Balance security vs. user convenience
- Shorter for sensitive operations
- Use session refresh for better UX
-
Monitor for suspicious activity
- Multiple sessions from different IPs
- Rapid session creation
- Failed authentication attempts
-
Force logout on security events
- Password changes
- Permission changes
- Suspicious activity detected
-
Whitelist specific origins
# Good CORS_ALLOW_ORIGINS=https://app.example.com # Bad CORS_ALLOW_ORIGINS=*
-
Restrict HTTP methods
- Only allow necessary methods
- Current: GET, POST, PUT, DELETE, PATCH
-
Validate Origin header
- Handled automatically by CORSMiddleware
- Never echo back Origin header
-
Monitor audit logs
- Failed authentication attempts
- Permission denied events
- Unusual patterns
-
Set up alerts
- High rate of failed logins
- API key creation by admins
- Rate limit violations
-
Regular security reviews
- Review active API keys
- Audit admin access
- Check for suspicious patterns
For security issues, please contact: security@example.com
Do not disclose security vulnerabilities publicly.