Session Management

Secure session configuration, monitoring, and lifecycle management for the application.

Session Management

Secure session management is crucial for maintaining user authentication state and preventing session-based attacks. This guide covers session configuration, security monitoring, and best practices.

Session Configuration

Secure Session Settings

// lib/auth/session.ts
export const sessionConfig = {
  // Session timeout (8 hours)
  maxAge: 8 * 60 * 60,
  
  // Secure cookie settings
  secure: process.env.NODE_ENV === 'production',
  httpOnly: true,
  sameSite: 'strict' as const,
  
  // Session rotation
  rotationInterval: 30 * 60, // 30 minutes
  
  // Concurrent session limit
  maxConcurrentSessions: 3
}

export async function createSession(userId: string, metadata: any) {
  const sessionId = crypto.randomUUID()
  const expiresAt = new Date(Date.now() + sessionConfig.maxAge * 1000)
  
  // Store session in database
  await supabase
    .from('user_sessions')
    .insert({
      id: sessionId,
      user_id: userId,
      expires_at: expiresAt.toISOString(),
      ip_address: metadata.ip,
      user_agent: metadata.userAgent
    })
  
  return sessionId
}

export async function validateSession(sessionId: string) {
  const { data: session } = await supabase
    .from('user_sessions')
    .select('*')
    .eq('id', sessionId)
    .gte('expires_at', new Date().toISOString())
    .single()
  
  if (!session) {
    throw new Error('Invalid or expired session')
  }
  
  // Check for suspicious activity
  await checkSessionSecurity(session)
  
  return session
}

Session Rotation

// lib/auth/rotation.ts
export async function rotateSession(sessionId: string) {
  const oldSession = await validateSession(sessionId)
  
  // Create new session
  const newSessionId = crypto.randomUUID()
  const expiresAt = new Date(Date.now() + sessionConfig.maxAge * 1000)
  
  // Update session in database
  await supabase
    .from('user_sessions')
    .update({
      id: newSessionId,
      expires_at: expiresAt.toISOString(),
      rotated_at: new Date().toISOString(),
      previous_session_id: sessionId
    })
    .eq('id', sessionId)
  
  // Log rotation
  await createAuditLog({
    action: 'session_rotated',
    user_id: oldSession.user_id,
    details: {
      old_session_id: sessionId,
      new_session_id: newSessionId
    }
  })
  
  return newSessionId
}

// Automatic session rotation middleware
export async function sessionRotationMiddleware(request: NextRequest) {
  const sessionId = request.cookies.get('session_id')?.value
  
  if (!sessionId) return NextResponse.next()
  
  try {
    const session = await validateSession(sessionId)
    const sessionAge = Date.now() - new Date(session.created_at).getTime()
    
    // Rotate if session is older than rotation interval
    if (sessionAge > sessionConfig.rotationInterval * 1000) {
      const newSessionId = await rotateSession(sessionId)
      
      const response = NextResponse.next()
      response.cookies.set('session_id', newSessionId, {
        ...sessionConfig,
        maxAge: sessionConfig.maxAge
      })
      
      return response
    }
    
    return NextResponse.next()
  } catch (error) {
    // Invalid session, redirect to login
    return NextResponse.redirect(new URL('/auth/login', request.url))
  }
}

Session Security Monitoring

Security Checks

// lib/auth/security.ts
export async function checkSessionSecurity(session: any) {
  const now = new Date()
  const sessionAge = now.getTime() - new Date(session.created_at).getTime()
  
  // Force re-authentication for old sessions
  if (sessionAge > sessionConfig.rotationInterval * 1000) {
    await rotateSession(session.id)
  }
  
  // Check for suspicious location changes
  const recentSessions = await supabase
    .from('user_sessions')
    .select('ip_address, created_at')
    .eq('user_id', session.user_id)
    .gte('created_at', new Date(now.getTime() - 24 * 60 * 60 * 1000).toISOString())
    .order('created_at', { ascending: false })
  
  if (recentSessions.data) {
    const ips = recentSessions.data.map(s => s.ip_address)
    const uniqueIPs = new Set(ips)
    
    // Alert on multiple IPs in short time
    if (uniqueIPs.size > 3) {
      await createSecurityAlert({
        user_id: session.user_id,
        type: 'suspicious_location_change',
        details: { ip_addresses: Array.from(uniqueIPs) }
      })
    }
  }
}

export async function detectConcurrentSessions(userId: string) {
  const activeSessions = await supabase
    .from('user_sessions')
    .select('*')
    .eq('user_id', userId)
    .gte('expires_at', new Date().toISOString())
  
  if (activeSessions.data && activeSessions.data.length > sessionConfig.maxConcurrentSessions) {
    // Keep only the most recent sessions
    const sortedSessions = activeSessions.data.sort(
      (a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime()
    )
    
    const sessionsToTerminate = sortedSessions.slice(sessionConfig.maxConcurrentSessions)
    
    for (const session of sessionsToTerminate) {
      await terminateSession(session.id, 'concurrent_session_limit')
    }
    
    // Alert user about terminated sessions
    await sendNotification({
      user_id: userId,
      type: 'security_alert',
      message: `Some of your sessions were terminated due to concurrent login limit (${sessionConfig.maxConcurrentSessions})`
    })
  }
}

Session Lifecycle Management

// lib/auth/lifecycle.ts
export async function terminateSession(sessionId: string, reason?: string) {
  await supabase
    .from('user_sessions')
    .delete()
    .eq('id', sessionId)
  
  // Log session termination
  await createAuditLog({
    action: 'session_terminated',
    details: { session_id: sessionId, reason }
  })
}

export async function terminateAllUserSessions(userId: string, excludeSessionId?: string) {
  let query = supabase
    .from('user_sessions')
    .delete()
    .eq('user_id', userId)
  
  if (excludeSessionId) {
    query = query.neq('id', excludeSessionId)
  }
  
  await query
  
  await createAuditLog({
    action: 'all_sessions_terminated',
    user_id: userId,
    details: { excluded_session: excludeSessionId }
  })
}

export async function cleanupExpiredSessions() {
  const { data: expiredSessions } = await supabase
    .from('user_sessions')
    .select('id, user_id')
    .lt('expires_at', new Date().toISOString())
  
  if (expiredSessions && expiredSessions.length > 0) {
    await supabase
      .from('user_sessions')
      .delete()
      .lt('expires_at', new Date().toISOString())
    
    await createAuditLog({
      action: 'expired_sessions_cleanup',
      details: { 
        count: expiredSessions.length,
        session_ids: expiredSessions.map(s => s.id)
      }
    })
  }
}

// Schedule cleanup job
export function scheduleSessionCleanup() {
  setInterval(async () => {
    await cleanupExpiredSessions()
  }, 60 * 60 * 1000) // Run every hour
}

Session Monitoring

Real-time Session Tracking

// lib/auth/monitoring.ts
export class SessionMonitor {
  
  static async trackSessionActivity(sessionId: string, activity: string) {
    await supabase
      .from('session_activities')
      .insert({
        session_id: sessionId,
        activity,
        timestamp: new Date().toISOString()
      })
  }
  
  static async getSessionMetrics(userId: string) {
    const sessions = await supabase
      .from('user_sessions')
      .select(`
        *,
        session_activities(count)
      `)
      .eq('user_id', userId)
      .gte('expires_at', new Date().toISOString())
    
    return {
      active_sessions: sessions.data?.length || 0,
      total_activity: sessions.data?.reduce((sum, session) => 
        sum + (session.session_activities?.length || 0), 0
      ) || 0,
      locations: [...new Set(sessions.data?.map(s => s.ip_address) || [])],
      devices: [...new Set(sessions.data?.map(s => s.user_agent) || [])]
    }
  }
  
  static async detectAnomalousActivity(sessionId: string) {
    const activities = await supabase
      .from('session_activities')
      .select('*')
      .eq('session_id', sessionId)
      .gte('timestamp', new Date(Date.now() - 60 * 60 * 1000).toISOString())
      .order('timestamp', { ascending: false })
    
    if (activities.data) {
      const activityCount = activities.data.length
      const uniqueActivities = new Set(activities.data.map(a => a.activity))
      
      // Alert on excessive activity
      if (activityCount > 1000) {
        await this.createSecurityAlert('excessive_session_activity', {
          session_id: sessionId,
          activity_count: activityCount,
          unique_activities: Array.from(uniqueActivities)
        })
      }
    }
  }
  
  private static async createSecurityAlert(type: string, details: any) {
    await supabase
      .from('security_alerts')
      .insert({
        type,
        details,
        severity: 'medium',
        created_at: new Date().toISOString()
      })
  }
}

Best Practices

Session Security Guidelines

  1. Always use HTTPS in production

    • Encrypt session tokens in transit
    • Set secure cookie flags
  2. Implement proper session timeout

    • Idle timeout for inactive sessions
    • Absolute timeout for maximum session duration
  3. Use secure session storage

    • HTTP-only cookies to prevent XSS
    • SameSite attribute to prevent CSRF
  4. Monitor session activities

    • Track login locations and devices
    • Alert on suspicious activities
  5. Implement session rotation

    • Regularly rotate session IDs
    • Invalidate old sessions

Session Management Checklist

  • Secure session configuration
  • Session timeout implementation
  • Session rotation mechanism
  • Concurrent session limits
  • Session activity monitoring
  • Automatic cleanup of expired sessions
  • Security alerts for suspicious activities
  • Proper session termination on logout
  • Session invalidation on security events
  • Regular security audits of session handling

Integration with Authentication

// lib/auth/integration.ts
export async function handleUserLogin(credentials: LoginCredentials) {
  // Authenticate user
  const { data: { user }, error } = await supabase.auth.signInWithPassword(credentials)
  
  if (error || !user) {
    throw new Error('Authentication failed')
  }
  
  // Create session
  const sessionId = await createSession(user.id, {
    ip: credentials.ip,
    userAgent: credentials.userAgent
  })
  
  // Check concurrent sessions
  await detectConcurrentSessions(user.id)
  
  // Track login activity
  await SessionMonitor.trackSessionActivity(sessionId, 'login')
  
  return { user, sessionId }
}

export async function handleUserLogout(sessionId: string) {
  // Track logout activity
  await SessionMonitor.trackSessionActivity(sessionId, 'logout')
  
  // Terminate session
  await terminateSession(sessionId, 'user_logout')
  
  // Clear session cookie
  return { success: true }
}

This comprehensive session management system ensures secure authentication state management while providing monitoring and security features to protect against session-based attacks.