fix: Use HVAC_Secure_Storage consistently for Zoho credentials

- Fix Client ID regex to allow lowercase letters
- Update HVAC_Zoho_CRM_Auth to use encrypted storage for all operations
- Update class-zoho-admin.php to use HVAC_Secure_Storage for credential retrieval
- Update OAuth callback to use secure storage for token storage
- Update Status.md with blocking production issue (400 Bad Request)

Note: Issue persists on production - needs further investigation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
ben 2025-12-16 15:28:16 -04:00
parent 08944d48ee
commit 5a55b78d03
3 changed files with 90 additions and 35 deletions

View file

@ -8,9 +8,49 @@
## 🎯 CURRENT SESSION - ZOHO CRM INTEGRATION SETUP (Dec 16, 2025)
### Zoho CRM Integration - Staging Environment
### Zoho CRM Integration - Production Issue (BLOCKING)
**Objective:** Configure and test Zoho CRM sync implementation for staging environment.
**Objective:** Configure and test Zoho CRM sync implementation for production environment.
**Status:** 🔴 BLOCKING - Credential save hangs on production (400 Bad Request)
### Active Issue - Credential Save Hanging on Production
**Problem:** When saving Zoho CRM credentials at `https://upskillhvac.com/wp-admin/admin.php?page=hvac-zoho-sync`, the AJAX request returns a 400 Bad Request error and the form hangs on "Saving...".
**Console Error:**
```
POST https://upskillhvac.com/wp-admin/admin-ajax.php 400 (Bad Request)
```
**Investigation Completed:**
1. ✅ Fixed Client ID regex to allow lowercase letters (`[A-Z0-9]` → `[A-Za-z0-9]`)
2. ✅ Fixed credential storage mismatch - all methods now use `HVAC_Secure_Storage`
3. ✅ Updated `HVAC_Zoho_CRM_Auth` class to use encrypted storage consistently
4. ✅ Updated OAuth callback to use secure storage
5. ✅ Updated test_connection to use secure storage
6. ✅ Deployed fixes to production - **Issue persists**
**Files Modified:**
- `includes/admin/class-zoho-admin.php` - Secure storage for credentials, fixed regex
- `includes/zoho/class-zoho-crm-auth.php` - All credential operations use HVAC_Secure_Storage
**Next Steps for Investigation:**
1. Check PHP error logs on production server for detailed error
2. Test AJAX endpoint directly via curl to isolate frontend vs backend issue
3. Verify nonce generation and validation on production
4. Check if WAF/security plugin is blocking the request
5. Test with browser network tab to see exact request/response
**Possible Causes:**
- Server-side security rules (Cloudflare, ModSecurity) blocking POST to admin-ajax.php
- Nonce validation failing due to caching
- Plugin conflict on production
- HVAC_Secure_Storage encryption key difference between environments
---
### Zoho CRM Integration - Staging Environment (Working)
**Status:** ✅ OAuth Working, Sync Methods Implemented, Dry-Run Tested

View file

@ -386,7 +386,7 @@ class HVAC_Zoho_Admin {
}
// Validate Client ID format (should start with "1000.")
if (!preg_match('/^1000\.[A-Z0-9]+$/', $client_id)) {
if (!preg_match('/^1000\.[A-Za-z0-9]+$/', $client_id)) {
wp_send_json_error(array('message' => 'Invalid Client ID format. Should start with "1000."'));
return;
}
@ -557,6 +557,11 @@ class HVAC_Zoho_Admin {
wp_die('OAuth callback missing state parameter. Possible CSRF attack.');
}
// Load secure storage for credential handling
if (!class_exists('HVAC_Secure_Storage')) {
require_once HVAC_PLUGIN_DIR . 'includes/class-hvac-secure-storage.php';
}
require_once HVAC_PLUGIN_DIR . 'includes/zoho/class-zoho-crm-auth.php';
$auth = new HVAC_Zoho_CRM_Auth();
@ -564,9 +569,12 @@ class HVAC_Zoho_Admin {
wp_die('OAuth state validation failed. Please try the authorization again.');
}
// Get credentials from WordPress options
$client_id = get_option('hvac_zoho_client_id', '');
$client_secret = get_option('hvac_zoho_client_secret', '');
// Get credentials using secure storage (credentials are stored encrypted)
if (!class_exists('HVAC_Secure_Storage')) {
require_once HVAC_PLUGIN_DIR . 'includes/class-hvac-secure-storage.php';
}
$client_id = HVAC_Secure_Storage::get_credential('hvac_zoho_client_id', '');
$client_secret = HVAC_Secure_Storage::get_credential('hvac_zoho_client_secret', '');
if (empty($client_id) || empty($client_secret)) {
wp_die('OAuth callback error: Missing client credentials. Please configure your Zoho CRM credentials first.');
@ -609,20 +617,19 @@ class HVAC_Zoho_Admin {
exit;
}
// Save tokens
update_option('hvac_zoho_access_token', $token_data['access_token']);
// Save tokens using secure storage
HVAC_Secure_Storage::store_credential('hvac_zoho_access_token', $token_data['access_token']);
update_option('hvac_zoho_token_expires', time() + ($token_data['expires_in'] ?? 3600));
// Refresh token might not be returned on subsequent authorizations
if (isset($token_data['refresh_token']) && !empty($token_data['refresh_token'])) {
update_option('hvac_zoho_refresh_token', $token_data['refresh_token']);
HVAC_Secure_Storage::store_credential('hvac_zoho_refresh_token', $token_data['refresh_token']);
} else {
$existing_refresh = get_option('hvac_zoho_refresh_token');
$existing_refresh = HVAC_Secure_Storage::get_credential('hvac_zoho_refresh_token', '');
if (empty($existing_refresh)) {
// This is critical - we need a refresh token for long-term access
// Store a warning but still complete the flow
update_option('hvac_zoho_missing_refresh_token', true);
} else {
}
}
@ -653,9 +660,12 @@ class HVAC_Zoho_Admin {
return;
}
// Get credentials from WordPress options
$client_id = get_option('hvac_zoho_client_id', '');
$client_secret = get_option('hvac_zoho_client_secret', '');
// Get credentials using secure storage (credentials are stored encrypted)
if (!class_exists('HVAC_Secure_Storage')) {
require_once HVAC_PLUGIN_DIR . 'includes/class-hvac-secure-storage.php';
}
$client_id = HVAC_Secure_Storage::get_credential('hvac_zoho_client_id', '');
$client_secret = HVAC_Secure_Storage::get_credential('hvac_zoho_client_secret', '');
// Check configuration before attempting connection
if (empty($client_id)) {
@ -679,7 +689,7 @@ class HVAC_Zoho_Admin {
}
// Check if we have stored refresh token from previous OAuth
$stored_refresh_token = get_option('hvac_zoho_refresh_token');
$stored_refresh_token = HVAC_Secure_Storage::get_credential('hvac_zoho_refresh_token', '');
if (empty($stored_refresh_token)) {

View file

@ -13,7 +13,7 @@ if (!defined('ABSPATH')) {
}
class HVAC_Zoho_CRM_Auth {
private $client_id;
private $client_secret;
private $refresh_token;
@ -21,27 +21,32 @@ class HVAC_Zoho_CRM_Auth {
private $access_token;
private $token_expiry;
private $last_error = null;
public function __construct() {
// Load credentials from WordPress options (new approach)
$this->client_id = get_option('hvac_zoho_client_id', '');
$this->client_secret = get_option('hvac_zoho_client_secret', '');
$this->refresh_token = get_option('hvac_zoho_refresh_token', '');
// Load secure storage class
if (!class_exists('HVAC_Secure_Storage')) {
require_once plugin_dir_path(dirname(__FILE__)) . 'class-hvac-secure-storage.php';
}
// Load credentials from WordPress options using secure storage (encrypted)
$this->client_id = HVAC_Secure_Storage::get_credential('hvac_zoho_client_id', '');
$this->client_secret = HVAC_Secure_Storage::get_credential('hvac_zoho_client_secret', '');
$this->refresh_token = HVAC_Secure_Storage::get_credential('hvac_zoho_refresh_token', '');
$this->redirect_uri = get_site_url() . '/oauth/callback';
// Fallback to config file if options are empty (backward compatibility)
if (empty($this->client_id) || empty($this->client_secret)) {
$config_file = plugin_dir_path(__FILE__) . 'zoho-config.php';
if (file_exists($config_file)) {
require_once $config_file;
$this->client_id = empty($this->client_id) && defined('ZOHO_CLIENT_ID') ? ZOHO_CLIENT_ID : $this->client_id;
$this->client_secret = empty($this->client_secret) && defined('ZOHO_CLIENT_SECRET') ? ZOHO_CLIENT_SECRET : $this->client_secret;
$this->refresh_token = empty($this->refresh_token) && defined('ZOHO_REFRESH_TOKEN') ? ZOHO_REFRESH_TOKEN : $this->refresh_token;
$this->redirect_uri = defined('ZOHO_REDIRECT_URI') ? ZOHO_REDIRECT_URI : $this->redirect_uri;
}
}
// Load stored access token from WordPress options
$this->load_access_token();
}
@ -354,31 +359,31 @@ class HVAC_Zoho_CRM_Auth {
}
/**
* Save tokens to WordPress options
* Save tokens to WordPress options using secure storage
*/
private function save_tokens() {
update_option('hvac_zoho_refresh_token', $this->refresh_token);
HVAC_Secure_Storage::store_credential('hvac_zoho_refresh_token', $this->refresh_token);
$this->save_access_token();
}
/**
* Save access token
* Save access token using secure storage
*/
private function save_access_token() {
update_option('hvac_zoho_access_token', $this->access_token);
HVAC_Secure_Storage::store_credential('hvac_zoho_access_token', $this->access_token);
update_option('hvac_zoho_token_expiry', $this->token_expiry);
}
/**
* Load access token from WordPress options
* Load access token from WordPress options using secure storage
*/
private function load_access_token() {
$this->access_token = get_option('hvac_zoho_access_token');
$this->access_token = HVAC_Secure_Storage::get_credential('hvac_zoho_access_token', '');
$this->token_expiry = get_option('hvac_zoho_token_expiry', 0);
// Load refresh token if not set
if (!$this->refresh_token) {
$this->refresh_token = get_option('hvac_zoho_refresh_token');
$this->refresh_token = HVAC_Secure_Storage::get_credential('hvac_zoho_refresh_token', '');
}
}