← back to Designer Wallcoverings

DW-Agents/dw-agents/global-auth-system.backup.20260109/sms-verifier.ts

184 lines

/**
 * DW Global Auth - SMS Verification System
 * Handles SMS 2FA via Twilio
 */

import { SMSVerification } from './types';
import twilio from 'twilio';
import fs from 'fs/promises';
import path from 'path';

const VERIFICATION_FILE = path.join(__dirname, 'data', 'sms-verifications.json');
const CODE_EXPIRY_MS = 10 * 60 * 1000; // 10 minutes
const MAX_ATTEMPTS = 3;

export class SMSVerifier {
  private verifications: Map<string, SMSVerification> = new Map();
  private twilioClient: twilio.Twilio | null = null;

  constructor() {
    const accountSid = process.env.TWILIO_ACCOUNT_SID;
    const authToken = process.env.TWILIO_AUTH_TOKEN;

    if (accountSid && authToken) {
      this.twilioClient = twilio(accountSid, authToken);
      console.log('📱 Twilio SMS client initialized');
    } else {
      console.warn('⚠️  Twilio credentials not found - SMS verification disabled');
    }

    this.loadVerifications();
  }

  private async loadVerifications() {
    try {
      const data = await fs.readFile(VERIFICATION_FILE, 'utf-8');
      const verifications: SMSVerification[] = JSON.parse(data);
      verifications.forEach(v => {
        v.expiresAt = new Date(v.expiresAt);
        this.verifications.set(v.phone, v);
      });
    } catch (error) {
      // File doesn't exist yet
    }
  }

  private async saveVerifications() {
    try {
      await fs.mkdir(path.dirname(VERIFICATION_FILE), { recursive: true });
      const verificationsArray = Array.from(this.verifications.values());
      await fs.writeFile(VERIFICATION_FILE, JSON.stringify(verificationsArray, null, 2));
    } catch (error) {
      console.error('Failed to save verifications:', error);
    }
  }

  async sendVerificationCode(phone: string, userName: string): Promise<{ success: boolean; message: string }> {
    if (!this.twilioClient) {
      // Development mode - return a static code
      const code = '123456';
      console.log(`📱 [DEV MODE] SMS code for ${phone}: ${code}`);

      const verification: SMSVerification = {
        phone,
        code,
        expiresAt: new Date(Date.now() + CODE_EXPIRY_MS),
        attempts: 0
      };

      this.verifications.set(phone, verification);
      await this.saveVerifications();

      return {
        success: true,
        message: 'Development mode: Use code 123456'
      };
    }

    try {
      // Generate 6-digit code
      const code = Math.floor(100000 + Math.random() * 900000).toString();

      // Send SMS via Twilio
      await this.twilioClient.messages.create({
        body: `Your DW-Agents verification code is: ${code}\n\nThis code expires in 10 minutes.\n\n- Designer Wallcoverings Security Team`,
        from: process.env.TWILIO_PHONE_NUMBER,
        to: phone
      });

      const verification: SMSVerification = {
        phone,
        code,
        expiresAt: new Date(Date.now() + CODE_EXPIRY_MS),
        attempts: 0
      };

      this.verifications.set(phone, verification);
      await this.saveVerifications();

      console.log(`📱 SMS code sent to ${phone}`);

      return {
        success: true,
        message: 'Verification code sent via SMS'
      };
    } catch (error: any) {
      console.error('Failed to send SMS:', error);
      return {
        success: false,
        message: `Failed to send SMS: ${error.message}`
      };
    }
  }

  verifyCode(phone: string, code: string): { success: boolean; message: string } {
    const verification = this.verifications.get(phone);

    if (!verification) {
      return {
        success: false,
        message: 'No verification code found for this phone number'
      };
    }

    // Check expiry
    if (new Date() > verification.expiresAt) {
      this.verifications.delete(phone);
      this.saveVerifications();
      return {
        success: false,
        message: 'Verification code expired'
      };
    }

    // Check attempts
    if (verification.attempts >= MAX_ATTEMPTS) {
      this.verifications.delete(phone);
      this.saveVerifications();
      return {
        success: false,
        message: 'Maximum verification attempts exceeded'
      };
    }

    // Verify code
    if (verification.code !== code) {
      verification.attempts++;
      this.saveVerifications();
      return {
        success: false,
        message: `Invalid code (${MAX_ATTEMPTS - verification.attempts} attempts remaining)`
      };
    }

    // Success - remove verification
    this.verifications.delete(phone);
    this.saveVerifications();

    console.log(`✅ SMS verified for ${phone}`);

    return {
      success: true,
      message: 'Phone number verified successfully'
    };
  }

  // Clean up expired verifications
  cleanup() {
    const now = new Date();
    let cleaned = 0;

    for (const [phone, verification] of this.verifications.entries()) {
      if (now > verification.expiresAt) {
        this.verifications.delete(phone);
        cleaned++;
      }
    }

    if (cleaned > 0) {
      console.log(`🧹 Cleaned up ${cleaned} expired SMS verifications`);
      this.saveVerifications();
    }
  }
}