@prism-engineer/router
Version:
Type-safe Express.js router with automatic client generation
589 lines • 23.4 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const vitest_1 = require("vitest");
const createAuthScheme_1 = require("../../createAuthScheme");
const createApiRoute_1 = require("../../createApiRoute");
const router_1 = require("../../router");
const typebox_1 = require("@sinclair/typebox");
(0, vitest_1.describe)('Authentication - Auth Middleware', () => {
let router;
(0, vitest_1.beforeEach)(() => {
router = (0, router_1.createRouter)();
vitest_1.vi.clearAllMocks();
});
(0, vitest_1.it)('should integrate auth middleware with Express router', () => {
const authScheme = (0, createAuthScheme_1.createAuthScheme)({
name: 'middleware-test',
validate: async (req) => {
const token = req.headers.authorization;
if (!token) {
throw new Error('No token provided');
}
return { user: { id: '1', token } };
}
});
const route = (0, createApiRoute_1.createApiRoute)({
path: '/api/protected',
method: 'GET',
auth: authScheme,
response: {
200: {
contentType: 'application/json',
body: typebox_1.Type.Object({ message: typebox_1.Type.String() })
}
},
handler: async (req) => {
return {
status: 200,
body: { message: `Authenticated as ${req.auth.context.user.id}` }
};
}
});
(0, vitest_1.expect)(() => router.registerRoute(route)).not.toThrow();
(0, vitest_1.expect)(router.app).toBeDefined();
});
(0, vitest_1.it)('should handle middleware execution order', () => {
const executionOrder = [];
const authScheme = (0, createAuthScheme_1.createAuthScheme)({
name: 'order-test',
validate: async (req) => {
executionOrder.push('auth-validate');
return { user: { id: '1' } };
}
});
const route = (0, createApiRoute_1.createApiRoute)({
path: '/api/order-test',
method: 'GET',
auth: authScheme,
response: {
200: {
contentType: 'application/json',
body: typebox_1.Type.Object({ order: typebox_1.Type.Array(typebox_1.Type.String()) })
}
},
handler: async () => {
executionOrder.push('route-handler');
return {
status: 200,
body: { order: executionOrder }
};
}
});
(0, vitest_1.expect)(() => router.registerRoute(route)).not.toThrow();
});
(0, vitest_1.it)('should handle middleware with multiple auth schemes', () => {
const bearerAuth = (0, createAuthScheme_1.createAuthScheme)({
name: 'bearer',
validate: async (req) => {
const auth = req.headers.authorization;
if (!auth?.startsWith('Bearer ')) {
throw new Error('Bearer token required');
}
return { user: { id: '1', type: 'bearer' } };
}
});
const apiKeyAuth = (0, createAuthScheme_1.createAuthScheme)({
name: 'apiKey',
validate: async (req) => {
const apiKey = req.headers['x-api-key'];
if (!apiKey) {
throw new Error('API key required');
}
return { client: { id: '1', type: 'api-key' } };
}
});
const route = (0, createApiRoute_1.createApiRoute)({
path: '/api/multi-auth-middleware',
method: 'GET',
auth: [bearerAuth, apiKeyAuth],
response: {
200: {
contentType: 'application/json',
body: typebox_1.Type.Object({ authType: typebox_1.Type.String() })
}
},
handler: async (req) => {
return {
status: 200,
body: { authType: req.auth.name }
};
}
});
(0, vitest_1.expect)(() => router.registerRoute(route)).not.toThrow();
});
(0, vitest_1.it)('should handle auth middleware with request transformation', () => {
const authScheme = (0, createAuthScheme_1.createAuthScheme)({
name: 'transform-middleware',
validate: async (req) => {
// Middleware can transform request
const userId = req.headers['x-user-id'];
const orgId = req.headers['x-org-id'];
return {
user: {
id: userId || 'anonymous',
organizationId: orgId || 'default'
},
enrichedRequest: {
timestamp: Date.now(),
ip: req.ip || 'unknown'
}
};
}
});
const route = (0, createApiRoute_1.createApiRoute)({
path: '/api/transform-middleware',
method: 'GET',
auth: authScheme,
response: {
200: {
contentType: 'application/json',
body: typebox_1.Type.Object({
userId: typebox_1.Type.String(),
hasEnrichment: typebox_1.Type.Boolean()
})
}
},
handler: async (req) => {
return {
status: 200,
body: {
userId: req.auth.context.user.id,
hasEnrichment: !!req.auth.context.enrichedRequest
}
};
}
});
(0, vitest_1.expect)(() => router.registerRoute(route)).not.toThrow();
});
(0, vitest_1.it)('should handle async middleware operations', () => {
const authScheme = (0, createAuthScheme_1.createAuthScheme)({
name: 'async-middleware',
validate: async (req) => {
// Simulate async database lookup
await new Promise(resolve => setTimeout(resolve, 1));
const token = req.headers.authorization;
if (!token) {
throw new Error('Token required');
}
// Simulate async validation
await new Promise(resolve => setTimeout(resolve, 1));
return {
user: {
id: '1',
validatedAt: new Date().toISOString()
}
};
}
});
const route = (0, createApiRoute_1.createApiRoute)({
path: '/api/async-middleware',
method: 'GET',
auth: authScheme,
response: {
200: {
contentType: 'application/json',
body: typebox_1.Type.Object({ validatedAt: typebox_1.Type.String() })
}
},
handler: async (req) => {
return {
status: 200,
body: { validatedAt: req.auth.context.user.validatedAt }
};
}
});
(0, vitest_1.expect)(() => router.registerRoute(route)).not.toThrow();
});
(0, vitest_1.it)('should handle middleware error handling', () => {
const authScheme = (0, createAuthScheme_1.createAuthScheme)({
name: 'error-middleware',
validate: async (req) => {
const shouldFail = req.headers['x-force-error'];
if (shouldFail) {
throw new Error('Forced authentication error');
}
return { user: { id: '1' } };
}
});
const route = (0, createApiRoute_1.createApiRoute)({
path: '/api/error-middleware',
method: 'GET',
auth: authScheme,
response: {
200: {
contentType: 'application/json',
body: typebox_1.Type.Object({ message: typebox_1.Type.String() })
}
},
handler: async () => {
return {
status: 200,
body: { message: 'Success' }
};
}
});
(0, vitest_1.expect)(() => router.registerRoute(route)).not.toThrow();
});
(0, vitest_1.it)('should handle middleware with caching layer', () => {
const cache = new Map();
const authScheme = (0, createAuthScheme_1.createAuthScheme)({
name: 'cache-middleware',
validate: async (req) => {
const token = req.headers.authorization;
const cacheKey = `auth:${token}`;
// Check cache first
if (cache.has(cacheKey)) {
const cached = cache.get(cacheKey);
return {
...cached,
fromCache: true
};
}
// Simulate expensive validation
await new Promise(resolve => setTimeout(resolve, 1));
const result = {
user: { id: '1', token },
fromCache: false
};
// Cache for future requests
cache.set(cacheKey, result);
setTimeout(() => cache.delete(cacheKey), 100); // TTL
return result;
}
});
const route = (0, createApiRoute_1.createApiRoute)({
path: '/api/cache-middleware',
method: 'GET',
auth: authScheme,
response: {
200: {
contentType: 'application/json',
body: typebox_1.Type.Object({ fromCache: typebox_1.Type.Boolean() })
}
},
handler: async (req) => {
return {
status: 200,
body: { fromCache: req.auth.context.fromCache }
};
}
});
(0, vitest_1.expect)(() => router.registerRoute(route)).not.toThrow();
});
(0, vitest_1.it)('should handle middleware with rate limiting', () => {
const rateLimits = new Map();
const authScheme = (0, createAuthScheme_1.createAuthScheme)({
name: 'rate-limit-middleware',
validate: async (req) => {
const clientId = req.headers['x-client-id'] || req.ip;
const now = Date.now();
const windowMs = 60000; // 1 minute
const maxRequests = 100;
if (!rateLimits.has(clientId)) {
rateLimits.set(clientId, { count: 0, resetTime: now + windowMs });
}
const limit = rateLimits.get(clientId);
if (now > limit.resetTime) {
limit.count = 0;
limit.resetTime = now + windowMs;
}
limit.count++;
if (limit.count > maxRequests) {
throw new Error('Rate limit exceeded');
}
return {
user: { id: '1' },
rateLimit: {
remaining: maxRequests - limit.count,
resetTime: limit.resetTime
}
};
}
});
const route = (0, createApiRoute_1.createApiRoute)({
path: '/api/rate-limit-middleware',
method: 'GET',
auth: authScheme,
response: {
200: {
contentType: 'application/json',
body: typebox_1.Type.Object({ remaining: typebox_1.Type.Number() })
}
},
handler: async (req) => {
return {
status: 200,
body: { remaining: req.auth.context.rateLimit.remaining }
};
}
});
(0, vitest_1.expect)(() => router.registerRoute(route)).not.toThrow();
});
(0, vitest_1.it)('should handle middleware with request logging', () => {
const logs = [];
const authScheme = (0, createAuthScheme_1.createAuthScheme)({
name: 'logging-middleware',
validate: async (req) => {
const logEntry = {
timestamp: new Date().toISOString(),
method: req.method,
path: req.path,
userAgent: req.headers['user-agent'],
ip: req.ip
};
logs.push(logEntry);
return {
user: { id: '1' },
requestId: `req-${Date.now()}`
};
}
});
const route = (0, createApiRoute_1.createApiRoute)({
path: '/api/logging-middleware',
method: 'GET',
auth: authScheme,
response: {
200: {
contentType: 'application/json',
body: typebox_1.Type.Object({ requestId: typebox_1.Type.String() })
}
},
handler: async (req) => {
return {
status: 200,
body: { requestId: req.auth.context.requestId }
};
}
});
(0, vitest_1.expect)(() => router.registerRoute(route)).not.toThrow();
(0, vitest_1.expect)(logs).toBeDefined();
});
(0, vitest_1.it)('should handle middleware with context injection', () => {
const authScheme = (0, createAuthScheme_1.createAuthScheme)({
name: 'injection-middleware',
validate: async (req) => {
// Inject additional context based on request
const userAgent = req.headers['user-agent'];
const acceptLanguage = req.headers['accept-language'];
return {
user: { id: '1' },
context: {
device: {
type: userAgent?.includes('Mobile') ? 'mobile' : 'desktop',
userAgent
},
locale: {
language: acceptLanguage?.split(',')[0] || 'en',
acceptedLanguages: acceptLanguage?.split(',') || ['en']
},
request: {
timestamp: Date.now(),
protocol: req.headers['x-forwarded-proto'] || 'http',
secure: req.headers['x-forwarded-proto'] === 'https'
}
}
};
}
});
const route = (0, createApiRoute_1.createApiRoute)({
path: '/api/injection-middleware',
method: 'GET',
auth: authScheme,
response: {
200: {
contentType: 'application/json',
body: typebox_1.Type.Object({
deviceType: typebox_1.Type.String(),
language: typebox_1.Type.String(),
isSecure: typebox_1.Type.Boolean()
})
}
},
handler: async (req) => {
const ctx = req.auth.context.context;
return {
status: 200,
body: {
deviceType: ctx.device.type,
language: ctx.locale.language,
isSecure: ctx.request.secure
}
};
}
});
(0, vitest_1.expect)(() => router.registerRoute(route)).not.toThrow();
});
(0, vitest_1.it)('should handle middleware with external service integration', () => {
const authScheme = (0, createAuthScheme_1.createAuthScheme)({
name: 'external-service-middleware',
validate: async (req) => {
const token = req.headers.authorization?.replace('Bearer ', '');
// Simulate external service call
const validateWithExternalService = async (token) => {
// Mock external validation
if (token === 'external-valid') {
return {
userId: 'ext-user-123',
permissions: ['read', 'write'],
metadata: { provider: 'oauth2', scope: 'profile' }
};
}
throw new Error('Invalid external token');
};
try {
const externalResult = await validateWithExternalService(token);
return {
user: {
id: externalResult.userId,
permissions: externalResult.permissions,
external: true
},
provider: externalResult.metadata.provider
};
}
catch (error) {
throw new Error(`External validation failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
});
const route = (0, createApiRoute_1.createApiRoute)({
path: '/api/external-service-middleware',
method: 'GET',
auth: authScheme,
response: {
200: {
contentType: 'application/json',
body: typebox_1.Type.Object({
userId: typebox_1.Type.String(),
provider: typebox_1.Type.String()
})
}
},
handler: async (req) => {
return {
status: 200,
body: {
userId: req.auth.context.user.id,
provider: req.auth.context.provider
}
};
}
});
(0, vitest_1.expect)(() => router.registerRoute(route)).not.toThrow();
});
(0, vitest_1.it)('should handle middleware with session management', () => {
const sessions = new Map();
const authScheme = (0, createAuthScheme_1.createAuthScheme)({
name: 'session-middleware',
validate: async (req) => {
const sessionId = req.headers.cookie?.match(/sessionId=([^;]+)/)?.[1];
if (!sessionId) {
throw new Error('Session required');
}
if (!sessions.has(sessionId)) {
throw new Error('Invalid session');
}
const session = sessions.get(sessionId);
// Check expiration
if (session.expiresAt < Date.now()) {
sessions.delete(sessionId);
throw new Error('Session expired');
}
// Update last access
session.lastAccess = Date.now();
sessions.set(sessionId, session);
return {
user: session.user,
session: {
id: sessionId,
createdAt: session.createdAt,
lastAccess: session.lastAccess,
expiresAt: session.expiresAt
}
};
}
});
// Setup a test session
sessions.set('test-session', {
user: { id: 'user-123', name: 'Test User' },
createdAt: Date.now(),
lastAccess: Date.now(),
expiresAt: Date.now() + 3600000 // 1 hour
});
const route = (0, createApiRoute_1.createApiRoute)({
path: '/api/session-middleware',
method: 'GET',
auth: authScheme,
response: {
200: {
contentType: 'application/json',
body: typebox_1.Type.Object({
userId: typebox_1.Type.String(),
sessionId: typebox_1.Type.String()
})
}
},
handler: async (req) => {
return {
status: 200,
body: {
userId: req.auth.context.user.id,
sessionId: req.auth.context.session.id
}
};
}
});
(0, vitest_1.expect)(() => router.registerRoute(route)).not.toThrow();
});
(0, vitest_1.it)('should handle middleware with permission checking', () => {
const authScheme = (0, createAuthScheme_1.createAuthScheme)({
name: 'permission-middleware',
validate: async (req) => {
const userId = req.headers['x-user-id'];
const requiredPermission = req.headers['x-required-permission'];
// Mock user permissions
const userPermissions = {
'user-1': ['read', 'write'],
'user-2': ['read'],
'admin-1': ['read', 'write', 'delete', 'admin']
};
const permissions = userPermissions[userId] || [];
if (requiredPermission && !permissions.includes(requiredPermission)) {
throw new Error(`Permission denied: ${requiredPermission} required`);
}
return {
user: {
id: userId || 'anonymous',
permissions
},
hasRequiredPermission: !requiredPermission || permissions.includes(requiredPermission)
};
}
});
const route = (0, createApiRoute_1.createApiRoute)({
path: '/api/permission-middleware',
method: 'GET',
auth: authScheme,
response: {
200: {
contentType: 'application/json',
body: typebox_1.Type.Object({
userId: typebox_1.Type.String(),
permissionCount: typebox_1.Type.Number()
})
}
},
handler: async (req) => {
return {
status: 200,
body: {
userId: req.auth.context.user.id,
permissionCount: req.auth.context.user.permissions.length
}
};
}
});
(0, vitest_1.expect)(() => router.registerRoute(route)).not.toThrow();
});
});
//# sourceMappingURL=auth-middleware.test.js.map