From b8a34d8b480ac41cb4cb0d72f6a3719c41510efd Mon Sep 17 00:00:00 2001 From: michael Date: Mon, 3 Nov 2025 10:50:40 -0600 Subject: [PATCH] moved to SPA auth architecture with MSAL.js in the browser --- index.php | 245 ++++++++++++++++++--------------------------- login.php | 91 ++++++++++++++++- validate_token.php | 86 ++++++++++++++++ 3 files changed, 275 insertions(+), 147 deletions(-) create mode 100644 validate_token.php diff --git a/index.php b/index.php index ff68360..315dbec 100755 --- a/index.php +++ b/index.php @@ -1,139 +1,21 @@ AZURE_CLIENT_ID, - 'redirectUri' => AZURE_REDIRECT_URI, - 'urlAuthorize' => AZURE_AUTHORITY . '/oauth2/v2.0/authorize', - 'urlAccessToken' => AZURE_AUTHORITY . '/oauth2/v2.0/token', - 'urlResourceOwnerDetails' => 'https://graph.microsoft.com/v1.0/me', - 'scopes' => 'openid profile email User.Read' - ]); - - // Exchange authorization code for access token with PKCE - $accessToken = $provider->getAccessToken('authorization_code', [ - 'code' => $_GET['code'], - 'code_verifier' => $codeVerifier - ]); - - // Get user information from Microsoft Graph API - $request = $provider->getAuthenticatedRequest( - 'GET', - 'https://graph.microsoft.com/v1.0/me', - $accessToken->getToken() - ); - - $client = new \GuzzleHttp\Client(); - $response = $client->send($request); - $userData = json_decode($response->getBody(), true); - - // Store user information in session - $_SESSION['authenticated'] = true; - $_SESSION['user_id'] = $userData['id']; - $_SESSION['user_name'] = $userData['displayName'] ?? $userData['userPrincipalName']; - $_SESSION['user_email'] = $userData['userPrincipalName'] ?? $userData['mail']; - $_SESSION['access_token'] = $accessToken->getToken(); - $_SESSION['last_activity'] = time(); - - // Initialize user files array for tracking uploads - $_SESSION['user_files'] = []; - - // Clean up temporary session variables - unset($_SESSION['oauth2state']); - unset($_SESSION['oauth2_code_verifier']); - - // Regenerate session ID for security - session_regenerate_id(true); - - // Redirect to main application (clean URL, no query parameters) - header('Location: index.php'); - exit; - - } catch (\League\OAuth2\Client\Provider\Exception\IdentityProviderException $e) { - // Handle authentication errors - DETAILED DEBUG OUTPUT - echo ''; - echo '

Authentication Failed

'; - echo '

Error Message: ' . htmlspecialchars($e->getMessage()) . '

'; - - echo '

Azure AD Response Body:

'; - echo '
';
-        $responseBody = $e->getResponseBody();
-        if (is_array($responseBody)) {
-            echo htmlspecialchars(print_r($responseBody, true));
-        } else {
-            echo htmlspecialchars($responseBody);
-        }
-        echo '
'; - - echo '

Session Variables:

'; - echo '
';
-        echo 'oauth2state: ' . (isset($_SESSION['oauth2state']) ? $_SESSION['oauth2state'] : 'NOT SET') . "\n";
-        echo 'oauth2_code_verifier: ' . (isset($_SESSION['oauth2_code_verifier']) ? 'SET (length: ' . strlen($_SESSION['oauth2_code_verifier']) . ')' : 'NOT SET') . "\n";
-        echo 'authenticated: ' . (isset($_SESSION['authenticated']) ? ($_SESSION['authenticated'] ? 'true' : 'false') : 'NOT SET') . "\n";
-        echo '
'; - - echo '

Request Parameters:

'; - echo '
';
-        echo 'GET code: ' . (isset($_GET['code']) ? 'present (length: ' . strlen($_GET['code']) . ')' : 'missing') . "\n";
-        echo 'GET state: ' . (isset($_GET['state']) ? $_GET['state'] : 'missing') . "\n";
-        echo '
'; - - echo '

Configuration:

'; - echo '
';
-        echo 'AZURE_CLIENT_ID: ' . substr(AZURE_CLIENT_ID, 0, 8) . '...' . "\n";
-        echo 'AZURE_AUTHORITY: ' . AZURE_AUTHORITY . "\n";
-        echo 'AZURE_REDIRECT_URI: ' . AZURE_REDIRECT_URI . "\n";
-        echo 'DEV_MODE: ' . (DEV_MODE ? 'true' : 'false') . "\n";
-        echo '
'; - - echo '

← Back to Login

'; - echo ''; - die(); - } catch (\Exception $e) { - // Handle other errors - echo ''; - echo '

An Error Occurred

'; - echo '

Error: ' . htmlspecialchars($e->getMessage()) . '

'; - echo '
';
-        echo htmlspecialchars($e->getTraceAsString());
-        echo '
'; - echo '

← Back to Login

'; - echo ''; - die(); - } +// If not authenticated and not a potential MSAL callback, redirect to login +if (!$isPotentialMSALCallback && !isAuthenticated()) { + header('Location: login.php'); + exit; } -// Normal flow - require authentication -requireAuth(); - -// Get current user info +// Get current user info (may be null if MSAL callback is being processed) $user = getCurrentUser(); +$isAuthenticated = isAuthenticated(); ?> @@ -146,22 +28,23 @@ $user = getCurrentUser(); -
- -
- 🔧 DEV MODE ACTIVE - Authentication Bypassed -
- - -
- + + \ No newline at end of file diff --git a/login.php b/login.php index 64f5706..771fb48 100755 --- a/login.php +++ b/login.php @@ -162,7 +162,7 @@ if (isAuthenticated()) { Translate with DeepL into 30+ languages

- + + +

@@ -179,5 +182,89 @@ if (isAuthenticated()) {

+ + + + diff --git a/validate_token.php b/validate_token.php new file mode 100644 index 0000000..05cd871 --- /dev/null +++ b/validate_token.php @@ -0,0 +1,86 @@ + false, 'error' => 'Method not allowed']); + exit; +} + +// Get JSON payload +$json = file_get_contents('php://input'); +$data = json_decode($json, true); + +if (!isset($data['accessToken'])) { + http_response_code(400); + echo json_encode(['success' => false, 'error' => 'Access token required']); + exit; +} + +$accessToken = $data['accessToken']; + +try { + // Validate token by calling Microsoft Graph API + // If the token is valid, Graph API will return user info + // If invalid, it will fail + $ch = curl_init('https://graph.microsoft.com/v1.0/me'); + curl_setopt($ch, CURLOPT_HTTPHEADER, [ + 'Authorization: Bearer ' . $accessToken, + 'Content-Type: application/json' + ]); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_TIMEOUT, 10); + + $response = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + + if ($httpCode !== 200) { + http_response_code(401); + echo json_encode(['success' => false, 'error' => 'Invalid token']); + exit; + } + + // Parse user data + $userData = json_decode($response, true); + + if (!$userData || !isset($userData['id'])) { + http_response_code(500); + echo json_encode(['success' => false, 'error' => 'Failed to retrieve user information']); + exit; + } + + // Start session and store user information + if (session_status() === PHP_SESSION_NONE) { + session_start(); + } + + $_SESSION['authenticated'] = true; + $_SESSION['user_id'] = $userData['id']; + $_SESSION['user_name'] = $userData['displayName'] ?? $userData['userPrincipalName']; + $_SESSION['user_email'] = $userData['userPrincipalName'] ?? $userData['mail']; + $_SESSION['access_token'] = $accessToken; + $_SESSION['last_activity'] = time(); + $_SESSION['user_files'] = []; + + // Regenerate session ID for security + session_regenerate_id(true); + + // Return success + echo json_encode([ + 'success' => true, + 'user' => [ + 'name' => $_SESSION['user_name'], + 'email' => $_SESSION['user_email'] + ] + ]); + +} catch (\Exception $e) { + http_response_code(500); + echo json_encode(['success' => false, 'error' => $e->getMessage()]); +}