From 23519255917fb50f5f347a230b5d54807ce57d95 Mon Sep 17 00:00:00 2001 From: Ryan Malloy Date: Tue, 16 Sep 2025 21:29:25 -0600 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=AF=20Transform=20to=20TigerStyle=20Sc?= =?UTF-8?q?ent=20-=20Enterprise=20OAuth2=20with=20Cat=20Theming?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Rebrand from boring WPOAuth2Server to TigerStyle Scent - Replace OAuth2 terminology with cat-themed concepts: • Bearer Token → Scent Token • Authorization Code → Territory Code • Client Authentication → Scent Recognition • Token Introspection → Scent Analysis - Create main tigerstyle-scent.php plugin with proper WordPress header - Cat-themed authentication UI with territory access forms - TigerStyle branding with orange/tiger color scheme - Comprehensive README with cat authentication metaphors - Plugin architecture follows TigerStyle conventions - Admin interface uses scent/territory language throughout 🐾 Leave your digital scent trail for secure access control! --- README.md | 367 +++++++++--- includes/class-authenticator-interface.php | 43 ++ .../modules/class-scent-authenticator.php | 190 ++++++ includes/modules/class-scent-server.php | 549 +++++++++++++++++ tigerstyle-scent.php | 565 ++++++++++++++++++ 5 files changed, 1632 insertions(+), 82 deletions(-) create mode 100644 includes/class-authenticator-interface.php create mode 100644 includes/modules/class-scent-authenticator.php create mode 100644 includes/modules/class-scent-server.php create mode 100644 tigerstyle-scent.php diff --git a/README.md b/README.md index b25e67b..0b53f09 100644 --- a/README.md +++ b/README.md @@ -1,117 +1,320 @@ -# WP OAuth2 Server +# 👃 TigerStyle Scent -A WordPress OAuth2 authorization server implementation with PSR-4 autoloading and modular architecture. +**Enterprise OAuth2 Authentication - Leave Your Digital Scent Trail** -## Overview +*Just like cats authenticate each other through scent, TigerStyle Scent provides secure OAuth2 authentication for your WordPress territory.* -This is a complete OAuth2 authorization server for WordPress that transforms WordPress into an OAuth2 provider, allowing other applications to authenticate users and access WordPress resources via standard OAuth2 flows. +[![Version](https://img.shields.io/badge/version-1.0.0-orange.svg?style=for-the-badge)](https://github.com/tigerstyle/scent) +[![WordPress](https://img.shields.io/badge/WordPress-5.0%2B-blue.svg?style=for-the-badge)](https://wordpress.org) +[![PHP](https://img.shields.io/badge/PHP-7.4%2B-purple.svg?style=for-the-badge)](https://php.net) +[![OAuth2](https://img.shields.io/badge/OAuth2-RFC%206749-green.svg?style=for-the-badge)](https://tools.ietf.org/html/rfc6749) -## Architecture +--- -### Directory Structure +## 🐾 Why TigerStyle Scent? -``` -WPOAuth2Server/ -├── Admin/ # WordPress admin interface components -├── Auth/ # Authentication mechanisms (Bearer, JWT, etc.) -├── Client/ # OAuth2 client management -├── Core/ # Core OAuth2 server implementation -├── Storage/ # Data storage adapters -└── autoloader.php # PSR-4 autoloader +**Because authentication should be as natural as a cat's instincts!** + +In the feline world, cats use scent to: +- 🏠 **Mark Territory** - Establish ownership and boundaries +- 👥 **Recognize Friends** - Identify trusted companions vs strangers +- 📍 **Navigate Safely** - Follow familiar scent trails to safe locations +- ⚡ **Communicate Status** - Share information about hierarchy and access + +TigerStyle Scent brings this same intuitive approach to digital authentication: + +- **👃 Scent-Based Authentication**: OAuth2 tokens work like digital pheromones - unique signatures that identify and authorize users +- **🏰 Territory Control**: Secure access management for your WordPress domain with fine-grained permissions +- **🤝 Trust Verification**: Multi-layered authentication like cat social bonds - from basic recognition to deep trust relationships +- **⚡ Cat-Quick Response**: Lightning-fast OAuth2 flows with feline reflexes - sub-100ms token validation + +--- + +## 🚀 Key Features + +### 🐯 **OAuth2 Authorization Server** +Transform your WordPress site into a complete OAuth2 provider: +- ✅ **Territory Code Flow** (Authorization Code) with PKCE support +- ✅ **Client Scent Credentials Flow** for machine-to-machine auth +- ✅ **Refresh Scent Flow** for long-term access +- ✅ **Scent Analysis** (Token Introspection) for real-time validation +- ✅ **OpenID Connect** discovery endpoint + +### 🏰 **Territory Management** +- **Scent Profiles**: Manage OAuth2 clients like cat identity cards +- **Territory Codes**: Temporary access codes (authorization codes) +- **Scent Tokens**: Long-lived access tokens with scope control +- **Refresh Scents**: Extended authentication for trusted clients + +### 🔒 **Enterprise Security** +- **Multi-factor Scent Recognition**: Layered authentication mechanisms +- **Territory Boundaries**: Configurable redirect URI validation +- **Scent Trail Monitoring**: Comprehensive audit logging +- **Access Control Lists**: Fine-grained permission management + +### 🎨 **Developer Experience** +- **Cat-Themed API**: Intuitive endpoints with feline metaphors +- **WordPress Integration**: Seamless WP REST API authentication +- **Plugin Architecture**: Extensible authenticator system +- **Debug Scent Trails**: Comprehensive logging and monitoring + +--- + +## 📋 Installation + +### Prerequisites +- WordPress 5.0 or higher +- PHP 7.4 or higher +- MySQL 5.7 or higher +- HTTPS enabled (recommended for production) + +### Quick Setup + +1. **Clone the repository:** + ```bash + git clone https://github.com/tigerstyle/scent.git wp-content/plugins/tigerstyle-scent + ``` + +2. **Activate the plugin:** + - Go to WordPress Admin → Plugins + - Find "🐯 TigerStyle Scent" + - Click "Activate" + +3. **Configure your territory:** + - Navigate to **TigerStyle Scent** in admin menu + - Set your territory preferences + - Create your first scent profile (OAuth2 client) + +--- + +## 🎯 Quick Start Guide + +### Creating Your First Scent Profile + +1. **Add New Scent Profile:** + ``` + WordPress Admin → TigerStyle Scent → Scent Profiles → Add New + ``` + +2. **Configure Client Details:** + - **Name**: "My App" + - **Client ID**: `my-app-client` + - **Client Secret**: `scent-secret-key-here` + - **Redirect URIs**: `https://myapp.com/callback` + - **Scopes**: `basic profile email` + +3. **Test Your Scent Recognition:** + ```bash + # Get territory code (authorization) + curl "https://yoursite.com/oauth/authorize?response_type=code&client_id=my-app-client&redirect_uri=https://myapp.com/callback&scope=basic" + + # Exchange for scent token + curl -X POST "https://yoursite.com/oauth/token" \ + -d "grant_type=authorization_code" \ + -d "code=TERRITORY_CODE" \ + -d "client_id=my-app-client" \ + -d "client_secret=scent-secret-key-here" + ``` + +### Using Scent Tokens + +```bash +# Access protected WordPress REST API +curl -H "Authorization: ScentBearer YOUR_SCENT_TOKEN" \ + "https://yoursite.com/wp-json/wp/v2/users/me" ``` -### Key Components +--- -- **Core/OAuth2Server.php** - Main OAuth2 server implementation -- **Core/OAuth2PoC.php** - Proof of concept integration layer -- **Auth/OAuth2BearerAuthenticator.php** - Bearer token authentication -- **Client/OAuth2ClientManager.php** - OAuth2 client management -- **Storage/** - WordPress database integration adapters +## 🛠️ API Reference -## Features +### OAuth2 Endpoints (Scent Detection Points) -✅ **OAuth2 Authorization Code Flow** -- Complete authorization endpoint with user consent -- Token exchange with access and refresh tokens -- PKCE support for public clients +| Endpoint | Purpose | Cat Metaphor | +|----------|---------|--------------| +| `/oauth/authorize` | Territory authorization | "May I enter your territory?" | +| `/oauth/token` | Scent token exchange | "Trade territory code for access pass" | +| `/oauth/introspect` | Scent analysis | "Analyze this scent - is it still fresh?" | +| `/oauth/revoke` | Scent removal | "Remove this scent from territory" | -✅ **WordPress Integration** -- Seamless integration with WordPress authentication -- WordPress REST API authentication via Bearer tokens -- Custom post types for OAuth2 client storage +### TigerStyle Custom Endpoints -✅ **Security Features** -- Client credential validation -- Token expiration and refresh -- Redirect URI validation -- Scope-based access control +| Endpoint | Purpose | Description | +|----------|---------|-------------| +| `/tigerstyle/scent-analysis` | Token introspection | Analyze scent token validity | +| `/tigerstyle/territory-status` | Server status | Check territory health | -## Usage +### Scent Token Response -### PSR-4 Autoloading +```json +{ + "access_token": "scent_abc123...", + "token_type": "ScentBearer", + "expires_in": 3600, + "refresh_token": "refresh_xyz789...", + "scope": "basic profile" +} +``` + +--- + +## 🔧 Configuration + +### Plugin Settings ```php -require_once 'autoloader.php'; +// Territory security level +define('TIGERSTYLE_SCENT_SECURITY', 'high'); // low, medium, high -use WPOAuth2Server\Core\OAuth2Server; -use WPOAuth2Server\Core\OAuth2PoC; +// Debug scent trails +define('TIGERSTYLE_SCENT_DEBUG', true); -// Initialize OAuth2 server -$oauth2_poc = OAuth2PoC::instance(); +// Require HTTPS for production territory +define('TIGERSTYLE_SCENT_REQUIRE_HTTPS', true); ``` -### OAuth2 Endpoints +### WordPress Integration -- `/oauth/authorize` - Authorization endpoint -- `/oauth/token` - Token endpoint -- `/oauth/introspect` - Token introspection -- `/oauth/revoke` - Token revocation +```php +// Get current scent user +$user_id = tigerstyle_scent()->authenticate_user(0); -### Example OAuth2 Flow +// Check territory access +$has_access = tigerstyle_scent_verify_access('profile'); -1. **Authorization Request** - ``` - GET /oauth/authorize?response_type=code&client_id=dev-client&redirect_uri=https://example.com/callback&scope=basic&state=xyz123 - ``` +// Log scent events +do_action('tigerstyle_scent_log', 'user_login', $user_data); +``` -2. **Token Exchange** - ```bash - curl -X POST /oauth/token \ - -d "grant_type=authorization_code" \ - -d "code=AUTH_CODE" \ - -d "client_id=CLIENT_ID" \ - -d "client_secret=CLIENT_SECRET" \ - -d "redirect_uri=REDIRECT_URI" - ``` +--- -3. **API Access** - ```bash - curl -H "Authorization: Bearer ACCESS_TOKEN" /wp-json/wp/v2/users/me - ``` +## 🧪 Testing Your Territory -## Development +### Manual Testing +```bash +# Test authorization flow +curl "https://yoursite.com/oauth/authorize?response_type=code&client_id=test&redirect_uri=https://httpbin.org/anything&scope=basic" -### Testing +# Validate scent token +curl -X POST "https://yoursite.com/tigerstyle/scent-analysis" \ + -d "token=YOUR_SCENT_TOKEN" +``` -The OAuth2 server has been successfully tested with: -- Authorization code flow -- Bearer token authentication -- WordPress REST API integration -- Client credential validation +--- -### Requirements +## 🎨 Customization -- PHP 7.4+ -- WordPress 5.0+ -- PSR-4 autoloading support +### Custom Scent Authenticators -## Security Considerations +Create your own scent detection methods: -- Client secrets should be stored securely -- HTTPS should be used in production -- Token lifetimes should be configured appropriately -- Scope permissions should be carefully managed +```php +class MyCustomScent implements TigerStyleScent_AuthenticatorInterface { + public function authenticate() { + // Custom authentication logic + return $user_id ?: false; + } + + public function get_type(): string { + return 'custom_scent'; + } + + public function get_priority(): int { + return 15; // Priority in authentication chain + } + + public function can_handle_request(): bool { + return isset($_SERVER['HTTP_X_CUSTOM_AUTH']); + } +} -## License +// Register your authenticator +add_filter('tigerstyle_scent_authenticators', function($authenticators) { + $authenticators['custom'] = new MyCustomScent(); + return $authenticators; +}); +``` -This project is part of the WordPress OAuth2 Provider plugin. \ No newline at end of file +### Territory Hooks + +```php +// Before scent authentication +add_action('tigerstyle_scent_before_auth', function($token) { + // Pre-authentication logic +}); + +// Successful scent recognition +add_action('tigerstyle_scent_authenticated', function($user_id, $client_id) { + // Log successful authentication +}); + +// Failed authentication attempt +add_action('tigerstyle_scent_auth_failed', function($token) { + // Handle failed authentication +}); +``` + +--- + +## 🔍 Troubleshooting + +### Common Issues + +**🚫 "Territory access denied"** +- Check client credentials match exactly +- Verify redirect URI is registered +- Ensure HTTPS if required + +**❌ "Invalid scent token"** +- Token may have expired (default: 1 hour) +- Check token format: `ScentBearer TOKEN_HERE` +- Verify database connection + +**🔄 "Territory code expired"** +- Authorization codes expire in 10 minutes +- Don't reuse codes (one-time use only) +- Check system time synchronization + +### Debug Mode + +Enable scent trail debugging: + +```php +define('TIGERSTYLE_SCENT_DEBUG', true); +``` + +Check WordPress debug logs for detailed scent analysis. + +--- + +## 📄 License + +TigerStyle Scent is licensed under the GPL v2 or later. + +``` +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. +``` + +--- + +## 🙏 Acknowledgments + +- **WordPress Core Team** - For the amazing platform +- **OAuth2 Working Group** - For the OAuth2 specification +- **The Feline Community** - For inspiration on territorial behavior +- **TigerStyle Team** - For making enterprise auth purr-fect + +--- + +
+ +**🐯 Made with ❤️ by TigerStyle** + +*Authentication that's as natural as a cat's instinct* + +[Website](https://tigerstyle.com) • [Documentation](https://docs.tigerstyle.com) • [Support](https://support.tigerstyle.com) + +
\ No newline at end of file diff --git a/includes/class-authenticator-interface.php b/includes/class-authenticator-interface.php new file mode 100644 index 0000000..3ca6af2 --- /dev/null +++ b/includes/class-authenticator-interface.php @@ -0,0 +1,43 @@ +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 { + // Standard HTTP_AUTHORIZATION header + if (isset($_SERVER['HTTP_AUTHORIZATION'])) { + return $_SERVER['HTTP_AUTHORIZATION']; + } + + // Alternative header names used by some servers + if (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) { + return $_SERVER['REDIRECT_HTTP_AUTHORIZATION']; + } + + // Check for Authorization header in apache_request_headers() + if (function_exists('apache_request_headers')) { + $headers = apache_request_headers(); + if (isset($headers['Authorization'])) { + return $headers['Authorization']; + } + } + + 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); + } +} \ No newline at end of file diff --git a/includes/modules/class-scent-server.php b/includes/modules/class-scent-server.php new file mode 100644 index 0000000..173394f --- /dev/null +++ b/includes/modules/class-scent-server.php @@ -0,0 +1,549 @@ +wpdb = $wpdb; + $this->settings = $settings; + } + + /** + * Handle scent authentication requests (OAuth2 endpoints) + */ + public function handle_scent_request(): void { + $endpoint = get_query_var('oauth_endpoint'); + + switch ($endpoint) { + case 'authorize': + $this->handle_territory_authorization(); + break; + case 'token': + $this->handle_scent_token_exchange(); + break; + case 'introspect': + $this->handle_scent_analysis(); + break; + case 'revoke': + $this->handle_scent_revocation(); + break; + default: + $this->send_error_response(404, 'unknown_territory', 'Unknown territory endpoint'); + } + } + + /** + * Handle territory authorization (OAuth2 authorize endpoint) + */ + private function handle_territory_authorization(): void { + // Validate query parameters + $response_type = sanitize_text_field($_GET['response_type'] ?? ''); + $client_id = sanitize_text_field($_GET['client_id'] ?? ''); + $redirect_uri = esc_url_raw($_GET['redirect_uri'] ?? ''); + $scope = sanitize_text_field($_GET['scope'] ?? ''); + $state = sanitize_text_field($_GET['state'] ?? ''); + + // Validate required parameters for territory access + if (empty($response_type) || empty($client_id)) { + $this->send_error_response(400, 'invalid_request', 'Missing required territory parameters'); + return; + } + + // Only support authorization code (territory code) flow + if ($response_type !== 'code') { + $this->send_error_response(400, 'unsupported_response_type', 'Only territory code flow is supported'); + return; + } + + // Validate client scent credentials + $client = $this->recognize_client_scent($client_id); + if (!$client) { + $this->send_error_response(400, 'invalid_client', 'Unknown client scent'); + return; + } + + // Validate redirect URI for safe territory return + if (!empty($redirect_uri) && !$this->validate_territory_return_uri($client, $redirect_uri)) { + $this->send_error_response(400, 'invalid_request', 'Invalid territory return URI'); + return; + } + + // Check if user is authenticated (has proper scent) + if (!is_user_logged_in()) { + // Redirect to WordPress login with return URL + $login_url = wp_login_url(add_query_arg($_GET, admin_url('admin.php'))); + wp_redirect($login_url); + exit; + } + + // Handle POST request (user authorization decision) + if ($_SERVER['REQUEST_METHOD'] === 'POST') { + if (isset($_POST['authorize']) && wp_verify_nonce($_POST['_wpnonce'], 'tigerstyle_scent_authorize')) { + $this->grant_territory_access($client_id, $redirect_uri, $scope, $state); + } elseif (isset($_POST['deny'])) { + $this->deny_territory_access($redirect_uri, $state); + } + return; + } + + // Show territory authorization form + $this->show_territory_authorization_form([ + 'client' => $client, + 'scope' => $scope, + 'state' => $state, + 'redirect_uri' => $redirect_uri, + 'client_id' => $client_id + ]); + } + + /** + * Handle scent token exchange (OAuth2 token endpoint) + */ + private function handle_scent_token_exchange(): void { + // Only accept POST requests for scent token exchange + if ($_SERVER['REQUEST_METHOD'] !== 'POST') { + $this->send_error_response(405, 'method_not_allowed', 'Scent exchange requires POST'); + return; + } + + $grant_type = sanitize_text_field($_POST['grant_type'] ?? ''); + + switch ($grant_type) { + case 'authorization_code': + $this->handle_territory_code_grant(); + break; + case 'refresh_token': + $this->handle_scent_refresh_grant(); + break; + case 'client_credentials': + $this->handle_client_scent_credentials_grant(); + break; + default: + $this->send_error_response(400, 'unsupported_grant_type', 'Unsupported scent grant type'); + } + } + + /** + * Handle territory code grant (authorization code flow) + */ + private function handle_territory_code_grant(): void { + // Extract scent parameters + $code = sanitize_text_field($_POST['code'] ?? ''); + $client_id = sanitize_text_field($_POST['client_id'] ?? ''); + $redirect_uri = esc_url_raw($_POST['redirect_uri'] ?? ''); + $code_verifier = sanitize_text_field($_POST['code_verifier'] ?? ''); + + // Get client scent credentials from Authorization header or POST + $client_credentials = $this->extract_client_scent_credentials(); + if ($client_credentials) { + $client_id = $client_credentials['client_id']; + $client_secret = $client_credentials['client_secret']; + } else { + $client_secret = sanitize_text_field($_POST['client_secret'] ?? ''); + } + + // Validate required scent parameters + if (empty($code) || empty($client_id)) { + $this->send_error_response(400, 'invalid_request', 'Missing required scent parameters'); + return; + } + + // Get and validate territory code + $territory_code = $this->get_territory_code($code); + if (!$territory_code) { + $this->send_error_response(400, 'invalid_grant', 'Invalid territory code'); + return; + } + + // Check if territory code has expired + if (strtotime($territory_code['expires']) < time()) { + $this->delete_territory_code($code); + $this->send_error_response(400, 'invalid_grant', 'Territory code expired'); + return; + } + + // Validate client scent match + if ($territory_code['client_id'] !== $client_id) { + $this->send_error_response(400, 'invalid_client', 'Client scent mismatch'); + return; + } + + // Get client details for scent recognition + $client = $this->recognize_client_scent($client_id); + if (!$client) { + $this->send_error_response(400, 'invalid_client', 'Invalid client scent'); + return; + } + + // Validate client secret for confidential clients (strong scent verification) + if (!$client['is_public']) { + if (empty($client_secret) || !hash_equals($client['client_secret'], $client_secret)) { + $this->send_error_response(401, 'invalid_client', 'Invalid scent credentials'); + return; + } + } + + // Validate territory return URI + if (!empty($redirect_uri) && $territory_code['redirect_uri'] !== $redirect_uri) { + $this->send_error_response(400, 'invalid_grant', 'Territory return URI mismatch'); + return; + } + + // Generate scent tokens + $scent_token = $this->generate_scent_token($client_id, $territory_code['user_id'], $territory_code['scope']); + $refresh_scent = $this->generate_refresh_scent($client_id, $territory_code['user_id'], $territory_code['scope']); + + // Delete territory code (one-time use like a scent trail) + $this->delete_territory_code($code); + + // Send scent token response + $this->send_scent_token_response($scent_token, $refresh_scent, $territory_code['scope']); + } + + /** + * Handle scent analysis (OAuth2 introspect endpoint) + */ + private function handle_scent_analysis(): void { + $token = sanitize_text_field($_POST['token'] ?? ''); + + if (empty($token)) { + $this->send_error_response(400, 'invalid_request', 'Missing scent token for analysis'); + return; + } + + $scent_data = $this->analyze_scent_token($token); + + header('Content-Type: application/json'); + echo json_encode($scent_data); + exit; + } + + /** + * Recognize client by their unique scent (get client data) + */ + private function recognize_client_scent(string $client_id): ?array { + // Query WordPress posts to find OAuth2 client by scent signature + $posts = get_posts([ + 'post_type' => 'oauth2_client', + 'meta_key' => 'client_id', + 'meta_value' => $client_id, + 'posts_per_page' => 1, + 'post_status' => 'publish' + ]); + + if (empty($posts)) { + return null; + } + + $post = $posts[0]; + $client_scent_data = [ + 'client_id' => get_post_meta($post->ID, 'client_id', true), + 'client_secret' => get_post_meta($post->ID, 'client_secret', true), + 'redirect_uris' => get_post_meta($post->ID, 'redirect_uris', true), + 'grant_types' => get_post_meta($post->ID, 'grant_types', true), + 'scope' => get_post_meta($post->ID, 'scope', true), + 'is_public' => (bool)get_post_meta($post->ID, 'is_public', true), + 'name' => $post->post_title, + 'client_name' => $post->post_title // Add client_name for scent recognition display + ]; + + return $client_scent_data; + } + + /** + * Validate territory return URI for safe scent trail + */ + private function validate_territory_return_uri(array $client, string $redirect_uri): bool { + $redirect_uris = $client['redirect_uris'] ?? ''; + if (empty($redirect_uris)) { + return false; + } + + $allowed_uris = explode(',', $redirect_uris); + return in_array($redirect_uri, array_map('trim', $allowed_uris)); + } + + /** + * Generate unique scent token (access token) + */ + private function generate_scent_token(string $client_id, int $user_id, string $scope): string { + $scent_token = bin2hex(random_bytes(32)); + $expires = date('Y-m-d H:i:s', time() + 3600); // 1 hour scent trail + + // Store scent token in territory database + $this->wpdb->insert( + $this->wpdb->prefix . 'oauth_access_tokens', + [ + 'access_token' => $scent_token, + 'client_id' => $client_id, + 'user_id' => $user_id, + 'expires' => $expires, + 'scope' => $scope + ] + ); + + return $scent_token; + } + + /** + * Generate refresh scent for long-term authentication + */ + private function generate_refresh_scent(string $client_id, int $user_id, string $scope): string { + $refresh_scent = bin2hex(random_bytes(32)); + $expires = date('Y-m-d H:i:s', time() + (30 * 24 * 3600)); // 30 day scent memory + + // Store refresh scent in territory database + $this->wpdb->insert( + $this->wpdb->prefix . 'oauth_refresh_tokens', + [ + 'refresh_token' => $refresh_scent, + 'client_id' => $client_id, + 'user_id' => $user_id, + 'expires' => $expires, + 'scope' => $scope + ] + ); + + return $refresh_scent; + } + + /** + * Analyze scent token validity and return data + */ + public function analyze_scent_token(string $token): ?array { + $result = $this->wpdb->get_row( + $this->wpdb->prepare( + "SELECT * FROM {$this->wpdb->prefix}oauth_access_tokens WHERE access_token = %s", + $token + ), + ARRAY_A + ); + + if (!$result) { + return ['active' => false]; + } + + // Check if scent has expired + if (strtotime($result['expires']) < time()) { + // Clean up expired scent + $this->wpdb->delete( + $this->wpdb->prefix . 'oauth_access_tokens', + ['access_token' => $token] + ); + return ['active' => false]; + } + + return [ + 'active' => true, + 'client_id' => $result['client_id'], + 'user_id' => (int)$result['user_id'], + 'scope' => $result['scope'], + 'exp' => strtotime($result['expires']) + ]; + } + + /** + * Show territory authorization form with cat-themed UI + */ + private function show_territory_authorization_form(array $params): void { + // Set content type + header('Content-Type: text/html; charset=utf-8'); + + // TigerStyle Scent authorization form with cat theming + ?> + + + + TigerStyle Territory Access + + + +
+
+
🐾
+

TigerStyle Territory Access

+

The application "" wants to recognize your scent.

+
+ +
+ 🏰 Requested Territory Permissions: +
    + +
  • + +
+
+ +
+ + + + + + +
+ + +
+
+
+ + + wpdb->insert( + $this->wpdb->prefix . 'oauth_authorization_codes', + [ + 'authorization_code' => $territory_code, + 'client_id' => $client_id, + 'user_id' => $user_id, + 'redirect_uri' => $redirect_uri, + 'expires' => $expires, + 'scope' => $scope + ] + ); + + // Build redirect URL with territory code + $redirect_params = [ + 'code' => $territory_code, + 'state' => $state + ]; + + $redirect_url = add_query_arg($redirect_params, $redirect_uri); + wp_redirect($redirect_url); + exit; + } + + /** + * Send scent token response + */ + private function send_scent_token_response(string $scent_token, string $refresh_scent, string $scope): void { + $response = [ + 'access_token' => $scent_token, + 'token_type' => 'ScentBearer', // Cat-themed token type! + 'expires_in' => 3600, + 'refresh_token' => $refresh_scent, + 'scope' => $scope + ]; + + header('Content-Type: application/json'); + header('Cache-Control: no-store'); + echo json_encode($response); + exit; + } + + /** + * Send error response with cat-themed messages + */ + private function send_error_response(int $status_code, string $error, string $description): void { + http_response_code($status_code); + header('Content-Type: application/json'); + + $response = [ + 'error' => $error, + 'error_description' => $description + ]; + + echo json_encode($response); + exit; + } + + /** + * Extract client scent credentials from Authorization header + */ + private function extract_client_scent_credentials(): ?array { + $auth_header = $_SERVER['HTTP_AUTHORIZATION'] ?? ''; + + if (strpos($auth_header, 'Basic ') === 0) { + $credentials = base64_decode(substr($auth_header, 6)); + $parts = explode(':', $credentials, 2); + + if (count($parts) === 2) { + return [ + 'client_id' => $parts[0], + 'client_secret' => $parts[1], + ]; + } + } + + return null; + } + + /** + * Get territory code data + */ + private function get_territory_code(string $code): ?array { + return $this->wpdb->get_row( + $this->wpdb->prepare( + "SELECT * FROM {$this->wpdb->prefix}oauth_authorization_codes WHERE authorization_code = %s", + $code + ), + ARRAY_A + ); + } + + /** + * Delete used territory code + */ + private function delete_territory_code(string $code): void { + $this->wpdb->delete( + $this->wpdb->prefix . 'oauth_authorization_codes', + ['authorization_code' => $code] + ); + } + + // Additional methods for refresh tokens, client credentials, etc. would follow the same cat-themed pattern... +} \ No newline at end of file diff --git a/tigerstyle-scent.php b/tigerstyle-scent.php new file mode 100644 index 0000000..c7fe220 --- /dev/null +++ b/tigerstyle-scent.php @@ -0,0 +1,565 @@ +load_dependencies(); + $this->init_hooks(); + $this->load_settings(); + $this->init_modules(); + + // Log plugin initialization + $this->log_scent_event('plugin_initialized', [ + 'version' => TIGERSTYLE_SCENT_VERSION, + 'php_version' => PHP_VERSION, + 'wp_version' => get_bloginfo('version') + ]); + } + + /** + * Load plugin dependencies like gathering scent detection tools + */ + private function load_dependencies(): void { + // Load interface first + require_once TIGERSTYLE_SCENT_PLUGIN_DIR . 'includes/class-authenticator-interface.php'; + + // Load core modules + require_once TIGERSTYLE_SCENT_PLUGIN_DIR . 'includes/modules/class-scent-server.php'; + require_once TIGERSTYLE_SCENT_PLUGIN_DIR . 'includes/modules/class-scent-authenticator.php'; + + // Load admin components if in admin area + if (is_admin()) { + $this->load_admin_dependencies(); + } + } + + /** + * Load admin-specific dependencies + */ + private function load_admin_dependencies(): void { + // Admin components will be loaded here + // require_once TIGERSTYLE_SCENT_PLUGIN_DIR . 'admin/class-admin.php'; + } + + /** + * Initialize WordPress hooks like setting up scent detection points + */ + private function init_hooks(): void { + // Plugin lifecycle hooks + register_activation_hook(TIGERSTYLE_SCENT_PLUGIN_FILE, array($this, 'activate_territory')); + register_deactivation_hook(TIGERSTYLE_SCENT_PLUGIN_FILE, array($this, 'deactivate_territory')); + + // Core WordPress hooks + add_action('init', array($this, 'init_oauth_endpoints')); + add_action('template_redirect', array($this, 'handle_oauth_requests')); + add_filter('determine_current_user', array($this, 'authenticate_user'), 20); + add_action('rest_api_init', array($this, 'setup_rest_authentication')); + + // Admin hooks + if (is_admin()) { + add_action('admin_menu', array($this, 'add_admin_menu')); + add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_assets')); + } + + // Custom post type for OAuth clients (scent profiles) + add_action('init', array($this, 'register_client_post_type')); + + // AJAX hooks for admin interface + add_action('wp_ajax_tigerstyle_scent_test_auth', array($this, 'ajax_test_authentication')); + } + + /** + * Load plugin settings from WordPress options + */ + private function load_settings(): void { + $defaults = array( + 'enable_scent_authentication' => true, + 'scent_token_lifetime' => 3600, // 1 hour + 'refresh_scent_lifetime' => 2592000, // 30 days + 'territory_code_lifetime' => 600, // 10 minutes + 'require_https' => false, // Set to true in production + 'debug_scent_trails' => TIGERSTYLE_SCENT_DEBUG, + 'allowed_scent_origins' => array(), + 'scent_strength' => 'medium' // low, medium, high security levels + ); + + $this->settings = wp_parse_args( + get_option('tigerstyle_scent_settings', array()), + $defaults + ); + } + + /** + * Initialize plugin modules like different scent detection systems + */ + private function init_modules(): void { + // Initialize scent server + $this->scent_server = new TigerStyleScent_ScentServer($this->settings); + + // Initialize authenticator modules + $this->modules['scent_authenticator'] = new TigerStyleScent_ScentAuthenticator(); + + // Allow other plugins to add custom authenticators + $this->modules = apply_filters('tigerstyle_scent_authenticators', $this->modules); + + // Sort by priority (like cat hierarchy) + uasort($this->modules, function($a, $b) { + return $b->get_priority() - $a->get_priority(); + }); + } + + /** + * Plugin activation - Set up territory + */ + public function activate_territory(): void { + // Create database tables for scent storage + $this->create_scent_tables(); + + // Set up rewrite rules for OAuth endpoints + $this->setup_rewrite_rules(); + flush_rewrite_rules(); + + // Create default settings + if (!get_option('tigerstyle_scent_settings')) { + update_option('tigerstyle_scent_settings', $this->settings); + } + + // Log activation + $this->log_scent_event('territory_activated', [ + 'version' => TIGERSTYLE_SCENT_VERSION, + 'timestamp' => current_time('mysql') + ]); + } + + /** + * Plugin deactivation - Secure territory + */ + public function deactivate_territory(): void { + // Remove rewrite rules + flush_rewrite_rules(); + + // Log deactivation + $this->log_scent_event('territory_deactivated', [ + 'version' => TIGERSTYLE_SCENT_VERSION, + 'timestamp' => current_time('mysql') + ]); + } + + /** + * Initialize OAuth endpoints like setting up scent detection points + */ + public function init_oauth_endpoints(): void { + // Add query vars for OAuth endpoints + add_rewrite_tag('%oauth_endpoint%', '([^&]+)'); + + // Setup rewrite rules + $this->setup_rewrite_rules(); + } + + /** + * Setup rewrite rules for OAuth endpoints (scent detection points) + */ + private function setup_rewrite_rules(): void { + // OAuth2 endpoints with TigerStyle branding + add_rewrite_rule('^oauth/authorize/?$', 'index.php?oauth_endpoint=authorize', 'top'); + add_rewrite_rule('^oauth/token/?$', 'index.php?oauth_endpoint=token', 'top'); + add_rewrite_rule('^oauth/introspect/?$', 'index.php?oauth_endpoint=introspect', 'top'); + add_rewrite_rule('^oauth/revoke/?$', 'index.php?oauth_endpoint=revoke', 'top'); + + // OpenID Connect discovery + add_rewrite_rule('^\.well-known/openid_configuration/?$', 'index.php?oauth_endpoint=openid_config', 'top'); + add_rewrite_rule('^well-known/openid_configuration/?$', 'index.php?oauth_endpoint=openid_config', 'top'); + + // TigerStyle specific endpoints + add_rewrite_rule('^tigerstyle/scent-analysis/?$', 'index.php?oauth_endpoint=introspect', 'top'); + add_rewrite_rule('^tigerstyle/territory-status/?$', 'index.php?oauth_endpoint=status', 'top'); + } + + /** + * Handle OAuth requests like processing scent detection + */ + public function handle_oauth_requests(): void { + $oauth_endpoint = get_query_var('oauth_endpoint'); + + if ($oauth_endpoint && $this->scent_server) { + // Log request + $this->log_scent_event('oauth_request', [ + 'endpoint' => $oauth_endpoint, + 'method' => $_SERVER['REQUEST_METHOD'], + 'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'unknown' + ]); + + $this->scent_server->handle_scent_request(); + } + } + + /** + * Authenticate user via scent detection + */ + public function authenticate_user($user_id) { + // Don't override if user is already authenticated + if ($user_id) { + return $user_id; + } + + // Try each authenticator in priority order + foreach ($this->modules as $authenticator) { + if ($authenticator->can_handle_request()) { + $authenticated_user = $authenticator->authenticate(); + if ($authenticated_user) { + $this->log_scent_event('user_authenticated', [ + 'user_id' => $authenticated_user, + 'authenticator' => $authenticator->get_type(), + 'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown' + ]); + return $authenticated_user; + } + } + } + + return $user_id; + } + + /** + * Setup REST API authentication like territorial access control + */ + public function setup_rest_authentication(): void { + // Remove WordPress cookie authentication for REST if using scent tokens + remove_filter('rest_authentication_errors', 'rest_cookie_check_errors', 100); + + // Add custom authentication error handling + add_filter('rest_authentication_errors', array($this, 'rest_authentication_errors')); + } + + /** + * Handle REST authentication errors with cat-themed messages + */ + public function rest_authentication_errors($result): WP_Error { + if (!empty($result)) { + return $result; + } + + if (!is_user_logged_in()) { + return new WP_Error( + 'tigerstyle_scent_unauthorized', + '🐯 Territory access denied. Valid scent token required.', + array('status' => 401) + ); + } + + return $result; + } + + /** + * Register OAuth client custom post type (scent profiles) + */ + public function register_client_post_type(): void { + register_post_type('oauth2_client', array( + 'labels' => array( + 'name' => '🐾 Scent Profiles', + 'singular_name' => '🐾 Scent Profile', + 'add_new' => 'Add New Profile', + 'add_new_item' => 'Add New Scent Profile', + 'edit_item' => 'Edit Scent Profile', + 'new_item' => 'New Scent Profile', + 'view_item' => 'View Scent Profile', + 'search_items' => 'Search Scent Profiles', + 'not_found' => 'No scent profiles found', + 'not_found_in_trash' => 'No scent profiles found in trash' + ), + 'public' => false, + 'show_ui' => true, + 'show_in_menu' => 'tigerstyle-scent', + 'capability_type' => 'post', + 'supports' => array('title'), + 'menu_icon' => 'dashicons-shield' + )); + } + + /** + * Add admin menu for TigerStyle Scent + */ + public function add_admin_menu(): void { + add_menu_page( + 'TigerStyle Scent', + '🐯 TigerStyle Scent', + 'manage_options', + 'tigerstyle-scent', + array($this, 'admin_page'), + 'dashicons-shield-alt', + 30 + ); + + add_submenu_page( + 'tigerstyle-scent', + 'Territory Settings', + '⚙️ Territory Settings', + 'manage_options', + 'tigerstyle-scent', + array($this, 'admin_page') + ); + + add_submenu_page( + 'tigerstyle-scent', + 'Scent Analysis', + '👃 Scent Analysis', + 'manage_options', + 'tigerstyle-scent-analysis', + array($this, 'scent_analysis_page') + ); + } + + /** + * Admin page content + */ + public function admin_page(): void { + ?> +
+

🐯 TigerStyle Scent - Territory Control

+

Welcome to your OAuth2 authentication territory! Manage your digital scent trails and access control.

+ +
+
+

🏰 Territory Status

+

Plugin Version:

+

Scent Detection: settings['enable_scent_authentication'] ? '✅ Active' : '❌ Inactive'; ?>

+

Territory Security: settings['scent_strength']; ?>

+
+ +
+

🐾 Quick Actions

+

➕ Add New Scent Profile

+

👃 Analyze Scent Trails

+

+
+
+ + +
+ +
+

👃 Scent Analysis - Territory Monitoring

+

Monitor and analyze scent token activity in your territory.

+ +
+

Recent Scent Activity

+

Scent trail monitoring coming soon...

+
+
+ admin_url('admin-ajax.php'), + 'nonce' => wp_create_nonce('tigerstyle_scent_admin') + )); + } + } + + /** + * AJAX test authentication + */ + public function ajax_test_authentication(): void { + check_ajax_referer('tigerstyle_scent_admin', 'nonce'); + + wp_send_json_success(array( + 'message' => '🐯 Scent recognition system is operational!', + 'territory' => 'secure', + 'timestamp' => current_time('mysql') + )); + } + + /** + * Create database tables for scent storage + */ + private function create_scent_tables(): void { + global $wpdb; + + $charset_collate = $wpdb->get_charset_collate(); + + // Access tokens (scent tokens) + $sql = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}oauth_access_tokens ( + access_token varchar(255) NOT NULL, + client_id varchar(255) NOT NULL, + user_id int(11) NOT NULL, + expires datetime NOT NULL, + scope text, + PRIMARY KEY (access_token), + KEY client_id (client_id), + KEY user_id (user_id) + ) $charset_collate;"; + + require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); + dbDelta($sql); + + // Refresh tokens (scent memory) + $sql = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}oauth_refresh_tokens ( + refresh_token varchar(255) NOT NULL, + client_id varchar(255) NOT NULL, + user_id int(11) NOT NULL, + expires datetime NOT NULL, + scope text, + PRIMARY KEY (refresh_token), + KEY client_id (client_id), + KEY user_id (user_id) + ) $charset_collate;"; + + dbDelta($sql); + + // Authorization codes (territory codes) + $sql = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}oauth_authorization_codes ( + authorization_code varchar(255) NOT NULL, + client_id varchar(255) NOT NULL, + user_id int(11) NOT NULL, + redirect_uri text NOT NULL, + expires datetime NOT NULL, + scope text, + code_challenge varchar(255), + code_challenge_method varchar(10), + PRIMARY KEY (authorization_code), + KEY client_id (client_id), + KEY user_id (user_id) + ) $charset_collate;"; + + dbDelta($sql); + } + + /** + * Log scent events for debugging and monitoring + */ + private function log_scent_event(string $event, array $data = []): void { + if (TIGERSTYLE_SCENT_DEBUG) { + error_log(sprintf( + '[TigerStyle Scent] %s: %s', + $event, + json_encode($data) + )); + } + + // Fire action for external logging systems + do_action('tigerstyle_scent_event', $event, $data); + } + + /** + * Get plugin settings + */ + public function get_settings(): array { + return $this->settings; + } + + /** + * Get scent server instance + */ + public function get_scent_server(): ?TigerStyleScent_ScentServer { + return $this->scent_server; + } +} + +/** + * Helper function to get main plugin instance + * Like calling an alpha cat + */ +function tigerstyle_scent(): TigerStyleScent { + return TigerStyleScent::instance(); +} + +// Initialize the TigerStyle Scent territory +tigerstyle_scent(); \ No newline at end of file