API Authentication
Secure your API requests with authentication.
Methods
Cloudflare Access JWT
When Cloudflare Access is configured:
bash
curl https://your-domain.com/api/agents \
-H "CF-Access-JWT-Assertion: <jwt-token>"No Authentication
For development or public endpoints:
bash
curl https://your-domain.com/api/agentsCloudflare Access Setup
1. Create Access Application
- Go to Cloudflare Zero Trust
- Access > Applications
- Add an application
- Configure self-hosted application
2. Configure Identity Provider
Supported providers:
- Microsoft Azure AD
- Okta
- GitHub
- OneLogin
- SAML
- OpenID Connect
3. Set Audience Tag
Get the audience tag from Access application settings:
toml
# wrangler.toml
[vars]
CF_ACCESS_AUD = "your-audience-tag-here"4. Create Access Policy
Define who can access:
- Email domains
- Email addresses
- Identity provider groups
- IP ranges
JWT Validation
The worker validates JWTs automatically:
typescript
import { getUser } from './lib/auth'
export async function onRequest(context) {
const user = await getUser(context.request, context.env)
if (!user) {
return new Response('Unauthorized', { status: 401 })
}
// User is authenticated
console.log(user.email)
}JWT Payload
Cloudflare Access JWTs contain:
json
{
"aud": ["audience-tag"],
"email": "user@example.com",
"exp": 1702648800,
"iat": 1702645200,
"iss": "https://your-team.cloudflareaccess.com",
"sub": "user-id"
}Request Headers
JWT Header
CF-Access-JWT-Assertion: eyJhbGciOiJSUzI1NiIsInR5cCI...Client Info Headers
CF-Access-Authenticated-User-Email: user@example.com
CF-Access-Client-Id: client-idAgent Access Control
After authentication, check agent permissions:
typescript
async function checkAccess(db, agentId, email) {
// Owner or admin?
const admin = await db
.prepare('SELECT role FROM agent_admins WHERE agent_id = ? AND email = ?')
.bind(agentId, email)
.first()
return admin !== null
}Roles
| Role | Permissions |
|---|---|
owner | Full access, delete agent, manage admins |
admin | Full access, cannot delete |
viewer | Read-only access |
Public Endpoints
Some endpoints are public:
GET /api/agents(lists public agents)- Default agent access (when auth disabled)
Error Responses
401 Unauthorized
No valid authentication:
json
{
"error": {
"code": "UNAUTHORIZED",
"message": "Authentication required"
}
}403 Forbidden
Authenticated but no access:
json
{
"error": {
"code": "FORBIDDEN",
"message": "Access denied to this agent"
}
}Testing Authentication
With Access
bash
# Get token from browser or Access service token
TOKEN="your-jwt-token"
curl https://your-domain.com/api/agents \
-H "CF-Access-JWT-Assertion: $TOKEN"Service Tokens
For automated systems:
- Create Service Token in Zero Trust
- Use Client ID and Secret
- Exchange for JWT
bash
# Get service token JWT
curl -X POST "https://your-team.cloudflareaccess.com/cdn-cgi/access/get-identity" \
-H "CF-Access-Client-Id: $CLIENT_ID" \
-H "CF-Access-Client-Secret: $CLIENT_SECRET"Best Practices
1. Always Use HTTPS
All API calls should use HTTPS:
https://your-domain.com/api/...2. Validate on Every Request
Check authentication for each request:
typescript
const user = await getUser(request, env)
if (!user) return unauthorized()3. Use Service Tokens for Automation
For CI/CD and scripts:
- Create dedicated service tokens
- Rotate regularly
- Limit permissions
4. Log Access
Track authentication events:
typescript
await logAccess({
user: user.email,
action: 'api_request',
resource: request.url
})5. Handle Token Expiration
JWTs expire. Handle gracefully:
typescript
if (isExpired(token)) {
return new Response('Token expired', { status: 401 })
}