Skip to main content

Overview

The WhatsApp RAG Bot uses a secure credential management system to store sensitive API keys and access tokens. All credentials are encrypted using AES-256-CBC encryption before being stored in the database.

Encryption System

The bot uses the EncryptionService class to encrypt and decrypt sensitive credentials.

Encryption Configuration

Set up your encryption key in the environment configuration:
APP_CIPHER_KEY=your-32-character-encryption-key-here
The APP_CIPHER_KEY must be set before using the application. Without it, the system will throw a RuntimeException.

How Encryption Works

The encryption service uses the following process:
1

Generate Initialization Vector

Creates a random IV using openssl_random_pseudo_bytes() for each encryption operation.
2

Encrypt Data

Encrypts the value using AES-256-CBC cipher with the generated IV and your APP_CIPHER_KEY.
3

Store Encrypted Value

Combines the IV and encrypted data, then encodes it in base64 format before storing in the database.
src/Services/EncryptionService.php
public function encrypt(string $value): string
{
    $ivLength = openssl_cipher_iv_length($this->cipher);
    $iv = openssl_random_pseudo_bytes($ivLength);
    
    $encrypted = openssl_encrypt($value, $this->cipher, $this->cipherKey, OPENSSL_RAW_DATA, $iv);
    
    if ($encrypted === false) {
        throw new \RuntimeException('Encryption failed');
    }
    
    return base64_encode($iv . $encrypted);
}

Database Schema

Bot Credentials Table

Stores WhatsApp and OpenAI credentials:
database/schema.sql
CREATE TABLE IF NOT EXISTS bot_credentials (
    id INT AUTO_INCREMENT PRIMARY KEY,
    whatsapp_phone_number_id VARCHAR(255) DEFAULT '',
    whatsapp_access_token TEXT DEFAULT NULL,
    whatsapp_app_secret TEXT DEFAULT NULL,
    whatsapp_verify_token VARCHAR(255) DEFAULT '',
    openai_api_key TEXT DEFAULT NULL,
    openai_model VARCHAR(100) DEFAULT 'gpt-3.5-turbo',
    openai_embedding_model VARCHAR(100) DEFAULT 'text-embedding-ada-002',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
Encrypted fields: whatsapp_access_token, whatsapp_app_secret, openai_api_key

Google OAuth Credentials Table

Stores Google Calendar OAuth credentials:
database/schema.sql
CREATE TABLE IF NOT EXISTS google_oauth_credentials (
    id INT AUTO_INCREMENT PRIMARY KEY,
    client_id VARCHAR(255) DEFAULT '',
    client_secret TEXT DEFAULT NULL,
    access_token TEXT DEFAULT NULL,
    refresh_token TEXT DEFAULT NULL,
    token_expires_at TIMESTAMP NULL,
    calendar_id VARCHAR(255) DEFAULT '',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
Encrypted fields: client_secret, access_token, refresh_token

Settings Table

Stores general system settings:
database/schema.sql
CREATE TABLE IF NOT EXISTS settings (
    id INT AUTO_INCREMENT PRIMARY KEY,
    setting_key VARCHAR(100) NOT NULL UNIQUE,
    setting_type ENUM('text', 'boolean', 'json') DEFAULT 'text',
    setting_value TEXT NOT NULL,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

Using the Credential Service

Retrieving WhatsApp Credentials

$credentialService = new CredentialService($db, $encryption);
$creds = $credentialService->getWhatsAppCredentials();

// Returns:
[
    'phone_number_id' => 'YOUR_PHONE_NUMBER_ID',
    'access_token' => 'decrypted_token',
    'app_secret' => 'decrypted_secret',
    'verify_token' => 'YOUR_VERIFY_TOKEN'
]

Retrieving OpenAI Credentials

$creds = $credentialService->getOpenAICredentials();

// Returns:
[
    'api_key' => 'decrypted_api_key',
    'model' => 'gpt-3.5-turbo',
    'embedding_model' => 'text-embedding-ada-002'
]

Saving Credentials

Credentials are automatically encrypted when saved:
// Save WhatsApp credentials
$credentialService->saveWhatsAppCredentials([
    'phone_number_id' => '123456789',
    'access_token' => 'your_access_token',
    'app_secret' => 'your_app_secret',
    'verify_token' => 'your_verify_token'
]);

// Save OpenAI credentials
$credentialService->saveOpenAICredentials([
    'api_key' => 'sk-...',
    'model' => 'gpt-4',
    'embedding_model' => 'text-embedding-3-small'
]);

Checking Credential Status

The CredentialService provides helper methods to check if credentials are configured:
src/Services/CredentialService.php
public function hasWhatsAppCredentials(): bool
{
    $creds = $this->getWhatsAppCredentials();
    return !empty($creds['access_token']) && !empty($creds['phone_number_id']);
}

public function hasOpenAICredentials(): bool
{
    $creds = $this->getOpenAICredentials();
    return !empty($creds['api_key']);
}

public function hasGoogleOAuthCredentials(): bool
{
    $creds = $this->getGoogleOAuthCredentials();
    return !empty($creds['access_token']) && !empty($creds['calendar_id']);
}

Security Best Practices

Never commit credentials to version control
  • Keep your APP_CIPHER_KEY secure and never share it
  • Use environment variables for all sensitive configuration
  • Regularly rotate API keys and access tokens
  • Ensure your database is properly secured with access controls
The encryption key should be at least 32 characters long for AES-256 encryption. Generate a secure random key using:
openssl rand -base64 32

Configuration File vs Database

The system supports two configuration modes:
  1. Database-stored credentials (Recommended): Managed through the admin dashboard with encryption
  2. Config file credentials: Fallback option using config/config.php
webhook.php
if ($credentialService && $credentialService->hasWhatsAppCredentials()) {
    $waCreds = $credentialService->getWhatsAppCredentials();
    $whatsapp = new WhatsAppService(
        $waCreds['access_token'],
        $waCreds['phone_number_id'],
        Config::get('whatsapp.api_version'),
        $logger
    );
} else {
    // Fallback to config file
    $whatsapp = new WhatsAppService(
        Config::get('whatsapp.access_token'),
        Config::get('whatsapp.phone_number_id'),
        Config::get('whatsapp.api_version'),
        $logger
    );
}

Next Steps

WhatsApp Setup

Configure WhatsApp Business API credentials

OpenAI Setup

Set up OpenAI API integration

Build docs developers (and LLMs) love