🐯 Transform to TigerStyle Scent - Enterprise OAuth2 with Cat Theming

- 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!
This commit is contained in:
Ryan Malloy 2025-09-16 21:29:25 -06:00
parent 1da0acd25a
commit 2351925591
5 changed files with 1632 additions and 82 deletions

367
README.md
View File

@ -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.
### 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
---
<div align="center">
**🐯 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)
</div>

View File

@ -0,0 +1,43 @@
<?php
/**
* TigerStyle Scent Authenticator Interface
* Defines the contract for all authentication methods in the TigerStyle ecosystem
*
* @package TigerStyle Scent
*/
defined('ABSPATH') or die('Direct access forbidden.');
interface TigerStyleScent_AuthenticatorInterface {
/**
* Authenticate user using this method
* Like a cat recognizing another cat's identity
*
* @return int|false User ID if authenticated successfully, false otherwise
*/
public function authenticate();
/**
* Get the type identifier for this authenticator
*
* @return string Type identifier (e.g., 'scent_token', 'api_key', 'jwt')
*/
public function get_type(): string;
/**
* Get authentication priority
* Higher numbers are checked first, like a cat's hierarchy
*
* @return int Priority level
*/
public function get_priority(): int;
/**
* Check if this authenticator can handle the current request
* Like sensing if there's something this authenticator can recognize
*
* @return bool True if this authenticator should attempt authentication
*/
public function can_handle_request(): bool;
}

View File

@ -0,0 +1,190 @@
<?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 {
// 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);
}
}

View File

@ -0,0 +1,549 @@
<?php
/**
* TigerStyle Scent Server Implementation
*
* Implements secure OAuth2 authentication like a cat's scent recognition system
* with support for territory code flow and scent validation
*
* @package TigerStyle Scent
*/
defined('ABSPATH') or die('Direct access forbidden.');
class TigerStyleScent_ScentServer {
/**
* WordPress database instance
* @var \wpdb
*/
private \wpdb $wpdb;
/**
* Plugin settings
* @var array
*/
private array $settings;
/**
* Constructor
*
* @param array $settings Plugin configuration settings
*/
public function __construct(array $settings) {
global $wpdb;
$this->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
?>
<!DOCTYPE html>
<html>
<head>
<title>TigerStyle Territory Access</title>
<style>
body { font-family: Arial, sans-serif; background: linear-gradient(135deg, #ff6b35, #f7931e); margin: 0; padding: 20px; }
.scent-form { max-width: 500px; margin: 50px auto; background: white; padding: 30px; border-radius: 15px; box-shadow: 0 10px 30px rgba(0,0,0,0.3); }
.tiger-header { text-align: center; color: #ff6b35; margin-bottom: 20px; }
.scent-info { background: #f8f9fa; padding: 15px; border-radius: 8px; margin: 20px 0; }
.territory-buttons { display: flex; gap: 10px; justify-content: center; margin-top: 20px; }
.scent-button { padding: 12px 24px; border: none; border-radius: 8px; cursor: pointer; font-weight: bold; }
.authorize-btn { background: #28a745; color: white; }
.deny-btn { background: #dc3545; color: white; }
.scent-button:hover { opacity: 0.9; transform: translateY(-1px); }
.paw-icon { font-size: 24px; }
</style>
</head>
<body>
<div class="scent-form">
<div class="tiger-header">
<div class="paw-icon">🐾</div>
<h2>TigerStyle Territory Access</h2>
<p>The application "<strong><?php echo esc_html($params['client']['client_name']); ?></strong>" wants to recognize your scent.</p>
</div>
<div class="scent-info">
<strong>🏰 Requested Territory Permissions:</strong>
<ul>
<?php foreach (explode(' ', $params['scope']) as $scope): ?>
<li><?php echo esc_html($scope); ?></li>
<?php endforeach; ?>
</ul>
</div>
<form method="post">
<?php wp_nonce_field('tigerstyle_scent_authorize'); ?>
<input type="hidden" name="client_id" value="<?php echo esc_attr($params['client_id']); ?>">
<input type="hidden" name="redirect_uri" value="<?php echo esc_attr($params['redirect_uri']); ?>">
<input type="hidden" name="scope" value="<?php echo esc_attr($params['scope']); ?>">
<input type="hidden" name="state" value="<?php echo esc_attr($params['state']); ?>">
<div class="territory-buttons">
<button type="submit" name="authorize" class="scent-button authorize-btn">
🐯 Grant Territory Access
</button>
<button type="submit" name="deny" class="scent-button deny-btn">
Deny Access
</button>
</div>
</form>
</div>
</body>
</html>
<?php
exit;
}
/**
* Grant territory access and redirect with code
*/
private function grant_territory_access(string $client_id, string $redirect_uri, string $scope, string $state): void {
$user_id = get_current_user_id();
$territory_code = bin2hex(random_bytes(32));
$expires = date('Y-m-d H:i:s', time() + 600); // 10 minute territory code
// Store territory code
$this->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...
}

565
tigerstyle-scent.php Normal file
View File

@ -0,0 +1,565 @@
<?php
/**
* Plugin Name: TigerStyle Scent
* Plugin URI: https://tigerstyle.com/scent-plugin
* Description: Enterprise OAuth2 authentication server - leave your digital scent trail for secure access control. Like cats authenticate each other through scent, TigerStyle Scent provides secure OAuth2 authentication for your WordPress territory.
* Version: 1.0.0
* Author: TigerStyle
* Author URI: https://tigerstyle.com
* License: GPL v2 or later
* Text Domain: tigerstyle-scent
* Domain Path: /languages
* Requires at least: 5.0
* Tested up to: 6.4
* Requires PHP: 7.4
* Network: false
*/
// Prevent direct access to maintain territory security
defined('ABSPATH') or die('🐯 Direct access to TigerStyle territory forbidden.');
// Plugin constants for scent trail management
define('TIGERSTYLE_SCENT_VERSION', '1.0.0');
define('TIGERSTYLE_SCENT_PLUGIN_FILE', __FILE__);
define('TIGERSTYLE_SCENT_PLUGIN_DIR', plugin_dir_path(__FILE__));
define('TIGERSTYLE_SCENT_PLUGIN_URL', plugin_dir_url(__FILE__));
define('TIGERSTYLE_SCENT_PLUGIN_BASENAME', plugin_basename(__FILE__));
// Debug mode for scent trail tracking
if (!defined('TIGERSTYLE_SCENT_DEBUG')) {
define('TIGERSTYLE_SCENT_DEBUG', WP_DEBUG);
}
/**
* Main TigerStyle Scent Plugin Class
*
* Manages the entire OAuth2 authentication ecosystem like an alpha cat managing territory
*/
class TigerStyleScent {
/**
* Single instance of the plugin (singleton pattern like a territorial cat)
* @var TigerStyleScent|null
*/
private static $instance = null;
/**
* Plugin modules (like different scent detection systems)
* @var array
*/
private $modules = array();
/**
* Plugin settings
* @var array
*/
private $settings = array();
/**
* Scent server instance
* @var TigerStyleScent_ScentServer|null
*/
private $scent_server = null;
/**
* Get singleton instance (territorial control)
*
* @return TigerStyleScent
*/
public static function instance() {
if (is_null(self::$instance)) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Constructor - Initialize the TigerStyle Scent territory
*/
private function __construct() {
$this->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 {
?>
<div class="wrap">
<h1>🐯 TigerStyle Scent - Territory Control</h1>
<p>Welcome to your OAuth2 authentication territory! Manage your digital scent trails and access control.</p>
<div class="tigerstyle-dashboard">
<div class="tigerstyle-card">
<h2>🏰 Territory Status</h2>
<p><strong>Plugin Version:</strong> <?php echo TIGERSTYLE_SCENT_VERSION; ?></p>
<p><strong>Scent Detection:</strong> <?php echo $this->settings['enable_scent_authentication'] ? '✅ Active' : '❌ Inactive'; ?></p>
<p><strong>Territory Security:</strong> <?php echo $this->settings['scent_strength']; ?></p>
</div>
<div class="tigerstyle-card">
<h2>🐾 Quick Actions</h2>
<p><a href="<?php echo admin_url('post-new.php?post_type=oauth2_client'); ?>" class="button button-primary"> Add New Scent Profile</a></p>
<p><a href="<?php echo admin_url('admin.php?page=tigerstyle-scent-analysis'); ?>" class="button">👃 Analyze Scent Trails</a></p>
<p><button id="test-scent-auth" class="button">🧪 Test Scent Recognition</button></p>
</div>
</div>
<style>
.tigerstyle-dashboard { display: flex; gap: 20px; margin-top: 20px; }
.tigerstyle-card { background: white; padding: 20px; border: 1px solid #ccd0d4; border-radius: 8px; flex: 1; }
.tigerstyle-card h2 { margin-top: 0; color: #ff6b35; }
</style>
</div>
<?php
}
/**
* Scent analysis page
*/
public function scent_analysis_page(): void {
?>
<div class="wrap">
<h1>👃 Scent Analysis - Territory Monitoring</h1>
<p>Monitor and analyze scent token activity in your territory.</p>
<div class="tigerstyle-analysis">
<h2>Recent Scent Activity</h2>
<p><em>Scent trail monitoring coming soon...</em></p>
</div>
</div>
<?php
}
/**
* Enqueue admin assets
*/
public function enqueue_admin_assets($hook): void {
if (strpos($hook, 'tigerstyle-scent') !== false) {
wp_enqueue_script(
'tigerstyle-scent-admin',
TIGERSTYLE_SCENT_PLUGIN_URL . 'admin/js/admin.js',
array('jquery'),
TIGERSTYLE_SCENT_VERSION,
true
);
wp_localize_script('tigerstyle-scent-admin', 'tigerStyleScent', array(
'ajaxUrl' => 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();