Implemented complete Microsoft Authentication Library (MSAL) / Azure AD Single Sign-On (SSO) system following Ferrero app pattern. KEY FEATURE: Toggle authentication on/off via environment variable - SSO_ENABLED=false → Mock user, no login required (local dev) - SSO_ENABLED=true → Full Azure AD authentication (production) NEW FILES: - composer.json - Firebase JWT dependency - .env.example - Environment variable template - env_loader.php - Parse .env file - JWTValidator.php - Validate JWT tokens from Azure AD - AuthMiddleware.php - Core auth orchestrator with login UI - auth.php - Authentication API (login/logout/status) - auth-test.php - Debug authentication status - AUTH_README.md - Complete setup documentation UPDATED FILES: - config.php - Load env vars, add SSO constants - index.php - Require auth, add logout button, MSAL script - api.php - Add authentication check - enhance_prompt.php - Add authentication check - .gitignore - Exclude .env and vendor/ AUTHENTICATION FLOW: 1. User visits app → Auth check 2. If SSO disabled → Mock "Local Developer" user 3. If SSO enabled → Validate JWT from cookie 4. If no token → Show MSAL login page 5. User signs in → Token validated → Cookie set → App loads SECURITY FEATURES: ✅ httpOnly cookies (XSS prevention) ✅ SameSite=Lax (CSRF prevention) ✅ JWT signature validation ✅ Claims validation (exp, nbf, aud, iss) ✅ JWKS from Azure AD ✅ 24-hour token expiration ✅ Secure flag for HTTPS DEPENDENCIES INSTALLED: - firebase/php-jwt v6.11.1 TESTING: - Local: SSO disabled by default in .env - Server: Set SSO_ENABLED=true with Azure AD credentials - Cannot test MSAL locally (redirect URI bound to server) DEPLOYMENT: 1. Install composer dependencies 2. Configure .env with Azure AD credentials 3. Set SSO_ENABLED=true when ready 4. Visit auth-test.php to verify setup 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
55 lines
1.3 KiB
PHP
55 lines
1.3 KiB
PHP
<?php
|
|
/**
|
|
* Environment Variable Loader
|
|
* Parses .env file and sets environment variables
|
|
*/
|
|
|
|
function loadEnvFile($path = null) {
|
|
// Default to .env file in same directory
|
|
$path = $path ?? __DIR__ . '/.env';
|
|
|
|
// Check if .env file exists
|
|
if (!file_exists($path)) {
|
|
error_log("Warning: .env file not found at: $path");
|
|
return false;
|
|
}
|
|
|
|
// Read file line by line
|
|
$lines = file($path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
|
|
|
if ($lines === false) {
|
|
error_log("Error: Could not read .env file at: $path");
|
|
return false;
|
|
}
|
|
|
|
foreach ($lines as $line) {
|
|
// Skip comments
|
|
if (strpos(trim($line), '#') === 0) {
|
|
continue;
|
|
}
|
|
|
|
// Parse KEY=VALUE format
|
|
if (strpos($line, '=') !== false) {
|
|
list($key, $value) = explode('=', $line, 2);
|
|
|
|
// Trim whitespace
|
|
$key = trim($key);
|
|
$value = trim($value);
|
|
|
|
// Remove surrounding quotes if present
|
|
if (preg_match('/^(["\'])(.*)\1$/', $value, $matches)) {
|
|
$value = $matches[2];
|
|
}
|
|
|
|
// Set environment variable
|
|
putenv("$key=$value");
|
|
$_ENV[$key] = $value;
|
|
$_SERVER[$key] = $value;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Auto-load .env on include
|
|
loadEnvFile();
|