general 1. August 2024
Cloudflare Worker Auth Middleware
Lightweight Auth Middleware für Cloudflare Workers mit JWT Validation und Rate Limiting.
cloudflareworkersjwttypescriptauth
JWT Auth Middleware
Minimale JWT-Validierung für Cloudflare Workers ohne externe Dependencies.
interface Env {
JWT_SECRET: string;
RATE_LIMIT: KVNamespace;
}
interface JwtPayload {
sub: string;
exp: number;
iat: number;
}
export async function verifyJwt(token: string, secret: string): Promise<JwtPayload | null> {
try {
const [headerB64, payloadB64, signatureB64] = token.split('.');
const encoder = new TextEncoder();
const key = await crypto.subtle.importKey(
'raw',
encoder.encode(secret),
{ name: 'HMAC', hash: 'SHA-256' },
false,
['verify']
);
const signature = Uint8Array.from(atob(signatureB64.replace(/-/g, '+').replace(/_/g, '/')), (c) => c.charCodeAt(0));
const valid = await crypto.subtle.verify('HMAC', key, signature, encoder.encode(`${headerB64}.${payloadB64}`));
if (!valid) return null;
const payload: JwtPayload = JSON.parse(atob(payloadB64));
if (payload.exp * 1000 < Date.now()) return null;
return payload;
} catch {
return null;
}
}
export async function authMiddleware(request: Request, env: Env): Promise<Response | JwtPayload> {
const authHeader = request.headers.get('Authorization');
if (!authHeader?.startsWith('Bearer ')) {
return new Response(JSON.stringify({ error: 'Unauthorized' }), {
status: 401,
headers: { 'Content-Type': 'application/json' },
});
}
const token = authHeader.slice(7);
const payload = await verifyJwt(token, env.JWT_SECRET);
if (!payload) {
return new Response(JSON.stringify({ error: 'Invalid token' }), {
status: 401,
headers: { 'Content-Type': 'application/json' },
});
}
// Simple rate limiting
const key = `rate:${payload.sub}:${Math.floor(Date.now() / 60000)}`;
const count = parseInt(await env.RATE_LIMIT.get(key) || '0');
if (count > 100) {
return new Response(JSON.stringify({ error: 'Rate limited' }), {
status: 429,
headers: { 'Content-Type': 'application/json' },
});
}
await env.RATE_LIMIT.put(key, String(count + 1), { expirationTtl: 120 });
return payload;
}