# Xác Thực (Authentication)

## Tổng Quan

Zalo Personal SDK hỗ trợ hai phương thức đăng nhập chính:
- **Cookie Authentication**: Sử dụng cookie từ browser
- **QR Code Authentication**: Quét mã QR để đăng nhập

## Đăng Nhập Bằng Cookie

### 1. Lấy Cookie Từ Browser

Để sử dụng cookie authentication, bạn cần lấy cookie từ browser:

1. Mở Zalo Web (chat.zalo.me)
2. Đăng nhập vào tài khoản
3. Mở Developer Tools (F12)
4. Vào tab Application/Storage → Cookies
5. Copy tất cả cookies

### 2. Cấu Hình Credentials

```typescript
import { Zalo, Credentials } from 'zalo-personal-sdk';

const credentials: Credentials = {
  imei: 'your-device-imei', // IMEI thiết bị (có thể generate)
  cookie: [
    {
      domain: ".zalo.me",
      name: "_zlang", 
      value: "vn",
      path: "/",
      // ... các thuộc tính cookie khác
    }
    // ... thêm các cookie khác
  ],
  userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
  language: 'vi' // Optional, mặc định là 'vi'
};
```

### 3. Đăng Nhập

```typescript
const zalo = new Zalo({
  selfListen: true,    // Lắng nghe tin nhắn của chính mình
  logLevel: 'info',    // Mức độ log (info, debug, error)
  apiVersion: 'v1'     // Phiên bản API
});

try {
  const api = await zalo.login(credentials);
  console.log('Đăng nhập thành công!');
  
  // Sử dụng API
  const ownId = await api.getOwnId();
  console.log('ID của bạn:', ownId);
} catch (error) {
  console.error('Lỗi đăng nhập:', error.message);
}
```

### 4. Generate IMEI

```typescript
import { generateZaloUUID } from 'zalo-personal-sdk/utils';

const userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36';
const imei = generateZaloUUID(userAgent);
console.log('Generated IMEI:', imei);
```

## Đăng Nhập Bằng QR Code

### 1. Cấu Hình QR Login

```typescript
import { Zalo, LoginQRCallbackEventType } from 'zalo-personal-sdk';

const zalo = new Zalo();

const options = {
  userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
  language: 'vi',
  qrPath: './qr-code.png' // Optional: đường dẫn lưu QR code
};
```

### 2. Callback Handler

```typescript
const qrCallback = (event) => {
  switch (event.type) {
    case LoginQRCallbackEventType.QrCodeGenerated:
      console.log('QR Code đã được tạo tại:', event.data.qrCodePath);
      console.log('QR Code URL:', event.data.qrCodeUrl);
      // Hiển thị QR code cho người dùng quét
      break;
      
    case LoginQRCallbackEventType.WaitingForScan:
      console.log('Đang chờ quét QR code...');
      break;
      
    case LoginQRCallbackEventType.Scanned:
      console.log('QR code đã được quét, đang chờ xác nhận...');
      break;
      
    case LoginQRCallbackEventType.Confirmed:
      console.log('Đã xác nhận đăng nhập trên điện thoại');
      break;
      
    case LoginQRCallbackEventType.LoggedIn:
      console.log('Đăng nhập thành công!');
      break;
      
    case LoginQRCallbackEventType.GotLoginInfo:
      console.log('Thông tin đăng nhập:', {
        cookie: event.data.cookie,
        imei: event.data.imei,
        userAgent: event.data.userAgent
      });
      
      // Lưu thông tin này để sử dụng cho lần đăng nhập sau
      saveCredentials(event.data);
      break;
      
    case LoginQRCallbackEventType.Error:
      console.error('Lỗi đăng nhập QR:', event.data.error);
      break;
      
    case LoginQRCallbackEventType.Timeout:
      console.log('QR code đã hết hạn, tạo QR mới...');
      break;
  }
};
```

### 3. Thực Hiện QR Login

```typescript
try {
  const api = await zalo.loginQR(options, qrCallback);
  console.log('Đăng nhập QR thành công!');
  
  // API đã sẵn sàng sử dụng
  const ownId = await api.getOwnId();
  console.log('ID của bạn:', ownId);
} catch (error) {
  console.error('Lỗi đăng nhập QR:', error.message);
}
```

### 4. Lưu Trữ Credentials

```typescript
function saveCredentials(loginData) {
  const credentials = {
    imei: loginData.imei,
    cookie: loginData.cookie,
    userAgent: loginData.userAgent,
    language: 'vi'
  };
  
  // Lưu vào file
  fs.writeFileSync('./credentials.json', JSON.stringify(credentials, null, 2));
  
  // Hoặc lưu vào database
  // await db.saveCredentials(userId, credentials);
}

function loadCredentials() {
  try {
    const data = fs.readFileSync('./credentials.json', 'utf8');
    return JSON.parse(data);
  } catch (error) {
    return null;
  }
}
```

## Xử Lý Session

### 1. Kiểm Tra Session

```typescript
// Kiểm tra xem session có còn hợp lệ không
try {
  const accountInfo = await api.fetchAccountInfo();
  console.log('Session hợp lệ:', accountInfo);
} catch (error) {
  console.log('Session hết hạn, cần đăng nhập lại');
  // Thực hiện đăng nhập lại
}
```

### 2. Keep Alive

```typescript
// Giữ session active
setInterval(async () => {
  try {
    await api.keepAlive();
    console.log('Keep alive thành công');
  } catch (error) {
    console.error('Keep alive thất bại:', error.message);
  }
}, 5 * 60 * 1000); // 5 phút
```

### 3. Refresh Session

```typescript
async function refreshSession() {
  try {
    // Thử các API đơn giản để kiểm tra session
    await api.getOwnId();
    return true;
  } catch (error) {
    console.log('Session hết hạn, đăng nhập lại...');
    
    // Đăng nhập lại bằng credentials đã lưu
    const savedCredentials = loadCredentials();
    if (savedCredentials) {
      try {
        const newApi = await zalo.login(savedCredentials);
        return newApi;
      } catch (loginError) {
        console.error('Không thể đăng nhập lại:', loginError.message);
        return false;
      }
    }
    
    return false;
  }
}
```

## Xử Lý Lỗi

### 1. Các Lỗi Thường Gặp

```typescript
import { ZaloApiError } from 'zalo-personal-sdk';

try {
  const api = await zalo.login(credentials);
} catch (error) {
  if (error instanceof ZaloApiError) {
    switch (error.message) {
      case 'Missing required params':
        console.error('Thiếu thông tin đăng nhập bắt buộc');
        break;
      case 'Đăng nhập thất bại':
        console.error('Cookie không hợp lệ hoặc hết hạn');
        break;
      case 'Khởi tạo ngữ cảnh thất bại':
        console.error('Lỗi khởi tạo session');
        break;
      default:
        console.error('Lỗi không xác định:', error.message);
    }
  } else {
    console.error('Lỗi hệ thống:', error);
  }
}
```

### 2. Retry Logic

```typescript
async function loginWithRetry(credentials, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      console.log(`Thử đăng nhập lần ${attempt}...`);
      const api = await zalo.login(credentials);
      console.log('Đăng nhập thành công!');
      return api;
    } catch (error) {
      console.error(`Lần thử ${attempt} thất bại:`, error.message);
      
      if (attempt === maxRetries) {
        throw new Error(`Đăng nhập thất bại sau ${maxRetries} lần thử`);
      }
      
      // Chờ trước khi thử lại
      await new Promise(resolve => setTimeout(resolve, 2000 * attempt));
    }
  }
}
```

## Bảo Mật

### 1. Bảo Vệ Credentials

```typescript
// KHÔNG hard-code credentials trong code
// ✗ Sai
const credentials = {
  cookie: [{ name: 'session', value: 'abc123...' }]
};

// ✓ Đúng
const credentials = {
  cookie: JSON.parse(process.env.ZALO_COOKIES),
  imei: process.env.ZALO_IMEI,
  userAgent: process.env.ZALO_USER_AGENT
};
```

### 2. Mã Hóa Credentials

```typescript
const crypto = require('crypto');

function encryptCredentials(credentials, secretKey) {
  const cipher = crypto.createCipher('aes-256-cbc', secretKey);
  let encrypted = cipher.update(JSON.stringify(credentials), 'utf8', 'hex');
  encrypted += cipher.final('hex');
  return encrypted;
}

function decryptCredentials(encryptedData, secretKey) {
  const decipher = crypto.createDecipher('aes-256-cbc', secretKey);
  let decrypted = decipher.update(encryptedData, 'hex', 'utf8');
  decrypted += decipher.final('utf8');
  return JSON.parse(decrypted);
}
```

### 3. Validate Credentials

```typescript
function validateCredentials(credentials) {
  if (!credentials.imei || typeof credentials.imei !== 'string') {
    throw new Error('IMEI không hợp lệ');
  }
  
  if (!credentials.cookie || !Array.isArray(credentials.cookie)) {
    throw new Error('Cookie không hợp lệ');
  }
  
  if (!credentials.userAgent || typeof credentials.userAgent !== 'string') {
    throw new Error('User Agent không hợp lệ');
  }
  
  return true;
}
```

## Ví Dụ Hoàn Chỉnh

```typescript
import { Zalo, ZaloApiError } from 'zalo-personal-sdk';
import fs from 'fs/promises';

class ZaloAuth {
  private zalo: Zalo;
  private api: any = null;
  
  constructor() {
    this.zalo = new Zalo({
      selfListen: false,
      logLevel: 'info'
    });
  }
  
  async loginWithCredentials(credentialsPath: string) {
    try {
      const credentialsData = await fs.readFile(credentialsPath, 'utf8');
      const credentials = JSON.parse(credentialsData);
      
      validateCredentials(credentials);
      
      this.api = await this.zalo.login(credentials);
      console.log('✅ Đăng nhập thành công!');
      
      return this.api;
    } catch (error) {
      console.error('❌ Lỗi đăng nhập:', error.message);
      throw error;
    }
  }
  
  async loginWithQR(qrPath?: string) {
    return new Promise((resolve, reject) => {
      const options = {
        userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
        language: 'vi',
        qrPath: qrPath || './qr-login.png'
      };
      
      this.zalo.loginQR(options, (event) => {
        switch (event.type) {
          case 'qr_code_generated':
            console.log('📱 Vui lòng quét QR code tại:', event.data.qrCodePath);
            break;
          case 'logged_in':
            console.log('✅ Đăng nhập QR thành công!');
            resolve(event.api);
            break;
          case 'got_login_info':
            // Lưu credentials để lần sau sử dụng
            this.saveCredentials(event.data);
            break;
          case 'error':
            reject(new Error(event.data.error));
            break;
        }
      }).then(api => {
        this.api = api;
      }).catch(reject);
    });
  }
  
  private async saveCredentials(loginData: any) {
    const credentials = {
      imei: loginData.imei,
      cookie: loginData.cookie,
      userAgent: loginData.userAgent,
      language: 'vi',
      savedAt: new Date().toISOString()
    };
    
    await fs.writeFile('./credentials.json', JSON.stringify(credentials, null, 2));
    console.log('💾 Đã lưu credentials');
  }
  
  getApi() {
    if (!this.api) {
      throw new Error('Chưa đăng nhập. Vui lòng gọi login trước.');
    }
    return this.api;
  }
}

// Sử dụng
async function main() {
  const auth = new ZaloAuth();
  
  try {
    // Thử đăng nhập bằng credentials đã lưu
    await auth.loginWithCredentials('./credentials.json');
  } catch (error) {
    console.log('Không thể đăng nhập bằng credentials, sử dụng QR...');
    await auth.loginWithQR();
  }
  
  const api = auth.getApi();
  
  // Test API
  const ownId = await api.getOwnId();
  console.log('🆔 ID của bạn:', ownId);
}

main().catch(console.error);
```

## Lưu Ý Quan Trọng

1. **Cookie Expiry**: Cookie có thể hết hạn, cần xử lý đăng nhập lại
2. **Rate Limiting**: Không đăng nhập quá nhiều lần trong thời gian ngắn
3. **User Agent**: Sử dụng User Agent thật, không fake
4. **Security**: Không chia sẻ credentials với người khác
5. **Backup**: Luôn backup credentials ở nơi an toàn
6. **Environment**: Sử dụng environment variables cho production