From f6845a046fddaa9c56977115b9f7a74fadfbf274 Mon Sep 17 00:00:00 2001 From: cs2000 Date: Wed, 4 Feb 2026 13:24:50 +0000 Subject: [PATCH] Changes for M365 oAuth - New callback endpoint to complete Microsoft OAuth web flow. - Validates admin session + OAuth state, exchanges authorization code for tokens, stores refresh/access tokens and expiry, and redirects with success/error feedback. --- admin/oauth_microsoft_mail_callback.php | 101 ++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 admin/oauth_microsoft_mail_callback.php diff --git a/admin/oauth_microsoft_mail_callback.php b/admin/oauth_microsoft_mail_callback.php new file mode 100644 index 00000000..9c7e6cac --- /dev/null +++ b/admin/oauth_microsoft_mail_callback.php @@ -0,0 +1,101 @@ + $session_state_expires) { + flash_alert("Microsoft OAuth callback validation failed. Please try connecting again.", 'error'); + redirect('/admin/settings_mail.php'); +} + +if (empty($config_mail_oauth_client_id) || empty($config_mail_oauth_client_secret) || empty($config_mail_oauth_tenant_id)) { + flash_alert("Microsoft OAuth settings are incomplete. Please fill Client ID, Client Secret, and Tenant ID.", 'error'); + redirect('/admin/settings_mail.php'); +} + +if (defined('BASE_URL') && !empty(BASE_URL)) { + $base_url = rtrim((string) BASE_URL, '/'); +} else { + $base_url = 'https://' . rtrim((string) $config_base_url, '/'); +} + +$redirect_uri = $base_url . '/admin/oauth_microsoft_mail_callback.php'; +$token_url = 'https://login.microsoftonline.com/' . rawurlencode($config_mail_oauth_tenant_id) . '/oauth2/v2.0/token'; +$scope = 'offline_access openid profile https://outlook.office.com/IMAP.AccessAsUser.All https://outlook.office.com/SMTP.Send'; + +$ch = curl_init($token_url); +curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); +curl_setopt($ch, CURLOPT_POST, true); +curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([ + 'client_id' => $config_mail_oauth_client_id, + 'client_secret' => $config_mail_oauth_client_secret, + 'grant_type' => 'authorization_code', + 'code' => $code, + 'redirect_uri' => $redirect_uri, + 'scope' => $scope, +], '', '&')); +curl_setopt($ch, CURLOPT_TIMEOUT, 20); + +$raw_body = curl_exec($ch); +$curl_err = curl_error($ch); +$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); +curl_close($ch); + +if ($raw_body === false || $http_code < 200 || $http_code >= 300) { + $reason = !empty($curl_err) ? $curl_err : "HTTP $http_code"; + flash_alert("Microsoft OAuth token exchange failed: $reason", 'error'); + redirect('/admin/settings_mail.php'); +} + +$json = json_decode($raw_body, true); +if (!is_array($json) || empty($json['refresh_token']) || empty($json['access_token'])) { + flash_alert("Microsoft OAuth token exchange failed: refresh token or access token missing.", 'error'); + redirect('/admin/settings_mail.php'); +} + +$refresh_token = (string) $json['refresh_token']; +$access_token = (string) $json['access_token']; +$expires_at = date('Y-m-d H:i:s', time() + (int)($json['expires_in'] ?? 3600)); + +$refresh_token_esc = mysqli_real_escape_string($mysqli, $refresh_token); +$access_token_esc = mysqli_real_escape_string($mysqli, $access_token); +$expires_at_esc = mysqli_real_escape_string($mysqli, $expires_at); + +mysqli_query($mysqli, "UPDATE settings SET + config_imap_provider = 'microsoft_oauth', + config_smtp_provider = 'microsoft_oauth', + config_mail_oauth_refresh_token = '$refresh_token_esc', + config_mail_oauth_access_token = '$access_token_esc', + config_mail_oauth_access_token_expires_at = '$expires_at_esc' + WHERE company_id = 1 +"); + +logAction("Settings", "Edit", "$session_name completed Microsoft OAuth connect flow for mail settings"); +flash_alert("Microsoft OAuth connected successfully. Token expires at $expires_at."); +redirect('/admin/settings_mail.php');