-
Notifications
You must be signed in to change notification settings - Fork 11
Authentication & Security
R2-Manager-Worker uses Cloudflare Access (Zero Trust) to provide enterprise-grade authentication with GitHub SSO integration.
The application implements a Zero Trust security model where:
- All requests are authenticated by Cloudflare's edge network
- No user credentials are stored in the application
- JWT tokens are validated on every API request
- Access policies are managed centrally in the Cloudflare dashboard
┌─────────┐ ┌──────────────────┐ ┌─────────────┐
│ User │ │ Cloudflare Access │ │ Worker │
└────┬────┘ └────────┬─────────┘ └──────┬──────┘
│ │ │
│ 1. Visit App │ │
├────────────────────────>│ │
│ │ │
│ 2. Check Auth │ │
│<────────────────────────┤ │
│ │ │
│ 3. Redirect to GitHub │ │
├────────────────────────>│ │
│ │ │
│ 4. GitHub OAuth │ │
│<───────────────────────>│ │
│ │ │
│ 5. Set JWT Cookie │ │
│<────────────────────────┤ │
│ │ │
│ 6. Access App │ │
├────────────────────────────────────────────────────>│
│ │ │
│ │ 7. Validate JWT │
│ │<───────────────────────────┤
│ │ │
│ │ 8. Return User Email │
│ │────────────────────────────>
│ │ │
│ 9. Return Response │ │
│<────────────────────────────────────────────────────┤
- User visits your domain - Request reaches Cloudflare's edge network
- Cloudflare Access intercepts - Checks for valid authentication
- GitHub SSO login - User authenticates via GitHub OAuth
-
JWT cookie issued -
cf-access-jwt-assertioncookie is set automatically - Worker receives request - With JWT in cookie or header
- JWT validation - Worker validates JWT against Cloudflare's public keys
- User email extracted - From validated JWT payload
- Access granted - User can manage R2 buckets
The cf-access-jwt-assertion token is a standard JWT with three parts:
{
"alg": "RS256",
"kid": "key-id",
"typ": "JWT"
}{
"aud": ["your-application-aud-tag"],
"email": "user@example.com",
"exp": 1234567890,
"iat": 1234567800,
"nbf": 1234567800,
"iss": "https://yourteam.cloudflareaccess.com",
"type": "app",
"identity_nonce": "...",
"sub": "...",
"country": "US"
}Signed with Cloudflare's private key, validated using public keys from:
https://yourteam.cloudflareaccess.com/cdn-cgi/access/certs
The Worker validates JWT tokens using the jose library:
import { jwtVerify, createRemoteJWKSet } from "jose";
async function validateAccessJWT(
request: Request,
env: Env,
): Promise<string | null> {
// 1. Extract token from header
const token = request.headers.get("cf-access-jwt-assertion");
if (!token) return null;
// 2. Create JWK Set from Cloudflare's public keys
const JWKS = createRemoteJWKSet(
new URL(`${env.TEAM_DOMAIN}/cdn-cgi/access/certs`),
);
// 3. Verify token signature and claims
const { payload } = await jwtVerify(token, JWKS, {
issuer: env.TEAM_DOMAIN,
audience: env.POLICY_AUD,
});
// 4. Extract and return user email
return payload.email as string;
}-
Token extraction - Get JWT from
cf-access-jwt-assertionheader or cookie - Public key retrieval - Fetch Cloudflare's public keys from JWK endpoint
- Signature verification - Verify token was signed by Cloudflare
- Issuer check - Ensure token was issued by your team domain
- Audience check - Verify token is for your specific application
- Expiration check - Ensure token hasn't expired
- Email extraction - Extract user email from validated payload
- No stored credentials - No passwords, API keys, or tokens in the database
- Edge authentication - All authentication handled at Cloudflare's edge
- Policy-based access - Fine-grained control over who can access the app
- RS256 signing - Asymmetric cryptography prevents token forgery
- Short expiration - Tokens expire based on session duration (default: 24 hours)
- Audience validation - Tokens work only for specific applications
- Issuer validation - Tokens must come from your Cloudflare team
- MD5 checksum validation - All uploads verified using industry-standard MD5 checksums
- Client-side calculation - Checksums calculated before upload using spark-md5 library
- Server-side verification - ETag from R2 compared with client checksum
- Automatic detection - Identifies data corruption, network failures, and silent errors
- Visual feedback - UI displays verification status with "✓ Verified" indicator
- Prevents data loss - Ensures uploaded files match stored files exactly
- TLS encryption - All traffic encrypted via Cloudflare's network
- Automatic certificates - Cloudflare manages SSL/TLS certificates
- HSTS support - HTTP Strict Transport Security can be enabled
- HMAC-SHA256 signatures - Download URLs include tamper-proof signatures
- Timestamp-based - Each URL includes unique timestamp
- Path validation - Signatures cover full URL path and query parameters
function generateSignature(path: string, env: Env): string {
const encoder = new TextEncoder();
const data = encoder.encode(path + env.URL_SIGNING_KEY);
const hashArray = new Uint8Array(data);
const reducedHash = Array.from(hashArray).reduce((a, b) => a ^ b, 0);
return reducedHash.toString(16).padStart(2, "0");
}- Origin validation - CORS headers configured for legitimate domains
- Method restrictions - Only allowed HTTP methods permitted
- Header controls - Limited set of allowed request headers
- OAuth delegation - Authentication handled by GitHub
- SSO integration - Enterprise users can leverage existing identity providers
- Session management - Cloudflare manages session cookies
API endpoints are protected by intelligent rate limiting to prevent abuse and ensure fair resource allocation.
Tiered Rate Limits:
- READ Operations (GET): 100 requests per 60 seconds
- WRITE Operations (POST/PATCH): 30 requests per 60 seconds
- DELETE Operations: 10 requests per 60 seconds
Key Benefits:
- Abuse Prevention - Protects against brute force attacks and API abuse
- Fair Resource Sharing - Ensures no single user can monopolize system resources
- DDoS Mitigation - Automatic throttling of excessive requests
- Per-User Enforcement - Independent limits for each authenticated user
- Automatic Logging - All violations logged for security monitoring
How It Works:
- Rate limits are enforced after JWT validation
- Each user (identified by email) has independent rate limit counters
- Limits are applied per Cloudflare location for optimal performance
- When exceeded, requests return
429 Too Many Requestswith retry guidance - Violations are logged with timestamp, user email, and endpoint details
Response on Rate Limit Exceeded:
{
"error": "Rate limit exceeded",
"message": "You have exceeded the read operation rate limit of 100 requests per 60 seconds. Please wait before retrying.",
"tier": "READ",
"limit": 100,
"period": 60,
"retryAfter": 60
}Local Development:
Rate limiting is automatically bypassed for localhost requests to simplify development and testing workflows.
Configuration:
Rate limits are configurable via wrangler.toml and require Wrangler 4.36.0+. See Configuration Reference for details.
Cloudflare Access supports multiple policy types:
Allow Policies:
- Grant access to specific users, groups, or identity providers
- Example: Allow all users from GitHub organization "your-org"
Deny Policies:
- Block access for specific users or groups
- Example: Deny access to specific email addresses
Bypass Policies:
- Skip authentication for specific paths
- Example: Allow public access to
/site.webmanifest,/favicon.ico
By default, R2-Manager-Worker is configured with:
- Allow Policy - Authenticated GitHub users
- Bypass Policy - Static assets (manifests, icons, logos)
To limit access to specific users:
Update your Access application policies in the Cloudflare dashboard:
- Go to Access → Applications → Select your app
- Edit the Allow policy
- Change from "Everyone" to specific:
- GitHub organizations
- Email addresses
- Email domains
- Groups
Add email validation in the Worker code:
// In worker/index.ts
const ALLOWED_USERS = ["admin@example.com", "user@example.com"];
async function handleApiRequest(request: Request, env: Env): Promise<Response> {
const userEmail = await validateAccessJWT(request, env);
if (!userEmail) {
return new Response("Unauthorized", { status: 401 });
}
// Additional authorization check
if (!ALLOWED_USERS.includes(userEmail)) {
return new Response("Forbidden", { status: 403 });
}
// Continue with request handling...
}Future enhancements planned for access control:
- Per-bucket ownership tracking
- Role-based access control (RBAC)
- User-specific bucket views
- Audit logging with detailed tracking
During local development, JWT validation and rate limiting are disabled to simplify testing:
- Requests to
localhostskip authentication - Rate limiting is automatically bypassed
- All API endpoints are accessible without tokens
- Production JWT validation is not enforced
To test with authentication:
- Deploy to production, or
- Set
VITE_WORKER_APIto your deployed Worker URL
- Open your deployed application
- Log in via GitHub SSO
- Open DevTools (F12) → Application tab → Cookies
- Find
cf-access-jwt-assertionand copy its value - Visit jwt.io and paste the token
- Verify:
-
issmatches your team domain -
audcontains your Application Audience Tag - Token is not expired (check
exptimestamp)
-
curl -H "Cookie: cf-access-jwt-assertion=YOUR_JWT_TOKEN" \
https://YOUR_DOMAIN/api/bucketsReplace:
-
YOUR_JWT_TOKENwith the token from your browser -
YOUR_DOMAINwith your Worker's domain
Enable debug logging in worker/index.ts:
console.log("JWT Validation:", {
token: token ? "Present" : "Missing",
issuer: env.TEAM_DOMAIN,
audience: env.POLICY_AUD,
});View logs with:
npx wrangler tail- Keep
API_KEYandURL_SIGNING_KEYsecrets secure - Use GitHub organizations for team access control
- Enable 2FA on your Cloudflare account
- Regularly review Access logs in Cloudflare dashboard
- Set appropriate session durations (24 hours is reasonable)
- Use custom domains with your own SSL certificates
- Share JWT tokens between users
- Disable HTTPS or use HTTP in production
- Store secrets in code or version control
- Use weak or predictable signing keys
- Allow public access to API endpoints
- Bypass Cloudflare Access for authenticated routes
- Cloudflare Access Documentation
- JWT Validation Guide
- GitHub OAuth Apps
- jose Library Documentation
- Zero Trust Architecture
Questions? Check the Troubleshooting page or open an issue on GitHub.
- Home - Documentation overview
- Quick Start Guide - Get up and running in minutes
- Installation & Setup - Complete deployment guide
- Configuration Reference - Environment variables and settings
- Upgrade Guide - Database schema migrations
- Bucket Management - Create, rename, delete buckets
- Object Lifecycles - Automate expiration and IA transitions ⭐ NEW
- Local Uploads - Faster uploads via nearby edge storage ⭐ NEW
- Job History - Track bulk operations with audit trail ⭐ NEW
- Webhooks - Configure HTTP notifications for events ⭐ NEW
- AI Search - Semantic search with Cloudflare AI
- S3 Import - Migrate from AWS S3 to R2 ⭐ NEW
- Cross-Bucket Search - Search across all buckets with filters
- File Operations - Upload, download, move, copy, delete files
- Folder Management - Organize files hierarchically
- Signed URLs & Sharing - Generate secure shareable links
- Advanced Filtering - Filter by extension, size, and date
- Development Guide - Local setup and development workflow
- API Reference - Complete endpoint documentation
- Architecture Overview - Technical stack and design
- Authentication & Security - Zero Trust implementation
- JWT Validation - JWT token validation and verification
- Troubleshooting - Common issues and solutions
- FAQ - Frequently asked questions
- Roadmap - Planned features and enhancements