-
Notifications
You must be signed in to change notification settings - Fork 64
Description
Problem Statement
Currently, ClassifAI stores API credentials (API keys, endpoint URLs, etc.) in the WordPress database via options. This prevents integration with external secret management services like Azure Key Vault, AWS Secrets Manager, or HashiCorp Vault, where credentials should be fetched at runtime rather than stored in the database.
Proposed Solution
Create a unified credential filtering system that works across all providers, following WordPress conventions. This involves:
- A single generalized filter hook that all providers use
- A centralized helper class for credential management
- Provider-specific secondary filters for fine-grained control
Implementation Details
1. Create a Centralized Credentials Helper Class
Create a new helper class at includes/Classifai/Helpers/Credentials.php:
<?php
namespace Classifai\Helpers;
/**
* Centralized credential management for all providers.
*
* @since 3.x.0
*/
class Credentials {
/**
* Get credentials for a provider.
*
* @param string $provider_id The provider ID (e.g., 'azure_openai', 'openai_chatgpt').
* @param string $feature_id The feature ID (e.g., 'feature_title_generation').
* @param array $settings The provider settings from the database.
* @return array Filtered credentials array.
*/
public static function get_credentials( string $provider_id, string $feature_id, array $settings ): array {
/**
* Filter provider credentials before making an API request.
*
* This is the primary hook for integrating external secret management
* services like Azure Key Vault, AWS Secrets Manager, or HashiCorp Vault.
*
* @since 3.x.0
* @hook classifai_provider_credentials
*
* @param array $credentials The credentials array from settings.
* @param string $provider_id The provider ID (e.g., 'azure_openai', 'openai_chatgpt').
* @param string $feature_id The feature ID making the request.
*
* @return array Filtered credentials array.
*/
return apply_filters(
'classifai_provider_credentials',
$settings,
$provider_id,
$feature_id
);
}
/**
* Get a specific credential value.
*
* @param string $provider_id The provider ID.
* @param string $feature_id The feature ID.
* @param array $settings The provider settings.
* @param string $credential_key The specific credential key (e.g., 'api_key').
* @return mixed The credential value.
*/
public static function get_credential(
string $provider_id,
string $feature_id,
array $settings,
string $credential_key
) {
$credentials = self::get_credentials( $provider_id, $feature_id, $settings );
return $credentials[ $credential_key ] ?? '';
}
}2. Update the Base Provider Class
Modify includes/Classifai/Providers/Provider.php to add a method that providers can use:
/**
* Get filtered credentials for this provider.
*
* @param string $feature_id The feature ID making the request.
* @return array The filtered credentials.
*/
protected function get_provider_credentials( string $feature_id ): array {
$settings = $this->feature_instance->get_settings( static::ID );
return \Classifai\Helpers\Credentials::get_credentials(
static::ID,
$feature_id,
$settings
);
}3. Update All Providers to Use Centralized Credentials
Each provider will be updated to use the centralized credential retrieval. The credential keys vary by provider:
| Provider | Credential Keys |
|---|---|
| Azure OpenAI | api_key, endpoint_url, deployment |
| Azure Embeddings | api_key, endpoint_url, deployment |
| Azure Computer Vision | api_key, endpoint_url |
| Azure Speech | api_key, endpoint_url |
| OpenAI (all) | api_key |
| Google AI | api_key |
| IBM Watson | api_key, endpoint_url |
| AWS Polly | access_key_id, secret_access_key |
| ElevenLabs | api_key |
| xAI Grok | api_key |
4. Files to Modify
New file:
includes/Classifai/Helpers/Credentials.php- New centralized helper
Provider files to update:
includes/Classifai/Providers/Provider.php- Add base methodincludes/Classifai/Providers/Azure/OpenAI.phpincludes/Classifai/Providers/Azure/Embeddings.phpincludes/Classifai/Providers/Azure/ComputerVision.phpincludes/Classifai/Providers/Azure/Speech.phpincludes/Classifai/Providers/Azure/Read.phpincludes/Classifai/Providers/Azure/SmartCropping.phpincludes/Classifai/Providers/OpenAI/APIRequest.phpincludes/Classifai/Providers/GoogleAI/APIRequest.phpincludes/Classifai/Providers/Watson/NLU.phpincludes/Classifai/Providers/AWS/AmazonPolly.phpincludes/Classifai/Providers/ElevenLabs/ElevenLabs.phpincludes/Classifai/Providers/XAI/APIRequest.php
Usage Examples
Example 1: Azure Key Vault Integration (All Providers)
add_filter( 'classifai_provider_credentials', function( $credentials, $provider_id, $feature_id ) {
// Only override for Azure OpenAI providers
if ( ! str_starts_with( $provider_id, 'azure_' ) ) {
return $credentials;
}
$vault = new AzureKeyVaultClient( AZURE_VAULT_URL );
return array_merge( $credentials, [
'api_key' => $vault->get_secret( "classifai-{$provider_id}-api-key" ),
]);
}, 10, 3 );Example 2: Environment Variable Credentials
add_filter( 'classifai_provider_credentials', function( $credentials, $provider_id, $feature_id ) {
$env_key = strtoupper( "CLASSIFAI_{$provider_id}_API_KEY" );
if ( defined( $env_key ) ) {
$credentials['api_key'] = constant( $env_key );
}
return $credentials;
}, 10, 3 );Example 3: Feature-Specific Credentials
add_filter( 'classifai_provider_credentials', function( $credentials, $provider_id, $feature_id ) {
// Use different API key for content generation vs title generation
if ( 'feature_content_generation' === $feature_id ) {
$credentials['api_key'] = get_premium_api_key();
}
return $credentials;
}, 10, 3 );Considerations
- Backward Compatibility: Existing installations continue to work unchanged since the filter passes original values by default.
- Caching: Developers should implement caching when using external secret services to avoid excessive API calls.
- Settings Page: The authentication check on settings pages will also use filtered credentials, so developers can test their integration.
- Error Handling: Empty credentials will be caught by existing validation and display appropriate error messages.
- Security: Filtered credentials are never stored back to the database; they're only used for the current request.
Metadata
Metadata
Assignees
Labels
Type
Projects
Status