WIP oauth mail-authentication updates refresh-token and redirects to IdP, if refresh-token is expired

This commit is contained in:
ralf 2023-11-21 15:56:41 +02:00
parent fa71d9d450
commit 1c40074331
3 changed files with 29 additions and 12 deletions

View File

@ -1263,7 +1263,7 @@ class admin_mail
else
{
try {
if (empty($content['acc_imap_username']) && ($oauth = OpenIDConnectClient::providerByDomain(
if (($oauth = OpenIDConnectClient::providerByDomain(
$content['acc_oauth_username'] ?? $content['acc_imap_username'] ?? $content['ident_email'], $content['acc_imap_host'])))
{
$content += self::oauth2content($oauth);
@ -1541,17 +1541,21 @@ class admin_mail
$oidc->addScope($content['acc_oauth_scopes']);
}
if (!empty($content['acc_oauth_access_token']) || !empty($content['acc_oauth_refresh_token']))
if (!empty($content['acc_oauth_access_token']) ||
!empty($content['acc_oauth_refresh_token']) && $content['acc_oauth_refresh_token'] !== Mail\Credentials::UNAVAILABLE)
{
if (empty($content['acc_oauth_access_token']))
{
$content['acc_oauth_access_token'] = $oidc->refreshToken($content['acc_oauth_refresh_token']);
$content['acc_oauth_access_token'] = $oidc->refreshToken($content['acc_oauth_refresh_token'])->access_token;
}
if ($smtp)
if (!empty($content['acc_oauth_access_token']))
{
return new Horde_Smtp_Password_Xoauth2($content['acc_oauth_username'] ?? $content['acc_smtp_username'], $content['acc_oauth_access_token']);
if ($smtp)
{
return new Horde_Smtp_Password_Xoauth2($content['acc_oauth_username'] ?? $content['acc_smtp_username'], $content['acc_oauth_access_token']);
}
return new Horde_Imap_Client_Password_Xoauth2($content['acc_oauth_username'] ?? $content['acc_imap_username'], $content['acc_oauth_access_token']);
}
return new Horde_Imap_Client_Password_Xoauth2($content['acc_oauth_username'] ?? $content['acc_imap_username'], $content['acc_oauth_access_token']);
}
// Run OAuth authentication, will NOT return, but call success or failure callbacks below
$oidc->authenticateThen(__CLASS__.'::oauthAuthenticated', [$content], __CLASS__.'::oauthFailure', [$content]);

View File

@ -244,7 +244,7 @@ class Credentials
{
unset($results[$prefix.'password']);
$results[$prefix.'refresh_token'] = self::UNAVAILABLE; // no need to make it available
$results[$prefix.'access_token'] = self::getAccessToken($row['cred_username'], $password, $mailserver);
$results[$prefix.'access_token'] = self::getAccessToken($row['cred_username'], $password, $mailserver, $acc_id, $row['account_id']);
// if no extra imap&smtp username set, set the oauth one
foreach(['acc_imap_', 'acc_smtp_'] as $pre)
{
@ -267,11 +267,13 @@ class Credentials
* @param string $username
* @param string $refresh_token
* @param string|null $mailserver mailserver to detect oauth hosts
* @param ?int $acc_id to store updated refresh-token
* @param ?int $account_id ----------- " ------------
* @return string|null
*/
static protected function getAccessToken($username, $refresh_token, $mailserver=null)
static protected function getAccessToken(string $username, string $refresh_token, string $mailserver=null, int $acc_id=null, int $account_id=null)
{
return Api\Cache::getInstance(__CLASS__, 'access-token-'.$username.'-'.md5($refresh_token), static function() use ($username, $refresh_token, $mailserver)
return Api\Cache::getInstance(__CLASS__, 'access-token-'.$username.'-'.md5($refresh_token), static function() use ($acc_id, $account_id, $username, $refresh_token, $mailserver)
{
if (!($oidc = Api\Auth\OpenIDConnectClient::byDomain($username, $mailserver)))
{
@ -280,13 +282,24 @@ class Credentials
try
{
$token = $oidc->refreshToken($refresh_token);
return $token->access_token;
// if we got a new refresh-token, store it
if (isset($token->refresh_token) && $refresh_token !== $token->refresh_token && $acc_id > 0 && $account_id > 0)
{
self::write($acc_id, $username, $token->refresh_token, self::OAUTH_REFRESH_TOKEN, $account_id);
//error_log("Mail\\Credentials::getAccessToken($acc_id, $account_id, '$username', ..., ".json_encode($mailserver).") stored new refresh-token: ".json_encode($token));
}
if (isset($token->access_token))
{
return $token->access_token;
}
// we did NOT get an access-token
error_log("Mail\\Credentials::getAccessToken($acc_id, $account_id, '$username', '$refresh_token', ".json_encode($mailserver).") got NO access-token: ".json_encode($token));
}
catch (OpenIDConnectClientException $e) {
_egw_log_exception($e);
}
return null;
}, [], 3500); // access-token have a livetime of 3600s, give it some margin
}, [], 3500); // access-token have a lifetime of 3600s, give it some margin
}
/**

View File

@ -599,7 +599,7 @@ class mail_ui
catch (Exception $e)
{
// do not exit here. mail-tree should be build. if we exit here, we never get there.
error_log(__METHOD__.__LINE__.$e->getMessage().($e->details?', '.$e->details:'').' Menuaction:'.$_GET['menuaction'].'.'.function_backtrace());
_egw_log_exception($e);
if (isset($this->mail_bo))
{
if (empty($etpl))