Security Authentication Issues.md - johnpeterman72/CursorRIPER.sigma GitHub Wiki

Security and Authentication Issues

Authentication System Problems

JWT Token Issues

Problem: JWT authentication failing or behaving inconsistently

Symptoms:

401 Unauthorized errors despite valid login
Token validation failures
"Invalid signature" errors
Users getting logged out unexpectedly

Solutions:

JWT Token Debugging and Validation

// jwt-debugger.js
const jwt = require('jsonwebtoken');

class JWTDebugger {
  constructor(secret) {
    this.secret = secret;
  }

  // Debug token without verification (inspect payload)
  debugToken(token) {
    try {
      // Decode without verification to inspect payload
      const decoded = jwt.decode(token, { complete: true });
      
      console.log('=== JWT Token Debug ===');
      console.log('Header:', decoded.header);
      console.log('Payload:', decoded.payload);
      
      // Check expiration
      if (decoded.payload.exp) {
        const expirationDate = new Date(decoded.payload.exp * 1000);
        const now = new Date();
        console.log('Expires:', expirationDate.toISOString());
        console.log('Current time:', now.toISOString());
        console.log('Is expired:', now > expirationDate);
        console.log('Time until expiry:', Math.round((expirationDate - now) / 1000), 'seconds');
      }
      
      // Check issued at
      if (decoded.payload.iat) {
        const issuedAt = new Date(decoded.payload.iat * 1000);
        console.log('Issued at:', issuedAt.toISOString());
      }
      
      return decoded;
    } catch (error) {
      console.error('Failed to decode token:', error.message);
      return null;
    }
  }

  // Verify token with detailed error reporting
  verifyToken(token) {
    try {
      const decoded = jwt.verify(token, this.secret);
      console.log('✓ Token verification successful');
      return { valid: true, decoded, error: null };
    } catch (error) {
      console.error('✗ Token verification failed:', error.message);
      
      let errorType = 'unknown';
      let suggestion = '';
      
      switch (error.name) {
        case 'TokenExpiredError':
          errorType = 'expired';
          suggestion = 'Token has expired. User needs to login again or refresh token.';
          break;
        case 'JsonWebTokenError':
          if (error.message.includes('invalid signature')) {
            errorType = 'invalid_signature';
            suggestion = 'Token signature is invalid. Check JWT secret consistency.';
          } else if (error.message.includes('malformed')) {
            errorType = 'malformed';
            suggestion = 'Token format is invalid. Check token transmission.';
          }
          break;
        case 'NotBeforeError':
          errorType = 'not_active';
          suggestion = 'Token is not active yet. Check nbf claim.';
          break;
      }
      
      return { 
        valid: false, 
        decoded: null, 
        error: {
          type: errorType,
          message: error.message,
          suggestion
        }
      };
    }
  }

  // Generate a test token for debugging
  generateTestToken(payload = {}, expiresIn = '1h') {
    const defaultPayload = {
      userId: 'test-user-123',
      email: '[email protected]',
      role: 'user',
      ...payload
    };

    return jwt.sign(defaultPayload, this.secret, { expiresIn });
  }

  // Test token lifecycle
  testTokenLifecycle() {
    console.log('=== JWT Token Lifecycle Test ===');
    
    // Generate token
    const token = this.generateTestToken({}, '10s'); // 10 second expiry for testing
    console.log('Generated token:', token.substring(0, 50) + '...');
    
    // Immediate verification
    let result = this.verifyToken(token);
    console.log('Immediate verification:', result.valid ? 'PASS' : 'FAIL');
    
    // Test expiration
    setTimeout(() => {
      console.log('\nTesting after 11 seconds (should be expired):');
      result = this.verifyToken(token);
      console.log('Expiration test:', result.valid ? 'FAIL' : 'PASS');
    }, 11000);
  }
}

// Usage
const debugger = new JWTDebugger(process.env.JWT_SECRET);

// Debug an existing token
const problematicToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...';
debugger.debugToken(problematicToken);
debugger.verifyToken(problematicToken);

Enhanced JWT Middleware with Better Error Handling

// enhanced-jwt-middleware.js
const jwt = require('jsonwebtoken');

class EnhancedJWTMiddleware {
  constructor(options = {}) {
    this.secret = options.secret || process.env.JWT_SECRET;
    this.algorithms = options.algorithms || ['HS256'];
    this.issuer = options.issuer;
    this.audience = options.audience;
    this.clockTolerance = options.clockTolerance || 60; // 60 seconds
    this.ignoreExpiration = options.ignoreExpiration || false;
    
    if (!this.secret) {
      throw new Error('JWT secret is required');
    }
  }

  middleware() {
    return async (req, res, next) => {
      try {
        const token = this.extractToken(req);
        
        if (!token) {
          return this.sendAuthError(res, 'missing_token', 'No authentication token provided');
        }

        const verification = await this.verifyTokenWithRetry(token);
        
        if (!verification.valid) {
          return this.sendAuthError(res, verification.error.type, verification.error.message);
        }

        // Attach user info to request
        req.user = verification.decoded;
        req.token = token;

        // Check if token is close to expiry (within 5 minutes)
        if (verification.decoded.exp) {
          const expiryTime = verification.decoded.exp * 1000;
          const fiveMinutes = 5 * 60 * 1000;
          
          if (expiryTime - Date.now() < fiveMinutes) {
            res.setHeader('X-Token-Refresh-Needed', 'true');
          }
        }

        next();
      } catch (error) {
        console.error('JWT middleware error:', error);
        this.sendAuthError(res, 'auth_error', 'Authentication failed');
      }
    };
  }

  extractToken(req) {
    // Check Authorization header (Bearer token)
    const authHeader = req.headers.authorization;
    if (authHeader && authHeader.startsWith('Bearer ')) {
      return authHeader.substring(7);
    }

    // Check query parameter (for WebSocket connections)
    if (req.query.token) {
      return req.query.token;
    }

    // Check cookies
    if (req.cookies && req.cookies.access_token) {
      return req.cookies.access_token;
    }

    return null;
  }

  async verifyTokenWithRetry(token, retries = 2) {
    for (let attempt = 0; attempt <= retries; attempt++) {
      try {
        const options = {
          algorithms: this.algorithms,
          clockTolerance: this.clockTolerance,
          ignoreExpiration: this.ignoreExpiration
        };

        if (this.issuer) options.issuer = this.issuer;
        if (this.audience) options.audience = this.audience;

        const decoded = jwt.verify(token, this.secret, options);
        
        // Additional validation
        if (!decoded.userId && !decoded.sub) {
          throw new Error('Token missing user identifier');
        }

        return { valid: true, decoded, error: null };

      } catch (error) {
        if (attempt === retries) {
          return this.categorizeJWTError(error);
        }
        
        // Brief delay before retry
        await new Promise(resolve => setTimeout(resolve, 100));
      }
    }
  }

  categorizeJWTError(error) {
    let errorType = 'invalid_token';
    let message = 'Invalid authentication token';

    switch (error.name) {
      case 'TokenExpiredError':
        errorType = 'token_expired';
        message = 'Authentication token has expired';
        break;
      case 'JsonWebTokenError':
        if (error.message.includes('invalid signature')) {
          errorType = 'invalid_signature';
          message = 'Authentication token signature is invalid';
        } else if (error.message.includes('malformed')) {
          errorType = 'malformed_token';
          message = 'Authentication token is malformed';
        }
        break;
      case 'NotBeforeError':
        errorType = 'token_not_active';
        message = 'Authentication token is not yet active';
        break;
    }

    return { valid: false, decoded: null, error: { type: errorType, message } };
  }

  sendAuthError(res, type, message) {
    const statusCode = this.getStatusCodeForError(type);
    
    res.status(statusCode).json({
      error: {
        code: type,
        message,
        timestamp: new Date().toISOString()
      }
    });
  }

  getStatusCodeForError(type) {
    switch (type) {
      case 'missing_token':
      case 'malformed_token':
      case 'invalid_signature':
      case 'invalid_token':
        return 401;
      case 'token_expired':
        return 401;
      case 'token_not_active':
        return 401;
      default:
        return 401;
    }
  }

  // Utility method to generate tokens
  generateToken(payload, options = {}) {
    const defaultOptions = {
      expiresIn: '24h',
      issuer: this.issuer,
      audience: this.audience
    };

    return jwt.sign(payload, this.secret, { ...defaultOptions, ...options });
  }

  // Utility method to refresh tokens
  refreshToken(oldToken) {
    try {
      // Verify old token (ignoring expiration)
      const decoded = jwt.verify(oldToken, this.secret, { 
        ignoreExpiration: true,
        algorithms: this.algorithms
      });

      // Remove JWT specific claims
      const { iat, exp, nbf, ...payload } = decoded;

      // Generate new token
      return this.generateToken(payload);
    } catch (error) {
      throw new Error('Cannot refresh invalid token');
    }
  }
}

// Usage
const jwtMiddleware = new EnhancedJWTMiddleware({
  secret: process.env.JWT_SECRET,
  issuer: 'cursoriper-framework',
  audience: 'cursoriper-users',
  clockTolerance: 30
});

module.exports = jwtMiddleware;

Session Management Issues

Problem: User sessions not persisting or behaving unexpectedly

Symptoms:

Users logged out randomly
Session data lost between requests
"Session not found" errors
Multiple concurrent sessions issues

Solutions:

Robust Session Management

// session-manager.js
const session = require('express-session');
const MongoStore = require('connect-mongo');
const RedisStore = require('connect-redis')(session);
const Redis = require('redis');

class SessionManager {
  constructor(options = {}) {
    this.options = {
      secret: options.secret || process.env.SESSION_SECRET,
      name: options.name || 'bmad.session',
      maxAge: options.maxAge || 24 * 60 * 60 * 1000, // 24 hours
      secure: options.secure !== undefined ? options.secure : process.env.NODE_ENV === 'production',
      httpOnly: options.httpOnly !== undefined ? options.httpOnly : true,
      sameSite: options.sameSite || (process.env.NODE_ENV === 'production' ? 'strict' : 'lax'),
      rolling: options.rolling !== undefined ? options.rolling : true,
      store: options.store || 'memory',
      ...options
    };

    this.store = this.createStore();
  }

  createStore() {
    switch (this.options.store) {
      case 'redis':
        return this.createRedisStore();
      case 'mongodb':
        return this.createMongoStore();
      case 'memory':
      default:
        console.warn('Using memory store for sessions - not recommended for production');
        return undefined; // Express will use MemoryStore
    }
  }

  createRedisStore() {
    try {
      const redisClient = Redis.createClient({
        host: this.options.redis?.host || 'localhost',
        port: this.options.redis?.port || 6379,
        password: this.options.redis?.password,
        db: this.options.redis?.db || 1
      });

      redisClient.on('error', (error) => {
        console.error('Redis session store error:', error);
      });

      redisClient.on('connect', () => {
        console.log('Redis session store connected');
      });

      return new RedisStore({ 
        client: redisClient,
        prefix: 'bmad:session:',
        ttl: this.options.maxAge / 1000 // Redis expects TTL in seconds
      });
    } catch (error) {
      console.error('Failed to create Redis session store:', error);
      console.log('Falling back to memory store');
      return undefined;
    }
  }

  createMongoStore() {
    try {
      if (!this.options.mongodb?.uri) {
        throw new Error('MongoDB URI is required for MongoDB session store');
      }

      return MongoStore.create({
        mongoUrl: this.options.mongodb.uri,
        dbName: this.options.mongodb.dbName || 'sessions',
        collectionName: 'bmad_sessions',
        ttl: this.options.maxAge / 1000, // MongoDB expects TTL in seconds
        autoRemove: 'native',
        touchAfter: 24 * 3600 // Update session once per 24 hours unless changed
      });
    } catch (error) {
      console.error('Failed to create MongoDB session store:', error);
      console.log('Falling back to memory store');
      return undefined;
    }
  }

  getMiddleware() {
    const sessionConfig = {
      secret: this.options.secret,
      name: this.options.name,
      resave: false,
      saveUninitialized: false,
      rolling: this.options.rolling,
      store: this.store,
      cookie: {
        secure: this.options.secure,
        httpOnly: this.options.httpOnly,
        maxAge: this.options.maxAge,
        sameSite: this.options.sameSite
      }
    };

    // Validate configuration
    if (!sessionConfig.secret) {
      throw new Error('Session secret is required');
    }

    if (sessionConfig.secret === 'your-session-secret') {
      console.warn('⚠️  Using default session secret - change this in production!');
    }

    return session(sessionConfig);
  }

  // Session debugging middleware
  debugMiddleware() {
    return (req, res, next) => {
      console.log('Session Debug:', {
        sessionID: req.sessionID,
        session: req.session,
        cookies: req.headers.cookie,
        userAgent: req.headers['user-agent'],
        ip: req.ip
      });
      next();
    };
  }

  // Session validation middleware
  validateSession() {
    return (req, res, next) => {
      if (!req.session) {
        return res.status(500).json({
          error: 'Session not available',
          message: 'Session store is not working properly'
        });
      }

      // Check for session hijacking
      if (req.session.userAgent && req.session.userAgent !== req.headers['user-agent']) {
        console.warn('Potential session hijacking detected:', {
          sessionID: req.sessionID,
          originalUA: req.session.userAgent,
          currentUA: req.headers['user-agent'],
          ip: req.ip
        });

        // Destroy potentially hijacked session
        req.session.destroy((err) => {
          if (err) console.error('Failed to destroy session:', err);
        });

        return res.status(401).json({
          error: 'Session invalid',
          message: 'Please login again'
        });
      }

      // Store user agent on first request
      if (!req.session.userAgent) {
        req.session.userAgent = req.headers['user-agent'];
      }

      next();
    };
  }

  // Utility methods
  async getSessionCount() {
    if (!this.store || !this.store.length) {
      return null; // Not supported by store
    }

    return new Promise((resolve, reject) => {
      this.store.length((err, count) => {
        if (err) reject(err);
        else resolve(count);
      });
    });
  }

  async getAllSessions() {
    if (!this.store || !this.store.all) {
      return null; // Not supported by store
    }

    return new Promise((resolve, reject) => {
      this.store.all((err, sessions) => {
        if (err) reject(err);
        else resolve(sessions);
      });
    });
  }

  async destroySession(sessionId) {
    if (!this.store || !this.store.destroy) {
      return false;
    }

    return new Promise((resolve) => {
      this.store.destroy(sessionId, (err) => {
        if (err) {
          console.error('Failed to destroy session:', err);
          resolve(false);
        } else {
          resolve(true);
        }
      });
    });
  }

  async clearAllSessions() {
    if (!this.store || !this.store.clear) {
      return false;
    }

    return new Promise((resolve) => {
      this.store.clear((err) => {
        if (err) {
          console.error('Failed to clear sessions:', err);
          resolve(false);
        } else {
          console.log('All sessions cleared');
          resolve(true);
        }
      });
    });
  }
}

// Usage
const sessionManager = new SessionManager({
  secret: process.env.SESSION_SECRET,
  store: 'redis',
  redis: {
    host: process.env.REDIS_HOST || 'localhost',
    port: process.env.REDIS_PORT || 6379,
    password: process.env.REDIS_PASSWORD
  },
  secure: process.env.NODE_ENV === 'production',
  maxAge: 24 * 60 * 60 * 1000 // 24 hours
});

module.exports = sessionManager;

Authorization and Permissions

Role-Based Access Control Issues

Problem: Users accessing resources they shouldn't have access to

Symptoms:

403 Forbidden errors for valid users
Users seeing data from other organizations
Role escalation vulnerabilities
Inconsistent permission checking

Solutions:

Comprehensive RBAC System

// rbac-manager.js
class RBACManager {
  constructor(database) {
    this.db = database;
    this.permissionCache = new Map();
    this.roleHierarchy = new Map();
    this.setupRoleHierarchy();
  }

  setupRoleHierarchy() {
    // Define role inheritance
    this.roleHierarchy.set('admin', ['manager', 'user', 'viewer']);
    this.roleHierarchy.set('manager', ['user', 'viewer']);
    this.roleHierarchy.set('user', ['viewer']);
    this.roleHierarchy.set('viewer', []);
  }

  async checkPermission(userId, permission, resource = null) {
    try {
      // Get user roles
      const userRoles = await this.getUserRoles(userId);
      
      if (userRoles.length === 0) {
        console.warn(`No roles found for user ${userId}`);
        return false;
      }

      // Check each role and inherited roles
      for (const role of userRoles) {
        const allRoles = this.getInheritedRoles(role.name);
        
        for (const roleName of allRoles) {
          const hasPermission = await this.roleHasPermission(roleName, permission, resource, userId);
          if (hasPermission) {
            return true;
          }
        }
      }

      console.log(`Permission denied: ${userId} does not have ${permission} on ${resource?.type}:${resource?.id}`);
      return false;
    } catch (error) {
      console.error('Permission check error:', error);
      return false; // Fail securely
    }
  }

  async roleHasPermission(roleName, permission, resource, userId) {
    // Check cached permissions
    const cacheKey = `${roleName}:${permission}:${resource?.type || 'global'}:${resource?.id || 'all'}`;
    
    if (this.permissionCache.has(cacheKey)) {
      const cached = this.permissionCache.get(cacheKey);
      if (cached.expires > Date.now()) {
        return cached.value;
      }
      this.permissionCache.delete(cacheKey);
    }

    // Query database for permission
    let hasPermission = false;

    // Check direct role permissions
    const directPermissions = await this.db.query(`
      SELECT rp.* FROM role_permissions rp 
      JOIN roles r ON rp.role_id = r.id 
      WHERE r.name = ? AND rp.permission = ? AND rp.active = true
    `, [roleName, permission]);

    if (directPermissions.length > 0) {
      // Check resource-specific permissions
      if (resource) {
        hasPermission = await this.checkResourcePermission(
          directPermissions, 
          resource, 
          userId
        );
      } else {
        hasPermission = true;
      }
    }

    // Cache result for 5 minutes
    this.permissionCache.set(cacheKey, {
      value: hasPermission,
      expires: Date.now() + 5 * 60 * 1000
    });

    return hasPermission;
  }

  async checkResourcePermission(permissions, resource, userId) {
    for (const permission of permissions) {
      // Check resource type match
      if (permission.resource_type && permission.resource_type !== resource.type) {
        continue;
      }

      // Check resource ID match (if specified)
      if (permission.resource_id && permission.resource_id !== resource.id) {
        continue;
      }

      // Check organization context
      if (permission.organization_only) {
        const userOrg = await this.getUserOrganization(userId);
        const resourceOrg = await this.getResourceOrganization(resource);
        
        if (userOrg !== resourceOrg) {
          continue;
        }
      }

      // Check custom conditions
      if (permission.conditions) {
        const conditionsMet = await this.evaluateConditions(
          permission.conditions, 
          resource, 
          userId
        );
        if (!conditionsMet) {
          continue;
        }
      }

      return true; // Permission granted
    }

    return false; // No matching permission found
  }

  async evaluateConditions(conditions, resource, userId) {
    try {
      const parsedConditions = JSON.parse(conditions);
      
      // Evaluate each condition
      for (const condition of parsedConditions) {
        switch (condition.type) {
          case 'owner':
            if (resource.created_by !== userId) return false;
            break;
          case 'team_member':
            const isTeamMember = await this.isUserTeamMember(userId, resource.id);
            if (!isTeamMember) return false;
            break;
          case 'organization_member':
            const userOrg = await this.getUserOrganization(userId);
            const resourceOrg = await this.getResourceOrganization(resource);
            if (userOrg !== resourceOrg) return false;
            break;
          case 'custom':
            const customResult = await this.evaluateCustomCondition(
              condition.rule, 
              resource, 
              userId
            );
            if (!customResult) return false;
            break;
        }
      }
      
      return true; // All conditions met
    } catch (error) {
      console.error('Error evaluating conditions:', error);
      return false; // Fail securely
    }
  }

  getInheritedRoles(roleName) {
    const inherited = [roleName];
    const children = this.roleHierarchy.get(roleName) || [];
    
    children.forEach(childRole => {
      inherited.push(...this.getInheritedRoles(childRole));
    });
    
    return [...new Set(inherited)]; // Remove duplicates
  }

  async getUserRoles(userId) {
    return await this.db.query(`
      SELECT r.* FROM user_roles ur 
      JOIN roles r ON ur.role_id = r.id 
      WHERE ur.user_id = ? AND ur.active = true AND r.active = true
    `, [userId]);
  }

  async getUserOrganization(userId) {
    const [user] = await this.db.query(`
      SELECT organization_id FROM users WHERE id = ?
    `, [userId]);
    
    return user?.organization_id;
  }

  async getResourceOrganization(resource) {
    if (resource.organization_id) {
      return resource.organization_id;
    }

    // Try to fetch from database if not provided
    const [resourceData] = await this.db.query(`
      SELECT organization_id FROM ${resource.type}s WHERE id = ?
    `, [resource.id]);
    
    return resourceData?.organization_id;
  }

  async isUserTeamMember(userId, resourceId) {
    const [membership] = await this.db.query(`
      SELECT 1 FROM team_members tm
      JOIN teams t ON tm.team_id = t.id
      WHERE tm.user_id = ? AND t.resource_id = ? AND tm.active = true
    `, [userId, resourceId]);
    
    return !!membership;
  }

  // Middleware for Express.js
  requirePermission(permission, resourceExtractor = null) {
    return async (req, res, next) => {
      try {
        if (!req.user) {
          return res.status(401).json({
            error: 'Authentication required',
            message: 'Please login to access this resource'
          });
        }

        let resource = null;
        if (resourceExtractor) {
          resource = await resourceExtractor(req);
        }

        const hasPermission = await this.checkPermission(
          req.user.userId, 
          permission, 
          resource
        );

        if (!hasPermission) {
          return res.status(403).json({
            error: 'Permission denied',
            message: 'You do not have permission to perform this action'
          });
        }

        next();
      } catch (error) {
        console.error('Permission middleware error:', error);
        res.status(500).json({
          error: 'Authorization error',
          message: 'Failed to check permissions'
        });
      }
    };
  }

  // Utility method to clear permission cache
  clearPermissionCache(pattern = null) {
    if (pattern) {
      for (const key of this.permissionCache.keys()) {
        if (key.includes(pattern)) {
          this.permissionCache.delete(key);
        }
      }
    } else {
      this.permissionCache.clear();
    }
  }
}

// Usage examples
const rbac = new RBACManager(database);

// Check permission programmatically
const canEdit = await rbac.checkPermission(userId, 'bmad.model.edit', {
  type: 'business_model',
  id: modelId
});

// Use as middleware
app.put('/api/business-models/:id', 
  jwtMiddleware.middleware(),
  rbac.requirePermission('bmad.model.edit', async (req) => ({
    type: 'business_model',
    id: req.params.id
  })),
  updateBusinessModel
);

module.exports = RBACManager;

CORS and Security Headers

Cross-Origin Resource Sharing Issues

Problem: CORS errors preventing frontend from accessing API

Symptoms:

"Access to fetch at '...' has been blocked by CORS policy"
Preflight request failures
Missing CORS headers
Inconsistent CORS behavior

Solutions:

Comprehensive CORS Configuration

// cors-config.js
const cors = require('cors');

class CORSManager {
  constructor(options = {}) {
    this.options = {
      // Default allowed origins
      allowedOrigins: options.allowedOrigins || [
        'http://localhost:3000',
        'http://localhost:3001',
        'https://app.cursoriper.com',
        process.env.FRONTEND_URL
      ].filter(Boolean),
      
      // Development settings
      allowDevelopment: options.allowDevelopment !== undefined ? 
        options.allowDevelopment : process.env.NODE_ENV === 'development',
      
      // Credential support
      credentials: options.credentials !== undefined ? 
        options.credentials : true,
      
      // Methods
      methods: options.methods || ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'PATCH'],
      
      // Headers
      allowedHeaders: options.allowedHeaders || [
        'Origin',
        'X-Requested-With',
        'Content-Type',
        'Accept',
        'Authorization',
        'Cache-Control',
        'X-CSRF-Token'
      ],
      
      // Exposed headers
      exposedHeaders: options.exposedHeaders || [
        'X-Total-Count',
        'X-Page-Count',
        'X-Current-Page',
        'X-Token-Refresh-Needed'
      ],
      
      // Preflight cache
      maxAge: options.maxAge || 86400, // 24 hours
      
      ...options
    };
  }

  getCORSMiddleware() {
    return cors({
      origin: this.originHandler.bind(this),
      credentials: this.options.credentials,
      methods: this.options.methods,
      allowedHeaders: this.options.allowedHeaders,
      exposedHeaders: this.options.exposedHeaders,
      maxAge: this.options.maxAge,
      optionsSuccessStatus: 200
    });
  }

  originHandler(origin, callback) {
    // Allow requests with no origin (mobile apps, etc.)
    if (!origin) {
      console.log('CORS: Allowing request with no origin');
      return callback(null, true);
    }

    // Development mode - allow localhost and development domains
    if (this.options.allowDevelopment) {
      if (this.isDevelopmentOrigin(origin)) {
        console.log(`CORS: Allowing development origin: ${origin}`);
        return callback(null, true);
      }
    }

    // Check allowed origins list
    if (this.options.allowedOrigins.includes(origin)) {
      console.log(`CORS: Allowing whitelisted origin: ${origin}`);
      return callback(null, true);
    }

    // Check dynamic origin patterns
    if (this.matchesOriginPattern(origin)) {
      console.log(`CORS: Allowing pattern-matched origin: ${origin}`);
      return callback(null, true);
    }

    // Log blocked origin for debugging
    console.warn(`CORS: Blocking origin: ${origin}`);
    callback(new Error(`Origin ${origin} not allowed by CORS policy`));
  }

  isDevelopmentOrigin(origin) {
    const developmentPatterns = [
      /^http:\/\/localhost:\d+$/,
      /^http:\/\/127\.0\.0\.1:\d+$/,
      /^http:\/\/0\.0\.0\.0:\d+$/,
      /^http:\/\/.*\.local:\d+$/,
      /^https:\/\/.*\.ngrok\.io$/,
      /^https:\/\/.*\.vercel\.app$/,
      /^https:\/\/.*\.netlify\.app$/
    ];

    return developmentPatterns.some(pattern => pattern.test(origin));
  }

  matchesOriginPattern(origin) {
    // Add custom origin pattern matching logic here
    // For example, dynamic subdomains for multi-tenant apps
    const dynamicPatterns = [
      /^https:\/\/[\w-]+\.cursoriper\.com$/,
      /^https:\/\/[\w-]+\.bmad\.app$/
    ];

    return dynamicPatterns.some(pattern => pattern.test(origin));
  }

  // Debug middleware to log CORS information
  debugMiddleware() {
    return (req, res, next) => {
      console.log('CORS Debug:', {
        origin: req.headers.origin,
        method: req.method,
        headers: req.headers,
        isPreflightRequest: req.method === 'OPTIONS'
      });
      next();
    };
  }

  // Preflight handler for complex requests
  preflightHandler() {
    return (req, res, next) => {
      if (req.method === 'OPTIONS') {
        console.log('Handling CORS preflight request');
        
        // Set CORS headers manually for better control
        res.header('Access-Control-Allow-Origin', req.headers.origin);
        res.header('Access-Control-Allow-Credentials', 'true');
        res.header('Access-Control-Allow-Methods', this.options.methods.join(', '));
        res.header('Access-Control-Allow-Headers', this.options.allowedHeaders.join(', '));
        res.header('Access-Control-Max-Age', this.options.maxAge.toString());
        
        return res.status(200).end();
      }
      
      next();
    };
  }
}

// Usage
const corsManager = new CORSManager({
  allowedOrigins: [
    'https://app.cursoriper.com',
    'https://dashboard.cursoriper.com',
    process.env.FRONTEND_URL
  ],
  allowDevelopment: process.env.NODE_ENV === 'development',
  credentials: true
});

// Apply CORS middleware
app.use(corsManager.getCORSMiddleware());

// Optional: Add debug middleware in development
if (process.env.NODE_ENV === 'development') {
  app.use(corsManager.debugMiddleware());
}

module.exports = CORSManager;

Security Headers Middleware

// security-headers.js
const helmet = require('helmet');

class SecurityHeadersManager {
  constructor(options = {}) {
    this.options = {
      // Content Security Policy
      csp: {
        directives: {
          defaultSrc: ["'self'"],
          styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com"],
          scriptSrc: ["'self'", "https://cdn.jsdelivr.net"],
          imgSrc: ["'self'", "data:", "https:"],
          fontSrc: ["'self'", "https://fonts.gstatic.com"],
          connectSrc: ["'self'", "https://api.cursoriper.com"],
          frameSrc: ["'none'"],
          objectSrc: ["'none'"],
          mediaSrc: ["'self'"],
          manifestSrc: ["'self'"],
          workerSrc: ["'self'"]
        },
        reportOnly: process.env.NODE_ENV === 'development'
      },
      
      // HTTP Strict Transport Security
      hsts: {
        maxAge: 31536000, // 1 year
        includeSubDomains: true,
        preload: true
      },
      
      // Other security options
      noSniff: true,
      frameguard: { action: 'deny' },
      xssFilter: true,
      referrerPolicy: { policy: 'strict-origin-when-cross-origin' },
      
      ...options
    };
  }

  getSecurityMiddleware() {
    return helmet({
      contentSecurityPolicy: this.options.csp,
      hsts: this.options.hsts,
      noSniff: this.options.noSniff,
      frameguard: this.options.frameguard,
      xssFilter: this.options.xssFilter,
      referrerPolicy: this.options.referrerPolicy,
      
      // Additional security headers
      crossOriginEmbedderPolicy: false, // Disable if causing issues
      crossOriginOpenerPolicy: false,   // Disable if causing issues
      crossOriginResourcePolicy: false, // Disable if causing issues
      
      // Hide X-Powered-By header
      hidePoweredBy: true
    });
  }

  // Custom security headers middleware
  customSecurityHeaders() {
    return (req, res, next) => {
      // API-specific headers
      res.setHeader('X-API-Version', '1.0');
      res.setHeader('X-Frame-Options', 'DENY');
      res.setHeader('X-Content-Type-Options', 'nosniff');
      res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
      
      // Cache control for API responses
      if (req.path.startsWith('/api/')) {
        res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate');
        res.setHeader('Pragma', 'no-cache');
        res.setHeader('Expires', '0');
      }
      
      // Prevent MIME type sniffing
      res.setHeader('X-Content-Type-Options', 'nosniff');
      
      next();
    };
  }

  // Rate limiting headers
  rateLimitHeaders() {
    return (req, res, next) => {
      // Add rate limit information to headers
      if (req.rateLimit) {
        res.setHeader('X-RateLimit-Limit', req.rateLimit.limit);
        res.setHeader('X-RateLimit-Remaining', req.rateLimit.remaining);
        res.setHeader('X-RateLimit-Reset', req.rateLimit.reset);
      }
      
      next();
    };
  }
}

// Usage
const securityManager = new SecurityHeadersManager({
  csp: {
    directives: {
      defaultSrc: ["'self'"],
      styleSrc: ["'self'", "'unsafe-inline'"],
      scriptSrc: ["'self'"],
      imgSrc: ["'self'", "data:", "https:"],
      connectSrc: ["'self'", process.env.API_URL].filter(Boolean)
    }
  }
});

// Apply security middleware
app.use(securityManager.getSecurityMiddleware());
app.use(securityManager.customSecurityHeaders());
app.use(securityManager.rateLimitHeaders());

module.exports = SecurityHeadersManager;

SSL/TLS Configuration Issues

HTTPS Certificate Problems

Problem: SSL/TLS certificates not working properly

Symptoms:

"Certificate has expired" errors
"Self-signed certificate" warnings
Mixed content warnings
SSL handshake failures

Solutions:

SSL Certificate Management

// ssl-manager.js
const https = require('https');
const fs = require('fs');
const path = require('path');

class SSLManager {
  constructor(options = {}) {
    this.options = {
      certPath: options.certPath || './certs',
      keyFile: options.keyFile || 'private.key',
      certFile: options.certFile || 'certificate.crt',
      caFile: options.caFile || 'ca_bundle.crt',
      pfxFile: options.pfxFile,
      passphrase: options.passphrase,
      autoRenew: options.autoRenew || false,
      ...options
    };
  }

  loadCertificates() {
    try {
      const certPath = path.resolve(this.options.certPath);
      
      // Check if PFX file is provided (Windows/IIS style)
      if (this.options.pfxFile) {
        const pfxPath = path.join(certPath, this.options.pfxFile);
        
        if (!fs.existsSync(pfxPath)) {
          throw new Error(`PFX file not found: ${pfxPath}`);
        }

        return {
          pfx: fs.readFileSync(pfxPath),
          passphrase: this.options.passphrase
        };
      }

      // Load separate key and certificate files
      const keyPath = path.join(certPath, this.options.keyFile);
      const certPath_file = path.join(certPath, this.options.certFile);
      
      if (!fs.existsSync(keyPath)) {
        throw new Error(`Private key file not found: ${keyPath}`);
      }
      
      if (!fs.existsSync(certPath_file)) {
        throw new Error(`Certificate file not found: ${certPath_file}`);
      }

      const sslOptions = {
        key: fs.readFileSync(keyPath),
        cert: fs.readFileSync(certPath_file)
      };

      // Add CA bundle if available
      const caPath = path.join(certPath, this.options.caFile);
      if (fs.existsSync(caPath)) {
        sslOptions.ca = fs.readFileSync(caPath);
      }

      // Add passphrase if provided
      if (this.options.passphrase) {
        sslOptions.passphrase = this.options.passphrase;
      }

      return sslOptions;
    } catch (error) {
      console.error('Failed to load SSL certificates:', error.message);
      throw error;
    }
  }

  validateCertificate(certPath) {
    try {
      const cert = fs.readFileSync(certPath, 'utf8');
      const certificate = require('crypto').X509Certificate ? 
        new (require('crypto').X509Certificate)(cert) : null;

      if (!certificate) {
        console.warn('Certificate validation not supported in this Node.js version');
        return { valid: true, warning: 'Validation not supported' };
      }

      const now = new Date();
      const validFrom = new Date(certificate.validFrom);
      const validTo = new Date(certificate.validTo);

      const validation = {
        valid: now >= validFrom && now <= validTo,
        validFrom: validFrom.toISOString(),
        validTo: validTo.toISOString(),
        subject: certificate.subject,
        issuer: certificate.issuer,
        fingerprint: certificate.fingerprint,
        serialNumber: certificate.serialNumber
      };

      // Check if certificate is expiring soon (within 30 days)
      const thirtyDaysFromNow = new Date(now.getTime() + 30 * 24 * 60 * 60 * 1000);
      if (validTo <= thirtyDaysFromNow) {
        validation.warning = 'Certificate expires within 30 days';
      }

      // Check if certificate is self-signed
      if (certificate.issuer === certificate.subject) {
        validation.warning = 'Self-signed certificate detected';
      }

      return validation;
    } catch (error) {
      return {
        valid: false,
        error: error.message
      };
    }
  }

  createHTTPSServer(app) {
    try {
      const sslOptions = this.loadCertificates();
      
      // Validate certificate
      if (!this.options.pfxFile) {
        const certPath = path.join(this.options.certPath, this.options.certFile);
        const validation = this.validateCertificate(certPath);
        
        if (!validation.valid) {
          console.error('Invalid SSL certificate:', validation.error || 'Certificate validation failed');
          if (process.env.NODE_ENV === 'production') {
            throw new Error('Invalid SSL certificate in production');
          }
        }

        if (validation.warning) {
          console.warn('SSL Certificate Warning:', validation.warning);
        }

        console.log('SSL Certificate Info:', {
          subject: validation.subject,
          validFrom: validation.validFrom,
          validTo: validation.validTo,
          issuer: validation.issuer
        });
      }

      // Create HTTPS server
      const server = https.createServer(sslOptions, app);

      // Handle SSL errors
      server.on('tlsClientError', (err, tlsSocket) => {
        console.error('TLS Client Error:', err.message);
      });

      server.on('secureConnection', (tlsSocket) => {
        console.log('Secure connection established:', {
          protocol: tlsSocket.getProtocol(),
          cipher: tlsSocket.getCipher()
        });
      });

      return server;
    } catch (error) {
      console.error('Failed to create HTTPS server:', error.message);
      throw error;
    }
  }

  // HTTP to HTTPS redirect middleware
  httpsRedirect() {
    return (req, res, next) => {
      if (req.header('x-forwarded-proto') !== 'https') {
        return res.redirect(`https://${req.header('host')}${req.url}`);
      }
      next();
    };
  }

  // Generate self-signed certificate for development
  generateSelfSignedCert() {
    const forge = require('node-forge');
    
    try {
      // Generate key pair
      const keys = forge.pki.rsa.generateKeyPair(2048);
      
      // Create certificate
      const cert = forge.pki.createCertificate();
      cert.publicKey = keys.publicKey;
      cert.serialNumber = '01';
      cert.validity.notBefore = new Date();
      cert.validity.notAfter = new Date();
      cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 1);

      const attrs = [{
        name: 'commonName',
        value: 'localhost'
      }, {
        name: 'countryName',
        value: 'US'
      }, {
        shortName: 'ST',
        value: 'State'
      }, {
        name: 'localityName',
        value: 'City'
      }, {
        name: 'organizationName',
        value: 'CursorRIPER Development'
      }];

      cert.setSubject(attrs);
      cert.setIssuer(attrs);

      // Add extensions
      cert.setExtensions([{
        name: 'basicConstraints',
        cA: true
      }, {
        name: 'keyUsage',
        keyCertSign: true,
        digitalSignature: true,
        nonRepudiation: true,
        keyEncipherment: true,
        dataEncipherment: true
      }, {
        name: 'subjectAltName',
        altNames: [{
          type: 2, // DNS
          value: 'localhost'
        }, {
          type: 7, // IP
          ip: '127.0.0.1'
        }]
      }]);

      // Sign certificate
      cert.sign(keys.privateKey);

      // Convert to PEM format
      const certPem = forge.pki.certificateToPem(cert);
      const keyPem = forge.pki.privateKeyToPem(keys.privateKey);

      // Save to files
      const certDir = path.resolve(this.options.certPath);
      if (!fs.existsSync(certDir)) {
        fs.mkdirSync(certDir, { recursive: true });
      }

      fs.writeFileSync(path.join(certDir, this.options.certFile), certPem);
      fs.writeFileSync(path.join(certDir, this.options.keyFile), keyPem);

      console.log('Self-signed certificate generated successfully');
      console.log('Certificate saved to:', path.join(certDir, this.options.certFile));
      console.log('Private key saved to:', path.join(certDir, this.options.keyFile));

      return { cert: certPem, key: keyPem };
    } catch (error) {
      console.error('Failed to generate self-signed certificate:', error);
      throw error;
    }
  }
}

// Usage
const sslManager = new SSLManager({
  certPath: './certs',
  keyFile: 'server.key',
  certFile: 'server.crt'
});

// For production
try {
  const httpsServer = sslManager.createHTTPSServer(app);
  httpsServer.listen(443, () => {
    console.log('HTTPS Server listening on port 443');
  });
} catch (error) {
  console.error('HTTPS setup failed:', error.message);
  
  // Fallback to HTTP in development
  if (process.env.NODE_ENV === 'development') {
    app.listen(3000, () => {
      console.log('HTTP Server listening on port 3000 (HTTPS failed)');
    });
  }
}

module.exports = SSLManager;

Last Updated: June 28, 2025
Framework Version: CursorRIPER.sigma v1.0+