voice2text/auth.php
2025-11-03 15:23:55 +00:00

114 lines
4 KiB
PHP
Executable file

<?php
/**
* OAuth2 PKCE Authentication Handler
* Handles Microsoft Azure AD authentication flow with PKCE
*/
require_once 'auth_config.php';
use League\OAuth2\Client\Provider\GenericProvider;
// Start session
session_start();
// Azure AD OAuth2 Provider configuration
$provider = new GenericProvider([
'clientId' => 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'
]);
// Step 1: No authorization code - initiate OAuth flow
if (!isset($_GET['code'])) {
// Generate PKCE code verifier and challenge
$codeVerifier = bin2hex(random_bytes(32)); // 64-character random string
$codeChallenge = rtrim(strtr(base64_encode(hash('sha256', $codeVerifier, true)), '+/', '-_'), '=');
// Store code verifier in session for later use
$_SESSION['oauth2_code_verifier'] = $codeVerifier;
// Generate authorization URL with PKCE parameters
$authorizationUrl = $provider->getAuthorizationUrl([
'scope' => 'openid profile email User.Read',
'code_challenge' => $codeChallenge,
'code_challenge_method' => 'S256',
'response_type' => 'code',
'response_mode' => 'query'
]);
// Store state for CSRF protection
$_SESSION['oauth2state'] = $provider->getState();
// Redirect to Azure AD
header('Location: ' . $authorizationUrl);
exit;
}
// Step 2: Authorization code received - exchange for access token
elseif (isset($_GET['code'])) {
// Verify state to prevent CSRF attacks
if (empty($_GET['state']) || (isset($_SESSION['oauth2state']) && $_GET['state'] !== $_SESSION['oauth2state'])) {
unset($_SESSION['oauth2state']);
unset($_SESSION['oauth2_code_verifier']);
die('Invalid state. Possible CSRF attack.');
}
try {
// Retrieve code verifier from session
if (!isset($_SESSION['oauth2_code_verifier'])) {
die('Code verifier not found in session.');
}
$codeVerifier = $_SESSION['oauth2_code_verifier'];
// 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
header('Location: index.php');
exit;
} catch (\League\OAuth2\Client\Provider\Exception\IdentityProviderException $e) {
// Handle authentication errors
die('Authentication failed: ' . htmlspecialchars($e->getMessage()));
} catch (\Exception $e) {
// Handle other errors
die('An error occurred: ' . htmlspecialchars($e->getMessage()));
}
}