Blazingly fast authentication for Cloudflare Workers β‘οΈ
Add enterprise-grade authentication to your Cloudflare Workers in just a few lines of code. Built for speed with edge caching, zero dependencies, and native Web Crypto API.
- Stupid Simple API - Authenticate requests in 3 lines of code
- Blazingly Fast - JWT verification using native Web Crypto API
- Edge Caching - Automatic JWKS and token caching for sub-millisecond auth
- Zero Dependencies - Pure TypeScript, no external dependencies
- TypeScript First - Full type safety and IntelliSense support
- Flexible - Works with any JWT-based auth provider (Rownd, Auth0, Firebase, etc.)
- Middleware Pattern - Easy integration with existing Workers
npm install @rownd/cloudflareimport { RowndAuth } from '@rownd/cloudflare';
const auth = new RowndAuth({
appKey: 'your-app-key',
appSecret: 'your-app-secret',
});
export default {
async fetch(request: Request): Promise<Response> {
// Authenticate the request
const authContext = await auth.authenticate(request);
if (!authContext.isAuthenticated) {
return new Response('Unauthorized', { status: 401 });
}
// Access user info
const { user } = authContext;
return new Response(`Hello, ${user?.email}!`);
},
};import { RowndAuth } from '@rownd/cloudflare';
const auth = new RowndAuth({
appKey: 'your-app-key',
appSecret: 'your-app-secret',
});
export default {
async fetch(request: Request): Promise<Response> {
const url = new URL(request.url);
// Public route
if (url.pathname === '/public') {
return new Response('Public content');
}
// Protected route - automatically returns 401 if not authenticated
if (url.pathname === '/protected') {
return auth.requireAuth()(request, async (req, authContext) => {
return new Response(`Welcome, ${authContext.user?.userId}!`);
});
}
// Optional auth - doesn't reject unauthenticated requests
if (url.pathname === '/optional') {
return auth.withAuth()(request, async (req, authContext) => {
if (authContext.isAuthenticated) {
return new Response(`Hello, ${authContext.user?.email}!`);
}
return new Response('Hello, guest!');
});
}
return new Response('Not found', { status: 404 });
},
};const auth = new RowndAuth({
// Required: Your application credentials
appKey: 'your-app-key',
appSecret: 'your-app-secret',
// Optional: Custom JWKS endpoint (defaults to Rownd's endpoint)
jwksUrl: 'https://your-auth-provider.com/.well-known/jwks.json',
// Optional: JWKS cache TTL in seconds (default: 3600 = 1 hour)
jwksCacheTtl: 3600,
// Optional: Token cache TTL in seconds (default: 300 = 5 minutes)
tokenCacheTtl: 300,
// Optional: Custom auth header name (default: 'authorization')
authHeader: 'x-auth-token',
// Optional: Cookie name to check for auth token
authCookie: 'auth_token',
});Main authentication class.
Authenticates a request and returns the auth context.
const authContext = await auth.authenticate(request);
if (authContext.isAuthenticated) {
console.log('User ID:', authContext.user?.userId);
console.log('Email:', authContext.user?.email);
console.log('Custom data:', authContext.user?.data);
}Returns a middleware function that rejects unauthenticated requests with 401.
return auth.requireAuth()(request, async (req, authContext) => {
// This code only runs if authenticated
return new Response(`Hello, ${authContext.user?.userId}!`);
});Returns a middleware function that passes auth context but doesn't reject unauthenticated requests.
return auth.withAuth()(request, async (req, authContext) => {
if (authContext.isAuthenticated) {
return new Response(`Hello, ${authContext.user?.email}!`);
}
return new Response('Hello, guest!');
});interface AuthContext {
user: AuthenticatedUser | null;
isAuthenticated: boolean;
}interface AuthenticatedUser {
userId: string;
email?: string;
data?: Record<string, any>;
claims: Record<string, any>; // Full JWT payload
token: string; // Raw JWT token
}import { Hono } from 'hono';
import { RowndAuth } from '@rownd/cloudflare';
const app = new Hono();
const auth = new RowndAuth({
appKey: 'your-app-key',
appSecret: 'your-app-secret',
});
// Custom middleware
app.use('/api/*', async (c, next) => {
const authContext = await auth.authenticate(c.req.raw);
if (!authContext.isAuthenticated) {
return c.json({ error: 'Unauthorized' }, 401);
}
c.set('user', authContext.user);
await next();
});
app.get('/api/profile', (c) => {
const user = c.get('user');
return c.json({ user });
});
export default app;import { Router } from 'itty-router';
import { RowndAuth } from '@rownd/cloudflare';
const router = Router();
const auth = new RowndAuth({
appKey: 'your-app-key',
appSecret: 'your-app-secret',
});
// Custom auth middleware
const withAuth = async (request: Request) => {
const authContext = await auth.authenticate(request);
if (!authContext.isAuthenticated) {
return new Response('Unauthorized', { status: 401 });
}
request.user = authContext.user;
};
router.get('/api/protected', withAuth, (request) => {
return new Response(`Hello, ${request.user.email}!`);
});
export default {
fetch: router.handle,
};import { RowndAuth } from '@rownd/cloudflare';
const auth = new RowndAuth({
appKey: 'your-app-key',
appSecret: 'your-app-secret',
});
export default {
async fetch(request: Request): Promise<Response> {
const authContext = await auth.authenticate(request);
if (!authContext.isAuthenticated) {
return new Response('Unauthorized', { status: 401 });
}
// Access full JWT claims for custom validation
const { claims } = authContext.user!;
// Check custom claims
if (claims.role !== 'admin') {
return new Response('Forbidden', { status: 403 });
}
// Check audience
if (!claims.aud?.includes('my-api')) {
return new Response('Invalid audience', { status: 403 });
}
return new Response('Admin access granted');
},
};The SDK automatically checks for tokens in the following order:
- Authorization Header:
Authorization: Bearer <token> - Custom Header: If
authHeaderis configured - Cookie: If
authCookieis configured
const auth = new RowndAuth({
appKey: 'your-app-key',
appSecret: 'your-app-secret',
authCookie: 'auth_token', // Check this cookie for the token
});The SDK is optimized for edge performance:
- JWKS Caching: Public keys are cached at the edge for 1 hour (configurable)
- Token Caching: Verified tokens are cached for 5 minutes (configurable)
- Zero Cold Start: No dependencies means instant initialization
- Native Crypto: Uses Web Crypto API for hardware-accelerated verification
- First request (cold): ~50ms (includes JWKS fetch)
- Cached requests: <1ms
- Memory usage: <1MB
While designed for Rownd, the SDK works with any JWT-based auth provider:
const auth = new RowndAuth({
appKey: 'not-used',
appSecret: 'not-used',
jwksUrl: 'https://YOUR-DOMAIN.auth0.com/.well-known/jwks.json',
});const auth = new RowndAuth({
appKey: 'not-used',
appSecret: 'not-used',
jwksUrl: 'https://www.googleapis.com/service_accounts/v1/jwk/securetoken@system.gserviceaccount.com',
});const auth = new RowndAuth({
appKey: 'not-used',
appSecret: 'not-used',
jwksUrl: 'https://YOUR-DOMAIN.clerk.accounts.dev/.well-known/jwks.json',
});# Install dependencies
npm install
# Build
npm run build
# Watch mode
npm run dev
# Run tests
npm test
# Format code
npm run format
# Lint
npm run lintMIT
Contributions are welcome! Please feel free to submit a Pull Request.
- π Documentation
- π¬ Discord Community
- π§ Email Support
- π Issue Tracker
Made with β€οΈ by Rownd