Skip to main content

Overview

Security is paramount in authentication systems. The Strike Auth Service implements multiple layers of security to protect user data, prevent unauthorized access, and maintain system integrity. This guide covers the key security concepts, best practices, and implementation strategies.

Authentication Security

Multi-Factor Authentication (MFA)

The Strike Auth Service supports multiple authentication factors to enhance security:

Email Verification

Email-based verification for account creation and password recovery

SMS OTP

SMS-based one-time passwords for additional security

Magic Links

Passwordless authentication via secure email links

OAuth Providers

Integration with trusted OAuth providers (Google, GitHub, etc.)

Password Security

Password Requirements

const passwordRequirements = {
  minLength: 8,
  maxLength: 128,
  requireUppercase: true,
  requireLowercase: true,
  requireNumbers: true,
  requireSpecialChars: true,
  preventCommonPasswords: true,
  preventUserInfoInPassword: true
};

// Example validation
function validatePassword(password, userInfo) {
  const checks = {
    length: password.length >= 8 && password.length <= 128,
    uppercase: /[A-Z]/.test(password),
    lowercase: /[a-z]/.test(password),
    numbers: /\d/.test(password),
    special: /[!@#$%^&*(),.?":{}|<>]/.test(password),
    notCommon: !commonPasswords.includes(password.toLowerCase()),
    notUserInfo: !containsUserInfo(password, userInfo)
  };
  
  return Object.values(checks).every(check => check);
}

Password Hashing

The service uses industry-standard bcrypt with appropriate salt rounds:
const bcrypt = require('bcrypt');

// Secure password hashing
async function hashPassword(password) {
  const saltRounds = 12; // Recommended for 2024
  return await bcrypt.hash(password, saltRounds);
}

// Secure password verification
async function verifyPassword(password, hash) {
  return await bcrypt.compare(password, hash);
}

Token Security

JWT Implementation

Token Structure

// JWT Header
{
  "alg": "HS256",
  "typ": "JWT"
}

// JWT Payload
{
  "sub": "user_id",
  "email": "user@example.com",
  "role": "authenticated",
  "iat": 1703097600,
  "exp": 1703184000,
  "aud": "authenticated",
  "iss": "https://api.strike.com/auth"
}

Token Security Features

Access tokens expire within 1 hour to limit exposure window
const tokenExpiry = {
  accessToken: '1h',
  refreshToken: '30d',
  magicLink: '15m',
  resetToken: '1h'
};
Tokens are signed with strong secrets and algorithms
const jwt = require('jsonwebtoken');

// Use strong, randomly generated secrets
const JWT_SECRET = process.env.JWT_SECRET; // 256-bit random string
const REFRESH_SECRET = process.env.REFRESH_SECRET; // Separate secret

// Sign with secure algorithm
const token = jwt.sign(payload, JWT_SECRET, {
  algorithm: 'HS256',
  expiresIn: '1h',
  issuer: 'https://api.strike.com/auth',
  audience: 'authenticated'
});
Refresh tokens are rotated on each use to prevent replay attacks
async function refreshTokens(refreshToken) {
  // Verify current refresh token
  const decoded = jwt.verify(refreshToken, REFRESH_SECRET);
  
  // Invalidate old refresh token
  await invalidateRefreshToken(refreshToken);
  
  // Generate new token pair
  const newAccessToken = generateAccessToken(decoded.sub);
  const newRefreshToken = generateRefreshToken(decoded.sub);
  
  return { accessToken: newAccessToken, refreshToken: newRefreshToken };
}

Token Storage Security

Client-Side Storage

Never store sensitive tokens in localStorage or sessionStorage in production applications.
Recommended Approaches:
  1. HTTP-Only Cookies (Most Secure)
// Set secure HTTP-only cookie
app.use(cookieParser());

function setTokenCookie(res, token) {
  res.cookie('auth_token', token, {
    httpOnly: true,
    secure: process.env.NODE_ENV === 'production',
    sameSite: 'strict',
    maxAge: 3600000, // 1 hour
    path: '/'
  });
}
  1. Memory Storage with Refresh
// Store in memory, refresh before expiry
class TokenManager {
  constructor() {
    this.accessToken = null;
    this.refreshToken = null;
    this.expiryTime = null;
  }
  
  setTokens(accessToken, refreshToken) {
    this.accessToken = accessToken;
    this.refreshToken = refreshToken;
    
    // Decode to get expiry
    const decoded = jwt.decode(accessToken);
    this.expiryTime = decoded.exp * 1000;
    
    // Auto-refresh before expiry
    this.scheduleRefresh();
  }
  
  scheduleRefresh() {
    const timeUntilExpiry = this.expiryTime - Date.now();
    const refreshTime = timeUntilExpiry - 300000; // 5 minutes before expiry
    
    setTimeout(() => this.refreshTokens(), refreshTime);
  }
}

Rate Limiting and DDoS Protection

Request Rate Limiting

const rateLimit = require('express-rate-limit');

// General API rate limiting
const generalLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100, // Limit each IP to 100 requests per windowMs
  message: 'Too many requests from this IP',
  standardHeaders: true,
  legacyHeaders: false
});

// Strict rate limiting for auth endpoints
const authLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 5, // Limit each IP to 5 auth attempts per windowMs
  message: 'Too many authentication attempts',
  skipSuccessfulRequests: true
});

// Apply to routes
app.use('/auth/login', authLimiter);
app.use('/auth/signup', authLimiter);
app.use('/auth/recover', authLimiter);

Advanced Rate Limiting

// User-specific rate limiting
const userLimiter = rateLimit({
  windowMs: 15 * 60 * 1000,
  max: 10,
  keyGenerator: (req) => req.user?.id || req.ip,
  skip: (req) => req.user?.role === 'admin'
});

// Sliding window rate limiting
class SlidingWindowLimiter {
  constructor(maxRequests, windowMs) {
    this.maxRequests = maxRequests;
    this.windowMs = windowMs;
    this.requests = new Map();
  }
  
  isAllowed(key) {
    const now = Date.now();
    const userRequests = this.requests.get(key) || [];
    
    // Remove old requests outside window
    const validRequests = userRequests.filter(
      timestamp => now - timestamp < this.windowMs
    );
    
    if (validRequests.length >= this.maxRequests) {
      return false;
    }
    
    validRequests.push(now);
    this.requests.set(key, validRequests);
    return true;
  }
}

Input Validation and Sanitization

Request Validation

const Joi = require('joi');

// Email validation schema
const emailSchema = Joi.string()
  .email({ tlds: { allow: false } })
  .max(254)
  .required();

// Password validation schema
const passwordSchema = Joi.string()
  .min(8)
  .max(128)
  .pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]/)
  .required();

// User registration validation
const registrationSchema = Joi.object({
  email: emailSchema,
  password: passwordSchema,
  name: Joi.string().min(1).max(100).required(),
  metadata: Joi.object().max(10) // Limit metadata size
});

// Validation middleware
function validateRegistration(req, res, next) {
  const { error, value } = registrationSchema.validate(req.body);
  
  if (error) {
    return res.status(400).json({
      error: 'Validation failed',
      details: error.details.map(d => d.message)
    });
  }
  
  req.validatedData = value;
  next();
}

SQL Injection Prevention

// Use parameterized queries
async function getUserByEmail(email) {
  const { data, error } = await supabase
    .from('users')
    .select('*')
    .eq('email', email) // Automatically parameterized
    .single();
    
  return { data, error };
}

// For raw SQL (if needed), use proper escaping
const { Pool } = require('pg');
const pool = new Pool();

async function getUserByEmailRaw(email) {
  const query = 'SELECT * FROM users WHERE email = $1';
  const values = [email];
  
  const result = await pool.query(query, values);
  return result.rows[0];
}

HTTPS and Transport Security

TLS Configuration

const https = require('https');
const fs = require('fs');

// HTTPS server configuration
const httpsOptions = {
  key: fs.readFileSync('path/to/private-key.pem'),
  cert: fs.readFileSync('path/to/certificate.pem'),
  // Use strong cipher suites
  ciphers: [
    'ECDHE-RSA-AES128-GCM-SHA256',
    'ECDHE-RSA-AES256-GCM-SHA384',
    'ECDHE-RSA-AES128-SHA256',
    'ECDHE-RSA-AES256-SHA384'
  ].join(':'),
  honorCipherOrder: true,
  secureProtocol: 'TLSv1_2_method'
};

const server = https.createServer(httpsOptions, app);

Security Headers

const helmet = require('helmet');

// Apply security headers
app.use(helmet({
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'self'"],
      styleSrc: ["'self'", "'unsafe-inline'"],
      scriptSrc: ["'self'"],
      imgSrc: ["'self'", "data:", "https:"],
      connectSrc: ["'self'", "https://api.strike.com"]
    }
  },
  hsts: {
    maxAge: 31536000,
    includeSubDomains: true,
    preload: true
  }
}));

// Additional security headers
app.use((req, res, next) => {
  res.setHeader('X-Frame-Options', 'DENY');
  res.setHeader('X-Content-Type-Options', 'nosniff');
  res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
  next();
});

CORS Security

const cors = require('cors');

// Secure CORS configuration
const corsOptions = {
  origin: function (origin, callback) {
    const allowedOrigins = [
      'https://app.strike.com',
      'https://dashboard.strike.com',
      'https://strike.com'
    ];
    
    // Allow requests with no origin (mobile apps, etc.)
    if (!origin) return callback(null, true);
    
    if (allowedOrigins.includes(origin)) {
      callback(null, true);
    } else {
      callback(new Error('Not allowed by CORS'));
    }
  },
  credentials: true,
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  maxAge: 86400 // 24 hours
};

app.use(cors(corsOptions));

Session Security

Session Management

const session = require('express-session');
const MongoStore = require('connect-mongo');

// Secure session configuration
app.use(session({
  secret: process.env.SESSION_SECRET,
  resave: false,
  saveUninitialized: false,
  store: MongoStore.create({
    mongoUrl: process.env.MONGODB_URI,
    touchAfter: 24 * 3600 // Lazy session update
  }),
  cookie: {
    secure: process.env.NODE_ENV === 'production',
    httpOnly: true,
    maxAge: 1000 * 60 * 60 * 24, // 24 hours
    sameSite: 'strict'
  },
  name: 'strike_session' // Don't use default session name
}));

Session Invalidation

// Logout and invalidate session
app.post('/auth/logout', async (req, res) => {
  try {
    // Invalidate refresh token in database
    if (req.user?.id) {
      await supabase
        .from('refresh_tokens')
        .delete()
        .eq('user_id', req.user.id);
    }
    
    // Destroy session
    req.session.destroy((err) => {
      if (err) {
        console.error('Session destruction error:', err);
      }
      
      // Clear cookie
      res.clearCookie('strike_session');
      res.json({ message: 'Logged out successfully' });
    });
  } catch (error) {
    console.error('Logout error:', error);
    res.status(500).json({ error: 'Logout failed' });
  }
});

Audit Logging and Monitoring

Security Event Logging

// Security event logger
class SecurityLogger {
  static async logEvent(event, details) {
    const logEntry = {
      timestamp: new Date().toISOString(),
      event_type: event,
      ip_address: details.ip,
      user_agent: details.userAgent,
      user_id: details.userId,
      email: details.email,
      success: details.success,
      error_message: details.error,
      metadata: details.metadata
    };
    
    // Log to database
    await supabase
      .from('security_logs')
      .insert(logEntry);
    
    // Log to external service for critical events
    if (this.isCriticalEvent(event)) {
      await this.sendToSecurityService(logEntry);
    }
  }
  
  static isCriticalEvent(event) {
    const criticalEvents = [
      'failed_login_attempt',
      'account_lockout',
      'password_reset_request',
      'admin_action',
      'suspicious_activity'
    ];
    
    return criticalEvents.includes(event);
  }
}

// Usage in authentication
app.post('/auth/login', async (req, res) => {
  try {
    const { email, password } = req.body;
    const result = await authenticateUser(email, password);
    
    await SecurityLogger.logEvent('login_attempt', {
      ip: req.ip,
      userAgent: req.get('User-Agent'),
      email: email,
      success: !!result.user,
      error: result.error?.message
    });
    
    if (result.user) {
      res.json({ user: result.user, token: result.token });
    } else {
      res.status(401).json({ error: 'Invalid credentials' });
    }
  } catch (error) {
    await SecurityLogger.logEvent('login_error', {
      ip: req.ip,
      userAgent: req.get('User-Agent'),
      email: req.body.email,
      success: false,
      error: error.message
    });
    
    res.status(500).json({ error: 'Authentication failed' });
  }
});

Threat Detection and Response

Suspicious Activity Detection

// Anomaly detection
class ThreatDetector {
  static async checkSuspiciousActivity(userId, activity) {
    const checks = await Promise.all([
      this.checkRapidRequests(userId),
      this.checkUnusualLocation(userId, activity.ip),
      this.checkDeviceFingerprint(userId, activity.userAgent),
      this.checkTimePattern(userId)
    ]);
    
    const suspiciousScore = checks.reduce((score, check) => score + check.score, 0);
    
    if (suspiciousScore > 70) {
      await this.triggerSecurityResponse(userId, suspiciousScore, checks);
    }
    
    return { suspicious: suspiciousScore > 50, score: suspiciousScore };
  }
  
  static async triggerSecurityResponse(userId, score, details) {
    // Lock account temporarily
    await supabase
      .from('users')
      .update({ 
        locked_until: new Date(Date.now() + 15 * 60 * 1000), // 15 minutes
        lock_reason: 'suspicious_activity'
      })
      .eq('id', userId);
    
    // Send security alert
    await this.sendSecurityAlert(userId, score, details);
    
    // Log incident
    await SecurityLogger.logEvent('suspicious_activity', {
      userId,
      score,
      details,
      action: 'account_locked'
    });
  }
}

Security Best Practices

Development Guidelines

Principle of Least Privilege

Grant minimum necessary permissions to users and services

Defense in Depth

Implement multiple layers of security controls

Fail Securely

Ensure system fails to a secure state when errors occur

Regular Updates

Keep dependencies and security patches up to date

Security Checklist

  • Authentication
    • Strong password requirements enforced
    • Multi-factor authentication available
    • Account lockout after failed attempts
    • Secure password reset process
  • Authorization
    • Role-based access control implemented
    • API endpoints properly protected
    • Resource-level permissions enforced
  • Data Protection
    • Sensitive data encrypted at rest
    • Data encrypted in transit (HTTPS)
    • PII handling compliant with regulations
  • Monitoring
    • Security events logged
    • Anomaly detection in place
    • Incident response procedures defined
  • Infrastructure
    • Regular security updates applied
    • Network security configured
    • Backup and recovery tested

Compliance and Standards

GDPR Compliance

// Data subject rights implementation
class GDPRCompliance {
  static async handleDataRequest(userId, requestType) {
    switch (requestType) {
      case 'access':
        return await this.exportUserData(userId);
      case 'rectification':
        return await this.updateUserData(userId);
      case 'erasure':
        return await this.deleteUserData(userId);
      case 'portability':
        return await this.exportPortableData(userId);
      default:
        throw new Error('Invalid request type');
    }
  }
  
  static async deleteUserData(userId) {
    // Anonymize or delete user data
    await supabase
      .from('users')
      .update({
        email: `deleted_${userId}@example.com`,
        name: 'Deleted User',
        deleted_at: new Date().toISOString()
      })
      .eq('id', userId);
    
    // Log deletion for compliance
    await SecurityLogger.logEvent('gdpr_deletion', {
      userId,
      timestamp: new Date().toISOString()
    });
  }
}

SOC 2 Compliance

Key controls for SOC 2 Type II compliance:
  1. Access Controls: Role-based permissions and MFA
  2. Encryption: Data encryption at rest and in transit
  3. Monitoring: Comprehensive logging and alerting
  4. Incident Response: Documented procedures and testing
  5. Change Management: Controlled deployment processes
This comprehensive security framework ensures that the Strike Auth Service maintains the highest security standards while providing a seamless user experience.
I