mya-cli
Version:
MYA - AI-Powered Stock & Options Analysis CLI Tool
93 lines • 3.39 kB
JavaScript
/**
* Module: Worker Middleware
* Purpose: Rate limiting and JWT authentication middleware for Cloudflare Worker
* Dependencies: jose (JWT verification), Cloudflare Workers API
* Used by: worker.ts
*
* Public Endpoints (no JWT required):
* - /health, /api/v1/health
* - /auth, /api/v1/auth (for initial login)
* - /verify-otp, /api/v1/verify-otp (for OTP verification)
* - /auth/verify, /api/v1/auth/verify (for token validation)
* - /announcements, /api/v1/announcements (market announcements)
*
* All other endpoints require valid JWT Bearer token in Authorization header
*/
import * as jose from 'jose';
export async function checkRateLimit(env, userId) {
const kv = env.KV_NAMESPACE;
const rateLimitPerMinute = env.RATE_LIMIT_REQUESTS || 60;
const key = `ratelimit:${userId}`;
const ttl = 60; // 1 minute window
try {
const current = await kv.get(key);
const count = current ? parseInt(current, 10) : 0;
if (count >= rateLimitPerMinute) {
return false;
}
await kv.put(key, String(count + 1), { expirationTtl: ttl });
return true;
}
catch (error) {
console.error('Rate limit check failed:', error);
return true; // Allow if check fails
}
}
export function getJwtMiddleware(env) {
// Public endpoints that don't require authentication
const publicEndpoints = [
'/health',
'/auth',
'/verify-otp',
'/auth/verify',
'/api/v1/health',
'/api/v1/auth',
'/api/v1/verify-otp',
'/api/v1/auth/verify',
'/api/v1/announcements'
];
return async (c, next) => {
const path = c.req.path;
const method = c.req.method;
// Check if this is a public endpoint
// Match exact path or paths that start with the endpoint followed by a slash
const isPublic = publicEndpoints.some(endpoint => {
return path === endpoint || path.startsWith(endpoint + '/');
});
// Log for debugging
if (!isPublic) {
console.log(`[AUTH] Non-public endpoint: ${method} ${path}`);
}
if (isPublic) {
// Public endpoints don't need JWT verification
c.set('userId', 'anonymous');
return next();
}
const authHeader = c.req.header('Authorization');
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return c.json({ error: 'Unauthorized: Missing or invalid Authorization header' }, 401);
}
const token = authHeader.substring(7);
try {
const secret = new TextEncoder().encode(env.JWT_SECRET);
const verified = await jose.jwtVerify(token, secret);
const userId = (verified.payload.userId || verified.payload.sub || 'anonymous');
c.set('userId', userId);
}
catch {
return c.json({ error: 'Unauthorized: Token verification failed' }, 401);
}
return next();
};
}
export function getRateLimitMiddleware(env) {
return async (c, next) => {
const userId = c.get('userId') || 'anonymous';
const allowed = await checkRateLimit(env, userId);
if (!allowed) {
return c.json({ error: 'Rate limit exceeded' }, 429);
}
return next();
};
}
//# sourceMappingURL=middleware.js.map