web-vuln-scanner
Version:
Advanced, lightweight web vulnerability scanner with smart detection and easy-to-use interface
544 lines (475 loc) • 15.6 kB
JavaScript
/**
* 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 };