Authentication¶
Cello supports three authentication methods, all implemented in Rust with constant-time comparison to prevent timing attacks. Choose the method that fits your use case, or combine multiple methods for layered security.
Authentication Methods¶
| Method | Best For | Stateless | Token Location |
|---|---|---|---|
| JWT | APIs, SPAs, mobile apps | Yes | Authorization: Bearer <token> |
| Basic Auth | Internal tools, simple auth | Yes | Authorization: Basic <base64> |
| API Key | Service-to-service, third-party access | Yes | Custom header (e.g., X-API-Key) |
JWT Authentication¶
JSON Web Tokens are the recommended authentication method for most APIs. Tokens are validated entirely in Rust using the jsonwebtoken crate.
from cello import App, JwtConfig
from cello.middleware import JwtAuth
app = App()
jwt_config = JwtConfig(
secret=b"your-secret-key-minimum-32-bytes-long",
algorithm="HS256",
expiration=3600 # 1 hour
)
jwt_auth = JwtAuth(jwt_config)
jwt_auth.skip_path("/login")
jwt_auth.skip_path("/public")
app.use(jwt_auth)
@app.get("/protected")
def protected(request):
claims = request.context.get("jwt_claims")
return {"user": claims["sub"]}
Tip
For detailed JWT configuration including token creation, refresh, and blacklisting, see the JWT documentation.
Basic Authentication¶
HTTP Basic Auth encodes credentials as username:password in Base64. Best for simple internal tools or admin panels.
from cello.middleware import BasicAuth
def verify_credentials(username: str, password: str) -> bool:
"""Return True if the credentials are valid."""
# In production, check against a database with hashed passwords
return username == "admin" and password == "secret"
app.use(BasicAuth(verify_credentials))
@app.get("/admin")
def admin(request):
# request.context["user"] contains the authenticated username
return {"admin": True, "user": request.context.get("user")}
The middleware:
- Extracts the
Authorization: Basic <base64>header. - Decodes the Base64 credentials.
- Calls your
verify_credentialsfunction. - Returns
401 Unauthorizedif validation fails.
Security
Basic Auth transmits credentials in every request (Base64-encoded, not encrypted). Always use HTTPS when using Basic Auth in production.
API Key Authentication¶
API keys are ideal for service-to-service communication and third-party integrations.
from cello.middleware import ApiKeyAuth
# Define valid API keys mapped to service names
valid_keys = {
"sk_live_abc123": "payment-service",
"sk_live_def456": "analytics-service",
"sk_live_ghi789": "partner-api"
}
api_auth = ApiKeyAuth(
keys=valid_keys,
header="X-API-Key" # Header name to check
)
app.use(api_auth)
@app.get("/api/data")
def get_data(request):
# request.context["api_key_name"] contains the service name
service = request.context.get("api_key_name")
return {"data": [], "requested_by": service}
Request Format¶
Skip Paths¶
All authentication middleware supports skipping certain paths (public endpoints, login pages, health checks):
# JWT -- skip specific paths
jwt_auth = JwtAuth(jwt_config)
jwt_auth.skip_path("/login")
jwt_auth.skip_path("/register")
jwt_auth.skip_path("/health")
jwt_auth.skip_path("/docs")
jwt_auth.skip_path("/public")
app.use(jwt_auth)
Choosing an Authentication Method¶
Do you need stateless authentication?
│
Yes ──→ Do clients have API keys?
│ │
│ Yes│ No
│ ▼ ▼
│ API Key JWT
│
No ──→ Is this an internal tool?
│
Yes│ No
â–¼ â–¼
Basic Sessions
Auth (see Sessions docs)
Common Combinations¶
# Public API with JWT for users + API keys for services
jwt_auth = JwtAuth(jwt_config)
jwt_auth.skip_path("/public")
jwt_auth.skip_path("/api/v1/webhook") # Webhook uses API key
api_auth = ApiKeyAuth(keys=valid_keys, header="X-API-Key")
# Apply JWT first, then API key for webhook routes
app.use(jwt_auth)
Error Responses¶
Authentication failures return standard HTTP error responses:
| Status | Meaning | When |
|---|---|---|
401 Unauthorized | Missing or invalid credentials | No token, bad token, expired token |
403 Forbidden | Valid credentials but insufficient permissions | Use with Guards |
Security Best Practices¶
- Always use HTTPS in production
- Store secrets in environment variables, never in code
- Use strong secrets (minimum 32 bytes)
- Set short token expiration times (1 hour for JWT)
- Implement token refresh for long-lived sessions
- Log authentication failures for security monitoring
- Rate limit authentication endpoints to prevent brute force
Next Steps¶
- JWT - Detailed JWT configuration
- Sessions - Cookie-based session management
- Guards - Authorization and RBAC
- Security Headers - Protecting against common attacks