UNPKG

web-vuln-scanner

Version:

Advanced, lightweight web vulnerability scanner with smart detection and easy-to-use interface

544 lines (475 loc) 15.6 kB
/** * Authentication Routes * Production-ready authentication endpoints with comprehensive security */ const express = require('express'); const rateLimit = require('express-rate-limit'); const helmet = require('helmet'); const cors = require('cors'); const { AuthMiddleware } = require('./auth-middleware'); const { UserService } = require('./user-service'); const { APIKeyManager } = require('./apikey-manager'); const { RBACManager, PERMISSIONS } = require('./rbac-manager'); const { Logger } = require('../monitoring/logger'); const { ConfigManager } = require('../config/config-manager'); // Rate limiter for admin health check const adminHealthLimiter = RateLimit({ windowMs: 60 * 1000, // 1 minute window max: 10, // limit each admin to 10 requests per minute message: { error: 'Too many health check requests from this user, please try again later.', code: 'RATE_LIMITED' } }); class AuthRouter { constructor() { this.router = express.Router(); // Admin delete limiter: max 5 deletes per 10 minutes per IP const adminDeleteLimiter = rateLimit({ windowMs: 10 * 60 * 1000, // 10 minutes max: 5, // limit each IP/admin to 5 delete requests per window message: { error: 'Too many user deletion requests. Please try again later.', code: 'RATE_LIMIT_EXCEEDED', }, standardHeaders: true, // Return rate limit info in headers legacyHeaders: false, }); this.config = ConfigManager.getInstance(); this.logger = new Logger(this.config.get('logging')); // Initialize services this.authMiddleware = new AuthMiddleware(); this.userService = new UserService(); this.apiKeyManager = new APIKeyManager(); this.rbacManager = new RBACManager(); // Setup middleware this.setupMiddleware(); // Setup routes this.setupRoutes(); } /** * Setup common middleware for auth routes */ setupMiddleware() { // Security headers this.router.use(helmet({ contentSecurityPolicy: { directives: { defaultSrc: ['\'self\''], scriptSrc: ['\'self\''], styleSrc: ['\'self\'', '\'unsafe-inline\''], imgSrc: ['\'self\'', 'data:', 'https:'], connectSrc: ['\'self\''], fontSrc: ['\'self\''], objectSrc: ['\'none\''], mediaSrc: ['\'self\''], frameSrc: ['\'none\''] } }, crossOriginEmbedderPolicy: false })); // CORS configuration this.router.use(cors({ origin: this.config.get('server.corsOrigins', ['http://localhost:3000']), credentials: true, methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'], allowedHeaders: ['Content-Type', 'Authorization', 'X-API-Key'] })); // Request parsing this.router.use(express.json({ limit: '10mb' })); this.router.use(express.urlencoded({ extended: true, limit: '10mb' })); // Security and audit logging this.router.use(this.authMiddleware.securityHeaders()); this.router.use(this.authMiddleware.auditLog()); } /** * Setup authentication routes */ setupRoutes() { // Public routes (no authentication required) this.setupPublicRoutes(); // Protected routes (authentication required) this.setupProtectedRoutes(); // Admin routes (admin role required) this.setupAdminRoutes(); } /** * Setup public authentication routes */ setupPublicRoutes() { const rateLimiters = this.authMiddleware.getRateLimiters(); // User registration this.router.post('/register', rateLimiters.general, async (req, res) => { try { const user = await this.userService.createUser(req.body); res.status(201).json({ success: true, message: 'User registered successfully', user: { id: user.id, email: user.email, firstName: user.firstName, lastName: user.lastName, roles: user.roles } }); } catch (error) { this.logger.error('Registration failed', { error: error.message, email: req.body.email }); res.status(400).json({ error: error.message, code: 'REGISTRATION_FAILED' }); } } ); // User login this.router.post('/login', rateLimiters.login, async (req, res) => { try { const { email, password } = req.body; const result = await this.userService.loginUser(email, password); res.json({ success: true, message: 'Login successful', user: { id: result.user.id, email: result.user.email, firstName: result.user.firstName, lastName: result.user.lastName, roles: result.user.roles, permissions: this.rbacManager.getUserPermissions(result.user) }, tokens: result.tokens }); } catch (error) { this.logger.error('Login failed', { error: error.message, email: req.body.email }); res.status(401).json({ error: error.message, code: 'LOGIN_FAILED' }); } } ); // Token refresh this.router.post('/refresh', rateLimiters.general, async (req, res) => { try { const { refreshToken } = req.body; if (!refreshToken) { return res.status(400).json({ error: 'Refresh token is required', code: 'REFRESH_TOKEN_MISSING' }); } const tokens = await this.userService.refreshAccessToken(refreshToken); res.json({ success: true, message: 'Token refreshed successfully', ...tokens }); } catch (error) { this.logger.error('Token refresh failed', { error: error.message }); res.status(401).json({ error: error.message, code: 'TOKEN_REFRESH_FAILED' }); } } ); // Health check this.router.get('/health', (req, res) => { res.json({ status: 'healthy', timestamp: new Date().toISOString(), service: 'authentication' }); }); // Get available roles and permissions (for UI) this.router.get('/roles', (req, res) => { res.json({ roles: this.rbacManager.getAllRoles(), permissions: this.rbacManager.getAllPermissions() }); }); } /** * Setup protected routes (require authentication) */ setupProtectedRoutes() { // Apply authentication to all subsequent routes this.router.use(this.authMiddleware.authenticate()); // User logout this.router.post('/logout', async (req, res) => { try { const { refreshToken } = req.body; if (refreshToken) { await this.userService.logoutUser(refreshToken); } res.json({ success: true, message: 'Logged out successfully' }); } catch (error) { this.logger.error('Logout failed', { error: error.message, userId: req.user?.id }); res.status(500).json({ error: 'Logout failed', code: 'LOGOUT_FAILED' }); } }); // Get current user profile this.router.get('/profile', (req, res) => { res.json({ user: { id: req.user.id, email: req.user.email, firstName: req.user.firstName, lastName: req.user.lastName, roles: req.user.roles, permissions: this.rbacManager.getUserPermissions(req.user), organization: req.user.organization, department: req.user.department, lastLoginAt: req.user.lastLoginAt } }); }); // Update user profile this.router.put('/profile', async (req, res) => { try { const updatedUser = await this.userService.updateUserProfile(req.user.id, req.body); res.json({ success: true, message: 'Profile updated successfully', user: updatedUser }); } catch (error) { this.logger.error('Profile update failed', { error: error.message, userId: req.user.id }); res.status(400).json({ error: error.message, code: 'PROFILE_UPDATE_FAILED' }); } }); // Change password this.router.post('/change-password', async (req, res) => { try { await this.userService.changePassword(req.user.id, req.body); res.json({ success: true, message: 'Password changed successfully' }); } catch (error) { this.logger.error('Password change failed', { error: error.message, userId: req.user.id }); res.status(400).json({ error: error.message, code: 'PASSWORD_CHANGE_FAILED' }); } }); // API Key management routes this.setupAPIKeyRoutes(); } /** * Setup API key management routes */ setupAPIKeyRoutes() { // Create API key this.router.post('/api-keys', this.authMiddleware.requirePermission(PERMISSIONS.APIKEY_CREATE), async (req, res) => { try { const apiKeyData = { ...req.body, userId: req.user.id }; const apiKey = await this.apiKeyManager.generateAPIKey(apiKeyData); res.status(201).json({ success: true, message: 'API key created successfully', apiKey }); } catch (error) { this.logger.error('API key creation failed', { error: error.message, userId: req.user.id }); res.status(400).json({ error: error.message, code: 'API_KEY_CREATION_FAILED' }); } } ); // List user's API keys this.router.get('/api-keys', this.authMiddleware.requirePermission(PERMISSIONS.APIKEY_READ), (req, res) => { try { const apiKeys = this.apiKeyManager.listAPIKeys(req.user.id); res.json({ success: true, apiKeys }); } catch (error) { this.logger.error('API key listing failed', { error: error.message, userId: req.user.id }); res.status(500).json({ error: 'Failed to retrieve API keys', code: 'API_KEY_LIST_FAILED' }); } } ); // Update API key this.router.put('/api-keys/:keyId', this.authMiddleware.requirePermission(PERMISSIONS.APIKEY_UPDATE), async (req, res) => { try { // TODO: Add ownership check for non-admin users await this.apiKeyManager.updateAPIKey(req.params.keyId, req.body); res.json({ success: true, message: 'API key updated successfully' }); } catch (error) { this.logger.error('API key update failed', { error: error.message, keyId: req.params.keyId }); res.status(400).json({ error: error.message, code: 'API_KEY_UPDATE_FAILED' }); } } ); // Revoke API key this.router.delete('/api-keys/:keyId', this.authMiddleware.requirePermission(PERMISSIONS.APIKEY_DELETE), async (req, res) => { try { // TODO: Add ownership check for non-admin users await this.apiKeyManager.revokeAPIKey(req.params.keyId, 'user_request'); res.json({ success: true, message: 'API key revoked successfully' }); } catch (error) { this.logger.error('API key revocation failed', { error: error.message, keyId: req.params.keyId }); res.status(400).json({ error: error.message, code: 'API_KEY_REVOCATION_FAILED' }); } } ); } /** * Setup admin-only routes */ setupAdminRoutes() { // Admin authentication middleware const adminAuth = [ this.authMiddleware.authenticate(), this.authMiddleware.requireAdmin() ]; // Rate limiter for admin routes const adminRateLimiter = rateLimit({ windowMs: 5 * 60 * 1000, // 5 minutes max: 30, // limit each admin IP to 30 requests per windowMs message: { error: 'Too many requests from this IP, please try again after 5 minutes.' } }); // List all users this.router.get('/admin/users', [adminRateLimiter, ...adminAuth], (req, res) => { try { const users = this.userService.listUsers(req.query); res.json({ success: true, users, total: users.length }); } catch (error) { this.logger.error('Admin user listing failed', { error: error.message }); res.status(500).json({ error: 'Failed to retrieve users', code: 'USER_LIST_FAILED' }); } }); // Get user statistics this.router.get('/admin/stats', [adminRateLimiter, ...adminAuth], (req, res) => { try { const stats = this.userService.getUserStats(); res.json({ success: true, stats }); } catch (error) { this.logger.error('Admin stats retrieval failed', { error: error.message }); res.status(500).json({ error: 'Failed to retrieve statistics', code: 'STATS_RETRIEVAL_FAILED' }); } }); // Delete user this.router.delete('/admin/users/:userId', [adminRateLimiter, ...adminAuth], async (req, res) => { try { await this.userService.deleteUser(req.params.userId, req.user.id); res.json({ success: true, message: 'User deleted successfully' }); } catch (error) { this.logger.error('Admin user deletion failed', { error: error.message, targetUserId: req.params.userId, adminUserId: req.user.id }); res.status(400).json({ error: error.message, code: 'USER_DELETION_FAILED' }); } }); // System health check this.router.get('/admin/health', [adminRateLimiter, ...adminAuth], (req, res) => { res.json({ status: 'healthy', timestamp: new Date().toISOString(), services: { authentication: 'running', authorization: 'running', apiKeys: 'running' }, metrics: { totalUsers: this.userService.getUserStats().total, activeApiKeys: this.apiKeyManager.listAPIKeys().length } }); }); } /** * Get the configured router */ getRouter() { return this.router; } /** * Get middleware functions for use in other routers */ getMiddleware() { return { authenticate: this.authMiddleware.authenticate(), authenticateJWT: this.authMiddleware.authenticateJWT(), authenticateAPIKey: this.authMiddleware.authenticateAPIKey(), optionalAuth: this.authMiddleware.optionalAuth(), requirePermission: (permission) => this.authMiddleware.requirePermission(permission), requireRole: (role) => this.authMiddleware.requireRole(role), requireAdmin: this.authMiddleware.requireAdmin(), requireOwnership: (field) => this.authMiddleware.requireOwnership(field), requireScope: (scope) => this.authMiddleware.requireScope(scope), rateLimiters: this.authMiddleware.getRateLimiters() }; } } module.exports = { AuthRouter };