From 0736283d36e0e0742f37d00cb26cd896346dab2d Mon Sep 17 00:00:00 2001 From: Joshua Lamb Date: Thu, 24 Jul 2025 10:39:10 -0400 Subject: [PATCH 1/2] Modify rcmail_oauth.php and config defaults to allow changing of scope when fetching identity information. Fixes M365 oauth login. Refs #9598 --- config/defaults.inc.php | 4 ++++ program/include/rcmail_oauth.php | 24 +++++++++++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/config/defaults.inc.php b/config/defaults.inc.php index 14df04f7c7..f9fa25f422 100644 --- a/config/defaults.inc.php +++ b/config/defaults.inc.php @@ -384,6 +384,9 @@ // Mandatory: OAuth scopes to request (space-separated string) $config['oauth_scope'] = null; +// Optional: OAuth scopes specific to identity request (space-separated string) +$config['oauth_scope_identity'] = null; + // Optional: additional query parameters to send with login request (hash array) $config['oauth_auth_parameters'] = []; @@ -455,6 +458,7 @@ // $config['oauth_identity_uri'] = "https://graph.microsoft.com/v1.0/me"; // $config['oauth_identity_fields'] = ['email', 'userPrincipalName']; // $config['oauth_scope'] = "https://outlook.office365.com/IMAP.AccessAsUser.All https://outlook.office365.com/SMTP.Send User.Read offline_access"; +// $config['oauth_scope_identity'] = "https://graph.microsoft.com/IMAP.AccessAsUser.All SMTP.Send offline_access User.Read"; // $config['oauth_auth_parameters'] = ['nonce' => mt_rand()]; // ---------------------------------- diff --git a/program/include/rcmail_oauth.php b/program/include/rcmail_oauth.php index d942fbcc42..268462e388 100644 --- a/program/include/rcmail_oauth.php +++ b/program/include/rcmail_oauth.php @@ -161,6 +161,7 @@ public function __construct($options = []) 'language' => ['locale'], ]), 'scope' => $this->rcmail->config->get('oauth_scope', ''), + 'scope_identity' => $this->rcmail->config->get('oauth_scope_identity', ''), 'timeout' => $this->rcmail->config->get('oauth_timeout', 10), 'verify_peer' => $this->rcmail->config->get('oauth_verify_peer', true), 'auth_parameters' => $this->rcmail->config->get('oauth_auth_parameters', []), @@ -641,6 +642,13 @@ public function request_access_token($auth_code, $state = null) // request user identity (email) if (empty($username)) { + if($this->options['scope_identity']) { + $this->mask_auth_data($data); + $refresh_response = $this->refresh_access_token($data, $this->options['scope_identity']); + $data = $refresh_response['token']; + $authorization = $refresh_response['authorization']; + } + $fetched_identity = $this->fetch_userinfo($authorization); $this->log_debug('fetched identity: %s', json_encode($fetched_identity, true)); @@ -655,6 +663,13 @@ public function request_access_token($auth_code, $state = null) } } } + + // Restore scope for general mail functions + if($this->options['scope_identity']) { + $refresh_response = $this->refresh_access_token($data, $this->options['scope']); + $data = $refresh_response['token']; + $authorization = $refresh_response['authorization']; + } } $data['auth_type'] = $this->auth_type; @@ -720,11 +735,14 @@ public function request_access_token($auth_code, $state = null) * If successful, this will update the `oauth_token` entry in * session data. * + * @param array $token + * @param string $change_scope Optional, changes scope if specified + * * @return array|false Updated authorization data * * @see https://datatracker.ietf.org/doc/html/rfc6749#section-6 */ - public function refresh_access_token(array $token) + public function refresh_access_token(array $token, $change_scope = null) { $oauth_token_uri = $this->options['token_uri']; $oauth_client_id = $this->options['client_id']; @@ -741,6 +759,10 @@ public function refresh_access_token(array $token) 'client_secret' => $oauth_client_secret, ]; + if($change_scope) { + $form['scope'] = $change_scope; + } + if ($this->options['pkce']) { $form['code_verifier'] = $this->rcmail->decrypt($_SESSION['oauth_code_verifier']); } From dc93b5f9201d8ce73c5412598adf2b1d5785a6fe Mon Sep 17 00:00:00 2001 From: Joshua Lamb Date: Thu, 31 Jul 2025 12:44:31 -0400 Subject: [PATCH 2/2] Revision - remove the second call to refresh_access_token per suggestion from alecpl - Refs #9598 --- program/include/rcmail_oauth.php | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/program/include/rcmail_oauth.php b/program/include/rcmail_oauth.php index 268462e388..cc4a685797 100644 --- a/program/include/rcmail_oauth.php +++ b/program/include/rcmail_oauth.php @@ -645,11 +645,11 @@ public function request_access_token($auth_code, $state = null) if($this->options['scope_identity']) { $this->mask_auth_data($data); $refresh_response = $this->refresh_access_token($data, $this->options['scope_identity']); - $data = $refresh_response['token']; - $authorization = $refresh_response['authorization']; + $data_ident = $refresh_response['token']; + $authorization_ident = $refresh_response['authorization']; } - $fetched_identity = $this->fetch_userinfo($authorization); + $fetched_identity = $this->fetch_userinfo($authorization_ident); $this->log_debug('fetched identity: %s', json_encode($fetched_identity, true)); @@ -663,13 +663,6 @@ public function request_access_token($auth_code, $state = null) } } } - - // Restore scope for general mail functions - if($this->options['scope_identity']) { - $refresh_response = $this->refresh_access_token($data, $this->options['scope']); - $data = $refresh_response['token']; - $authorization = $refresh_response['authorization']; - } } $data['auth_type'] = $this->auth_type;