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); } }