diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..dbafee3 --- /dev/null +++ b/.env.example @@ -0,0 +1,13 @@ +# MSAL Authentication Configuration +# Set SSO_ENABLED=true to require Microsoft login +# Set SSO_ENABLED=false for local development (uses mock user) +SSO_ENABLED=false + +# Azure AD Configuration (required when SSO_ENABLED=true) +# Get these values from your Azure AD App Registration +SSO_TENANT_ID=your-azure-tenant-id-here +SSO_CLIENT_ID=your-azure-application-client-id-here + +# Example values (replace with your actual Azure AD credentials): +# SSO_TENANT_ID=e519c2e6-bc6d-4fdf-8d9c-923c2f002385 +# SSO_CLIENT_ID=9079054c-9620-4757-a256-23413042f1ef diff --git a/.gitignore b/.gitignore index 8cb01e4..e479e54 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,13 @@ # Configuration file with API key config.php +# Environment configuration +.env + +# Composer dependencies +/vendor/ +composer.lock + # PHP session files sessions/ diff --git a/AUTH_README.md b/AUTH_README.md new file mode 100644 index 0000000..9eeeadf --- /dev/null +++ b/AUTH_README.md @@ -0,0 +1,305 @@ +# MSAL Authentication Setup Guide + +## Overview +Nano Banana Pro now includes Microsoft Authentication Library (MSAL) / Azure AD Single Sign-On (SSO) authentication. The authentication can be **toggled on/off** via environment variable for seamless testing and deployment. + +--- + +## Quick Start + +### Local Development (No Authentication) +```bash +# 1. Ensure .env file exists with: +SSO_ENABLED=false + +# 2. Run the app normally in MAMP +# All users get mock "Local Developer" credentials +# No login required +``` + +### Production (with SSO) +```bash +# 1. Update .env file: +SSO_ENABLED=true +SSO_TENANT_ID=your-azure-tenant-id +SSO_CLIENT_ID=your-azure-application-id + +# 2. Deploy to server +# 3. Users must login with Microsoft account +``` + +--- + +## Installation Steps + +### 1. Install Dependencies +```bash +cd /Users/daveporter/Desktop/CODING-2024/NANO-RESEARCH +composer install +``` + +This installs the Firebase JWT library required for token validation. + +### 2. Configure Environment +```bash +# Copy example file +cp .env.example .env + +# Edit .env and set: +SSO_ENABLED=false # Start with authentication disabled +``` + +### 3. Azure AD Setup (When Enabling SSO) + +#### Create Azure AD App Registration: +1. Go to [Azure Portal](https://portal.azure.com) +2. Navigate to: **Azure Active Directory** → **App registrations** → **New registration** +3. Set name: "Nano Banana Pro" +4. Set redirect URI: `https://your-server-url.com/path/to/app/index.php` +5. Click **Register** + +#### Get Credentials: +1. Copy **Application (client) ID** → This is your `SSO_CLIENT_ID` +2. Copy **Directory (tenant) ID** → This is your `SSO_TENANT_ID` +3. Go to **Authentication** → Enable **ID tokens** checkbox +4. Go to **API permissions** → Add: `openid`, `profile`, `email` + +#### Update .env: +```bash +SSO_ENABLED=true +SSO_TENANT_ID=e519c2e6-bc6d-4fdf-8d9c-923c2f002385 +SSO_CLIENT_ID=9079054c-9620-4757-a256-23413042f1ef +``` + +--- + +## File Structure + +### New Files Created: +``` +/NANO-RESEARCH/ +├── composer.json # PHP dependencies (Firebase JWT) +├── .env # Environment config (gitignored) +├── .env.example # Template for environment variables +├── env_loader.php # Loads .env file +├── JWTValidator.php # JWT token validation logic +├── AuthMiddleware.php # Auth orchestrator + login UI +├── auth.php # Auth API endpoint +├── auth-test.php # Debugging page +├── AUTH_README.md # This file +└── vendor/ # Composer dependencies (gitignored) +``` + +### Modified Files: +``` +config.php # Added SSO constants +index.php # Added auth check, logout button +api.php # Added auth check +enhance_prompt.php # Added auth check +.gitignore # Added .env and vendor/ +``` + +--- + +## How It Works + +### When SSO_ENABLED=false (Testing Mode) +1. User visits app +2. AuthMiddleware returns mock "Local Developer" user +3. No login page shown +4. All features work normally +5. Perfect for local testing + +### When SSO_ENABLED=true (Production Mode) +1. User visits app +2. AuthMiddleware checks for `auth_token` cookie +3. If no token → Show MSAL login page +4. User clicks "Sign In with Microsoft" +5. MSAL popup opens for Azure AD login +6. User authenticates +7. Token sent to `auth.php` for validation +8. JWT validated against Azure AD public keys +9. Token stored in httpOnly cookie (24 hours) +10. User redirected to app +11. Logout button visible in header + +--- + +## Testing + +### Test Authentication Status +Visit: `http://your-server/auth-test.php` + +Shows: +- SSO configuration (enabled/disabled) +- Tenant ID and Client ID +- Current authentication status +- User information +- Cookie presence + +### Test Locally (SSO Disabled) +```bash +# 1. Set SSO_ENABLED=false in .env +# 2. Open app in MAMP +# 3. Should see "Welcome, Local Developer" (if SSO was previously enabled) +# 4. App functions normally +# 5. No login/logout buttons +``` + +### Test on Server (SSO Enabled) +```bash +# NOTE: Cannot test locally - Azure AD requires exact redirect URI match + +# 1. Deploy to production server +# 2. Set SSO_ENABLED=true in .env on server +# 3. Add Azure AD credentials to .env +# 4. Visit app URL +# 5. Should see login page +# 6. Click "Sign In with Microsoft" +# 7. Complete Microsoft login +# 8. Should redirect to app +# 9. Should see "Welcome, [Your Name]" and logout button +``` + +--- + +## Security Features + +✅ **httpOnly Cookies** - Prevents XSS attacks (JavaScript can't access token) +✅ **SameSite=Lax** - Prevents CSRF attacks +✅ **Secure Flag** - Cookie only sent over HTTPS in production +✅ **JWT Validation** - Cryptographic verification of tokens +✅ **Expiration Check** - Validates `exp` claim +✅ **Not-Before Check** - Validates `nbf` claim +✅ **Audience Validation** - Ensures token is for our app +✅ **Issuer Validation** - Ensures token from Azure AD +✅ **JWKS Verification** - Uses Azure AD public keys +✅ **24-Hour Expiration** - Tokens expire after 1 day + +--- + +## Troubleshooting + +### Login Page Shows But Can't Login +- Check Azure AD app registration has correct redirect URI +- Ensure `SSO_TENANT_ID` and `SSO_CLIENT_ID` are correct +- Check browser console for MSAL errors +- Visit `auth-test.php` to verify configuration + +### "Authentication Required" Error +- Check `auth_token` cookie exists (browser dev tools) +- Token may have expired (24-hour limit) +- Try logging out and back in +- Check `auth-test.php` for token status + +### SSO Not Disabling +- Verify `.env` has `SSO_ENABLED=false` (not "false" in quotes) +- Clear browser cookies +- Restart PHP server/MAMP +- Check `auth-test.php` shows "SSO Enabled: NO" + +### Token Validation Failing +- Check server can reach Azure AD endpoints +- Verify tenant ID and client ID match Azure AD +- Check token hasn't expired +- Review `error_log` for JWT validation details + +--- + +## API Endpoints + +### Login +```http +POST /auth.php +Content-Type: application/json + +{ + "action": "login", + "idToken": "eyJ0eXAiOiJKV1QiLCJhbGci...", + "accessToken": "eyJ0eXAiOiJKV1QiLCJhbGci..." +} +``` + +### Logout +```http +POST /auth.php +Content-Type: application/json + +{ + "action": "logout" +} +``` + +### Status Check +```http +POST /auth.php +Content-Type: application/json + +{ + "action": "status" +} +``` + +--- + +## Maintenance + +### Rotating Credentials +1. Update Azure AD app registration +2. Update `.env` with new credentials +3. No code changes needed +4. Existing sessions remain valid until cookie expires + +### Disabling SSO Temporarily +```bash +# In .env: +SSO_ENABLED=false + +# Immediately disables SSO for all users +# No restart needed +# Users get mock "Local Developer" access +``` + +### Monitoring +- Check `error_log` for authentication failures +- Monitor Azure AD sign-in logs +- Track failed login attempts +- Review token validation errors + +--- + +## Production Checklist + +Before enabling SSO in production: + +- [ ] Composer dependencies installed (`vendor/` directory exists) +- [ ] `.env` file configured with Azure AD credentials +- [ ] Azure AD app registration created +- [ ] Redirect URI matches production URL exactly +- [ ] ID tokens enabled in Azure AD app +- [ ] API permissions added (`openid`, `profile`, `email`) +- [ ] HTTPS enabled on production server +- [ ] `auth-test.php` shows correct configuration +- [ ] Test login/logout flow works +- [ ] Error logging enabled + +--- + +## Support + +For issues with: +- **MSAL errors**: Check [MSAL.js documentation](https://github.com/AzureAD/microsoft-authentication-library-for-js) +- **Azure AD setup**: Check [Azure AD app registration guide](https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app) +- **JWT validation**: Check Firebase JWT library logs in `error_log` +- **Configuration**: Run `auth-test.php` to see current setup + +--- + +## Important Notes + +- **Cannot test MSAL locally** - Azure AD requires exact URL match +- **Testing happens on server** after deployment +- **SSO toggle allows testing without auth** before enabling +- **httpOnly cookies** mean token not accessible via JavaScript +- **24-hour token expiration** - users must re-login daily +- **Mock user** (`dev@localhost`) used when SSO disabled diff --git a/AuthMiddleware.php b/AuthMiddleware.php new file mode 100644 index 0000000..092e724 --- /dev/null +++ b/AuthMiddleware.php @@ -0,0 +1,409 @@ +ssoEnabled = SSO_ENABLED; + $this->tenantId = SSO_TENANT_ID; + $this->clientId = SSO_CLIENT_ID; + + // Only initialize validator if SSO is enabled + if ($this->ssoEnabled) { + require_once __DIR__ . '/JWTValidator.php'; + $this->validator = new JWTValidator($this->tenantId, $this->clientId); + } + } + + /** + * Check if SSO is enabled + * + * @return bool + */ + public function isSSOEnabled() { + return $this->ssoEnabled; + } + + /** + * Check if user is authenticated + * + * @return array ['authenticated' => bool, 'user' => array|null, 'error' => string|null] + */ + public function isAuthenticated() { + // If SSO is disabled, return authenticated with mock user + if (!$this->ssoEnabled) { + return [ + 'authenticated' => true, + 'user' => [ + 'name' => 'Local Developer', + 'preferred_username' => 'dev@localhost' + ] + ]; + } + + // Get token from cookie + $token = $this->getTokenFromCookie(); + if (!$token) { + return ['authenticated' => false, 'error' => 'No authentication token found']; + } + + // Validate token + $validation = $this->validator->validateToken($token); + if (!$validation['valid']) { + return ['authenticated' => false, 'error' => $validation['error']]; + } + + return ['authenticated' => true, 'user' => $validation['payload']]; + } + + /** + * Require authentication - blocks if not authenticated + * + * @return array User data + */ + public function requireAuth() { + // If SSO is disabled, return mock user + if (!$this->ssoEnabled) { + return [ + 'name' => 'Local Developer', + 'preferred_username' => 'dev@localhost' + ]; + } + + // Check authentication + $auth = $this->isAuthenticated(); + + if (!$auth['authenticated']) { + $this->handleUnauthorized($auth['error'] ?? 'Authentication required'); + exit; + } + + return $auth['user']; + } + + /** + * Set authentication token (after login) + * + * @param string $token JWT token from MSAL + * @return array ['success' => bool, 'user' => array|null, 'error' => string|null] + */ + public function setAuthToken($token) { + if (!$this->ssoEnabled) { + return ['success' => false, 'error' => 'SSO is disabled']; + } + + // Validate token + $validation = $this->validator->validateToken($token); + if (!$validation['valid']) { + return ['success' => false, 'error' => $validation['error']]; + } + + // Set httpOnly cookie with security options + $cookieOptions = [ + 'expires' => time() + (24 * 60 * 60), // 24 hours + 'path' => '/', + 'domain' => '', + 'secure' => isset($_SERVER['HTTPS']), // Only over HTTPS in production + 'httponly' => true, + 'samesite' => 'Lax' + ]; + + setcookie('auth_token', $token, $cookieOptions); + + return ['success' => true, 'user' => $validation['payload']]; + } + + /** + * Clear authentication token (logout) + */ + public function clearAuthToken() { + setcookie('auth_token', '', [ + 'expires' => time() - 3600, + 'path' => '/', + 'httponly' => true + ]); + } + + /** + * Get token from cookie + * + * @return string|null + */ + private function getTokenFromCookie() { + return isset($_COOKIE['auth_token']) ? $_COOKIE['auth_token'] : null; + } + + /** + * Handle unauthorized access + * + * @param string $error Error message + */ + private function handleUnauthorized($error) { + if ($this->isAjaxRequest()) { + // For AJAX requests, return JSON + header('Content-Type: application/json'); + http_response_code(401); + echo json_encode([ + 'error' => 'Authentication required', + 'message' => $error, + 'requiresAuth' => true + ]); + } else { + // For page requests, show login interface + $this->showLoginPage($error); + } + } + + /** + * Check if request is AJAX + * + * @return bool + */ + private function isAjaxRequest() { + return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && + strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest'; + } + + /** + * Show login page with MSAL integration + * + * @param string $error Optional error message + */ + public function showLoginPage($error = '') { + ?> + + + + + + Sign In - Nano Banana Pro + + + + + + + +
+ +

Nano Banana Pro

+

AI Image Generation & Editing Tool

+ + +
+ +
+ + + + +
+ Sign in with your Microsoft account to access the image generator +
+
+ + + + + tenantId = $tenantId; + $this->clientId = $clientId; + } + + /** + * Validate a JWT token + * + * @param string $token The JWT token to validate + * @return array ['valid' => bool, 'payload' => array|null, 'error' => string|null] + */ + public function validateToken($token) { + if (empty($token)) { + return ['valid' => false, 'error' => 'Token is empty']; + } + + try { + // Get public keys from Azure AD + $jwks = $this->getJWKS(); + if (!$jwks) { + return ['valid' => false, 'error' => 'Could not retrieve public keys from Azure AD']; + } + + // Convert JWKS to Key objects + $keys = JWK::parseKeySet($jwks); + + // Decode and validate the JWT + $decoded = JWT::decode($token, $keys); + $payload = (array) $decoded; + + // Validate claims + $validation = $this->validateClaims($payload); + if (!$validation['valid']) { + return $validation; + } + + return ['valid' => true, 'payload' => $payload]; + + } catch (\Firebase\JWT\ExpiredException $e) { + return ['valid' => false, 'error' => 'Token has expired']; + } catch (\Firebase\JWT\SignatureInvalidException $e) { + return ['valid' => false, 'error' => 'Token signature is invalid']; + } catch (\Firebase\JWT\BeforeValidException $e) { + return ['valid' => false, 'error' => 'Token is not yet valid']; + } catch (Exception $e) { + return ['valid' => false, 'error' => 'JWT validation failed: ' . $e->getMessage()]; + } + } + + /** + * Validate JWT claims + * + * @param array $payload The decoded JWT payload + * @return array ['valid' => bool, 'error' => string|null] + */ + private function validateClaims($payload) { + $now = time(); + + // Check expiration (exp claim) + if (isset($payload['exp']) && $payload['exp'] < $now) { + return ['valid' => false, 'error' => 'Token has expired']; + } + + // Check not-before (nbf claim) + if (isset($payload['nbf']) && $payload['nbf'] > $now) { + return ['valid' => false, 'error' => 'Token is not yet valid']; + } + + // Validate audience (aud claim) - must be our client ID or Microsoft Graph + if (isset($payload['aud'])) { + $validAudiences = [ + $this->clientId, + '00000003-0000-0000-c000-000000000000', // Microsoft Graph + 'https://graph.microsoft.com' + ]; + + if (!in_array($payload['aud'], $validAudiences)) { + return ['valid' => false, 'error' => 'Invalid audience: ' . $payload['aud']]; + } + } + + // Validate issuer (iss claim) - must be from Azure AD tenant + if (isset($payload['iss'])) { + $validIssuers = [ + "https://login.microsoftonline.com/{$this->tenantId}/v2.0", + "https://login.microsoftonline.com/{$this->tenantId}/", + "https://sts.windows.net/{$this->tenantId}/", + "https://login.microsoftonline.com/common/v2.0" + ]; + + if (!in_array($payload['iss'], $validIssuers)) { + return ['valid' => false, 'error' => 'Invalid issuer: ' . $payload['iss']]; + } + } + + return ['valid' => true]; + } + + /** + * Get JWKS (JSON Web Key Set) from Azure AD + * + * @return array|null + */ + private function getJWKS() { + // Return cached JWKS if still valid + if ($this->jwksCache && (time() - $this->jwksCacheTime) < $this->jwksCacheDuration) { + return $this->jwksCache; + } + + // Get OpenID configuration from Azure AD + $configUrl = "https://login.microsoftonline.com/{$this->tenantId}/v2.0/.well-known/openid-configuration"; + + $config = $this->fetchJson($configUrl); + if (!$config || !isset($config['jwks_uri'])) { + error_log("Failed to get OpenID configuration from: $configUrl"); + return null; + } + + // Fetch JWKS from jwks_uri + $jwks = $this->fetchJson($config['jwks_uri']); + + if (!$jwks || !isset($jwks['keys'])) { + error_log("Failed to get JWKS from: " . $config['jwks_uri']); + return null; + } + + // Ensure all keys have the 'alg' parameter + foreach ($jwks['keys'] as &$key) { + if (!isset($key['alg'])) { + // Default to RS256 for RSA keys + if (isset($key['kty']) && $key['kty'] === 'RSA') { + $key['alg'] = 'RS256'; + } + } + } + + // Cache the JWKS + $this->jwksCache = $jwks; + $this->jwksCacheTime = time(); + + return $jwks; + } + + /** + * Fetch JSON from a URL + * + * @param string $url + * @return array|null + */ + private function fetchJson($url) { + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); + curl_setopt($ch, CURLOPT_TIMEOUT, 10); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); + + $response = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + $curlError = curl_error($ch); + curl_close($ch); + + if ($curlError) { + error_log("CURL Error fetching $url: $curlError"); + return null; + } + + if ($httpCode !== 200) { + error_log("HTTP Error $httpCode fetching $url"); + return null; + } + + $json = json_decode($response, true); + if (json_last_error() !== JSON_ERROR_NONE) { + error_log("JSON decode error fetching $url: " . json_last_error_msg()); + return null; + } + + return $json; + } +} diff --git a/api.php b/api.php index 7912150..3507b6c 100644 --- a/api.php +++ b/api.php @@ -1,10 +1,25 @@ isAuthenticated(); + +if (!$authStatus['authenticated']) { + http_response_code(401); + echo json_encode([ + 'success' => false, + 'error' => 'Authentication required', + 'requiresAuth' => true + ]); + exit; +} + // Initialize session manager for multi-user support $sessionManager = new SessionManager(); diff --git a/auth-test.php b/auth-test.php new file mode 100644 index 0000000..5c8eb57 --- /dev/null +++ b/auth-test.php @@ -0,0 +1,64 @@ +isSSOEnabled() ? 'YES' : 'NO') . "\n\n"; + + echo "3. Authentication Status:\n"; + $status = $auth->isAuthenticated(); + echo " Authenticated: " . ($status['authenticated'] ? 'YES' : 'NO') . "\n"; + + if ($status['authenticated']) { + echo " User Name: " . ($status['user']['name'] ?? 'Unknown') . "\n"; + echo " User Email: " . ($status['user']['preferred_username'] ?? $status['user']['upn'] ?? 'Unknown') . "\n"; + } else { + echo " Error: " . ($status['error'] ?? 'Unknown') . "\n"; + } + + echo "\n4. Cookie Check:\n"; + echo " auth_token cookie: " . (isset($_COOKIE['auth_token']) ? 'PRESENT' : 'NOT PRESENT') . "\n"; + + if (isset($_COOKIE['auth_token'])) { + $tokenLength = strlen($_COOKIE['auth_token']); + echo " Token length: " . $tokenLength . " chars\n"; + echo " Token preview: " . substr($_COOKIE['auth_token'], 0, 50) . "...\n"; + } + + echo "\n5. Environment Variables:\n"; + echo " SSO_ENABLED env: " . (getenv('SSO_ENABLED') ?: 'NOT SET') . "\n"; + echo " SSO_TENANT_ID env: " . (getenv('SSO_TENANT_ID') ?: 'NOT SET') . "\n"; + echo " SSO_CLIENT_ID env: " . (getenv('SSO_CLIENT_ID') ?: 'NOT SET') . "\n"; + + echo "\n6. Session Info:\n"; + if (session_status() === PHP_SESSION_ACTIVE) { + echo " Session active: YES\n"; + echo " Session ID: " . session_id() . "\n"; + } else { + echo " Session active: NO\n"; + } + + echo "\n=== Test Complete ===\n"; + +} catch (Exception $e) { + echo " ✗ Error: " . $e->getMessage() . "\n"; + echo " Stack trace:\n"; + echo $e->getTraceAsString() . "\n"; +} diff --git a/auth.php b/auth.php new file mode 100644 index 0000000..4e40467 --- /dev/null +++ b/auth.php @@ -0,0 +1,118 @@ + 'Invalid request - action required']); + exit; +} + +$action = $input['action']; + +// Handle different actions +switch ($action) { + case 'login': + handleLogin($auth, $input); + break; + + case 'logout': + handleLogout($auth); + break; + + case 'status': + handleStatus($auth); + break; + + default: + http_response_code(400); + echo json_encode(['error' => 'Unknown action: ' . $action]); + break; +} + +/** + * Handle login action + */ +function handleLogin($auth, $input) { + if (!$auth->isSSOEnabled()) { + http_response_code(400); + echo json_encode(['error' => 'SSO is disabled']); + return; + } + + // Prefer ID token for validation, fallback to access token + $token = $input['idToken'] ?? $input['accessToken'] ?? null; + + if (!$token) { + http_response_code(400); + echo json_encode(['error' => 'Authentication token is required']); + return; + } + + // Validate and set token + $result = $auth->setAuthToken($token); + + if ($result['success']) { + echo json_encode([ + 'success' => true, + 'message' => 'Authentication successful', + 'user' => [ + 'name' => $result['user']['name'] ?? 'Unknown', + 'email' => $result['user']['preferred_username'] ?? $result['user']['upn'] ?? 'Unknown' + ] + ]); + } else { + http_response_code(401); + echo json_encode([ + 'success' => false, + 'error' => $result['error'] + ]); + } +} + +/** + * Handle logout action + */ +function handleLogout($auth) { + $auth->clearAuthToken(); + echo json_encode([ + 'success' => true, + 'message' => 'Logged out successfully' + ]); +} + +/** + * Handle status check action + */ +function handleStatus($auth) { + $authStatus = $auth->isAuthenticated(); + + if ($authStatus['authenticated']) { + echo json_encode([ + 'authenticated' => true, + 'sso_enabled' => $auth->isSSOEnabled(), + 'user' => [ + 'name' => $authStatus['user']['name'] ?? 'Unknown', + 'email' => $authStatus['user']['preferred_username'] ?? $authStatus['user']['upn'] ?? 'Unknown' + ] + ]); + } else { + http_response_code(401); + echo json_encode([ + 'authenticated' => false, + 'sso_enabled' => $auth->isSSOEnabled(), + 'error' => $authStatus['error'] ?? 'Not authenticated' + ]); + } +} diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..d4be0a7 --- /dev/null +++ b/composer.json @@ -0,0 +1,21 @@ +{ + "name": "nano-banana-pro/image-generator", + "description": "AI Image Generation & Editing Tool with MSAL Authentication", + "type": "project", + "require": { + "php": ">=7.4", + "firebase/php-jwt": "^6.0" + }, + "require-dev": {}, + "autoload": { + "classmap": [ + "JWTValidator.php", + "AuthMiddleware.php", + "session_manager.php" + ] + }, + "config": { + "optimize-autoloader": true, + "sort-packages": true + } +} diff --git a/enhance_prompt.php b/enhance_prompt.php index fb5a193..5065182 100644 --- a/enhance_prompt.php +++ b/enhance_prompt.php @@ -5,9 +5,23 @@ */ require_once 'config.php'; +require_once 'AuthMiddleware.php'; header('Content-Type: application/json'); +// Check authentication +$auth = new AuthMiddleware(); +$authStatus = $auth->isAuthenticated(); + +if (!$authStatus['authenticated']) { + http_response_code(401); + echo json_encode([ + 'success' => false, + 'error' => 'Authentication required' + ]); + exit; +} + // Get POST data $input = json_decode(file_get_contents('php://input'), true); diff --git a/env_loader.php b/env_loader.php new file mode 100644 index 0000000..3551f82 --- /dev/null +++ b/env_loader.php @@ -0,0 +1,55 @@ +requireAuth(); // Blocks if not authenticated +$ssoEnabled = $auth->isSSOEnabled(); + // Initialize session manager for multi-user support require_once 'session_manager.php'; $sessionManager = new SessionManager(); @@ -767,8 +773,22 @@ $imageHistory = $sessionManager->getImageHistory();
-

Nano Banana Pro

-

AI Image Generation & Iterative Editing

+
+
+

Nano Banana Pro

+

AI Image Generation & Iterative Editing

+
+ +
+
+ Welcome, +
+ +
+ +
@@ -1431,6 +1451,57 @@ Session Directory: getSessionDir()); ?> + const msalConfig = { + auth: { + clientId: "", + authority: "https://login.microsoftonline.com/", + redirectUri: window.location.origin + window.location.pathname + }, + cache: { + cacheLocation: "sessionStorage", + storeAuthStateInCookie: true, + } + }; + + const myMSALObj = new msal.PublicClientApplication(msalConfig); + + function signOut() { + if (!confirm('Are you sure you want to log out?')) { + return; + } + + // Call server to clear cookie + fetch('auth.php', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + action: 'logout' + }) + }) + .then(() => { + // Also clear MSAL session + myMSALObj.logoutPopup().then(() => { + window.location.href = 'index.php'; + }).catch(error => { + console.error("Logout error:", error); + window.location.href = 'index.php'; + }); + }) + .catch(error => { + console.error("Logout error:", error); + window.location.href = 'index.php'; + }); + } + + + + +