- Add .distignore (operator-private files excluded) - Add build.sh for WordPress-installable release ZIPs - Update CLAUDE.md references (now operator-private only)
205 lines
6.8 KiB
PHP
205 lines
6.8 KiB
PHP
<?php
|
|
/**
|
|
* TigerStyle Scent Authenticator
|
|
* Implements scent-based authentication for WordPress like cats recognize each other
|
|
*
|
|
* @package TigerStyle Scent
|
|
*/
|
|
|
|
defined('ABSPATH') or die('Direct access forbidden.');
|
|
|
|
class TigerStyleScent_ScentAuthenticator implements TigerStyleScent_AuthenticatorInterface {
|
|
|
|
/**
|
|
* Authenticate user via scent token detection
|
|
*
|
|
* @return int|false User ID if scent is recognized, false otherwise
|
|
*/
|
|
public function authenticate() {
|
|
// Detect scent token from HTTP headers
|
|
$scent_token = $this->detect_scent_token();
|
|
|
|
if (!$scent_token) {
|
|
return false;
|
|
}
|
|
|
|
// Validate scent and return associated user ID
|
|
return $this->validate_scent_token($scent_token);
|
|
}
|
|
|
|
/**
|
|
* Detect scent token from various sources like a cat's keen senses
|
|
*
|
|
* @return string|null The detected scent token
|
|
*/
|
|
private function detect_scent_token(): ?string {
|
|
// Check Authorization header for Bearer/ScentBearer token
|
|
$auth_header = $this->get_authorization_header();
|
|
|
|
if ($auth_header) {
|
|
// Support both Bearer and our custom ScentBearer format
|
|
if (preg_match('/^(?:Bearer|ScentBearer)\s+(.+)$/i', $auth_header, $matches)) {
|
|
return trim($matches[1]);
|
|
}
|
|
}
|
|
|
|
// Check for scent token in POST data (like a scent trail)
|
|
if (isset($_POST['scent_token'])) {
|
|
return sanitize_text_field($_POST['scent_token']);
|
|
}
|
|
|
|
// Check for scent token in GET parameters (less secure, like faint scent)
|
|
if (isset($_GET['scent_token'])) {
|
|
return sanitize_text_field($_GET['scent_token']);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Get Authorization header across different server configurations
|
|
*
|
|
* @return string|null
|
|
*/
|
|
private function get_authorization_header(): ?string {
|
|
$auth_header = null;
|
|
|
|
// Standard HTTP_AUTHORIZATION header
|
|
if (isset($_SERVER['HTTP_AUTHORIZATION'])) {
|
|
$auth_header = $_SERVER['HTTP_AUTHORIZATION'];
|
|
}
|
|
// Alternative header names used by some servers
|
|
elseif (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) {
|
|
$auth_header = $_SERVER['REDIRECT_HTTP_AUTHORIZATION'];
|
|
}
|
|
// Check for Authorization header in apache_request_headers()
|
|
elseif (function_exists('apache_request_headers')) {
|
|
$headers = apache_request_headers();
|
|
if (isset($headers['Authorization'])) {
|
|
$auth_header = $headers['Authorization'];
|
|
}
|
|
}
|
|
|
|
// 🔐 SECURITY: Validate authorization header format to prevent injection
|
|
if ($auth_header !== null) {
|
|
// Only allow Bearer/ScentBearer tokens with valid characters
|
|
if (preg_match('/^(Bearer|ScentBearer)\s+([A-Za-z0-9+\/=._-]+)$/i', $auth_header, $matches)) {
|
|
return $auth_header;
|
|
}
|
|
|
|
// Log suspicious authorization header attempts
|
|
if (defined('TIGERSTYLE_SCENT_DEBUG') && TIGERSTYLE_SCENT_DEBUG) {
|
|
error_log('[TigerStyle Scent Security] Invalid authorization header format: ' . substr($auth_header, 0, 50));
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Validate scent token and return associated user ID like recognizing a familiar cat
|
|
*/
|
|
private function validate_scent_token(string $token): ?int {
|
|
// Use ScentServer for database-backed token validation
|
|
$scent_server = new TigerStyleScent_ScentServer([]);
|
|
$scent_data = $scent_server->analyze_scent_token($token);
|
|
|
|
if ($scent_data && $scent_data['active']) {
|
|
// Log successful scent recognition
|
|
do_action('tigerstyle_scent_authenticated', $scent_data['user_id'], $scent_data['client_id']);
|
|
|
|
return $scent_data['user_id'];
|
|
}
|
|
|
|
// Log failed scent recognition attempt
|
|
do_action('tigerstyle_scent_authentication_failed', $token);
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Get authentication type identifier
|
|
*
|
|
* @return string
|
|
*/
|
|
public function get_type(): string {
|
|
return 'scent_token';
|
|
}
|
|
|
|
/**
|
|
* Get authentication priority (higher = checked first)
|
|
* Scent authentication should be high priority like a cat's primary sense
|
|
*
|
|
* @return int
|
|
*/
|
|
public function get_priority(): int {
|
|
return 20; // Higher than basic auth, lower than emergency authentication
|
|
}
|
|
|
|
/**
|
|
* Check if this authenticator can handle the current request
|
|
* Like a cat detecting if there's a scent to analyze
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function can_handle_request(): bool {
|
|
// Can handle if we detect any scent token
|
|
return $this->detect_scent_token() !== null;
|
|
}
|
|
|
|
/**
|
|
* Provide authentication challenges/hints for API clients
|
|
*
|
|
* @return array
|
|
*/
|
|
public function get_authentication_challenge(): array {
|
|
return [
|
|
'type' => 'ScentBearer',
|
|
'realm' => 'TigerStyle Territory',
|
|
'description' => 'Provide your scent token in the Authorization header: "ScentBearer YOUR_TOKEN"',
|
|
'hint' => 'Get your scent token from /oauth/token endpoint'
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Verify scent token scope for specific resource access
|
|
* Like checking if a cat has permission to enter certain territory
|
|
*
|
|
* @param string $token Scent token to verify
|
|
* @param string $required_scope Required scope for access
|
|
* @return bool
|
|
*/
|
|
public function verify_territory_access(string $token, string $required_scope): bool {
|
|
$scent_server = new TigerStyleScent_ScentServer([]);
|
|
$scent_data = $scent_server->analyze_scent_token($token);
|
|
|
|
if (!$scent_data || !$scent_data['active']) {
|
|
return false;
|
|
}
|
|
|
|
// Check if token has required scope
|
|
$token_scopes = explode(' ', $scent_data['scope'] ?? '');
|
|
return in_array($required_scope, $token_scopes);
|
|
}
|
|
|
|
/**
|
|
* Log scent authentication events for territory monitoring
|
|
*
|
|
* @param string $event Event type
|
|
* @param array $data Event data
|
|
*/
|
|
private function log_scent_event(string $event, array $data = []): void {
|
|
if (defined('TIGERSTYLE_SCENT_DEBUG') && TIGERSTYLE_SCENT_DEBUG) {
|
|
error_log(sprintf(
|
|
'[TigerStyle Scent] %s: %s',
|
|
$event,
|
|
json_encode($data)
|
|
));
|
|
}
|
|
|
|
// Fire WordPress action for logging systems
|
|
do_action('tigerstyle_scent_log', $event, $data);
|
|
}
|
|
} |