From 2cc352cacd10cd75be24debe62b2cc616db577c4 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 25 Feb 2026 22:46:27 +0100 Subject: [PATCH] Fix data saved with generic oauth --- htdocs/admin/oauthlogintokens.php | 2 +- .../modules/oauth/generic_oauthcallback.php | 61 ++++++++++++++----- .../OAuth/Common/Storage/DoliStorage.php | 10 ++- 3 files changed, 56 insertions(+), 17 deletions(-) diff --git a/htdocs/admin/oauthlogintokens.php b/htdocs/admin/oauthlogintokens.php index 99327e786ff..c06f9c7a49f 100644 --- a/htdocs/admin/oauthlogintokens.php +++ b/htdocs/admin/oauthlogintokens.php @@ -432,7 +432,7 @@ if ($mode == 'setup' && $user->admin) { print ''; if ($keyforprovider != 'Login') { if (is_object($tokenobj)) { - print $form->textwithpicto(yn(1), $langs->trans("HasAccessToken").' : '.dol_print_date($storage->date_modification, 'dayhour').' state='.dol_escape_htmltag($storage->state)); + print $form->textwithpicto(yn(1), $langs->trans("HasAccessToken").' : '.dol_print_date($storage->date_modification, 'dayhour').'
Scopes saved into field state='.dol_escape_htmltag($storage->state)); } else { print ''.$langs->trans("NoAccessToken").''; } diff --git a/htdocs/core/modules/oauth/generic_oauthcallback.php b/htdocs/core/modules/oauth/generic_oauthcallback.php index 9a89ce154d6..5258d3e69f2 100644 --- a/htdocs/core/modules/oauth/generic_oauthcallback.php +++ b/htdocs/core/modules/oauth/generic_oauthcallback.php @@ -114,9 +114,9 @@ if ($state) { } } -// Add a test to check that the state parameter is provided into URL when we make the first call to ask the redirect or when we receive the callback -// but not when callback was ok and we recall the page -if ($action != 'delete' && !GETPOST('afteroauthloginreturn') && (empty($statewithscopeonly) || empty($requestedpermissionsarray)) && $state != 'none') { +// Add a test to check that the state parameter is provided into URL when we make the first call to ask the redirect or when we receive the callback, +// but NOT when callback was ok and we recall the page +if ($action != 'delete' && !GETPOST('afteroauthloginreturn') && (empty($statewithscopeonly) || empty($requestedpermissionsarray)) && !preg_match('/^none/', $state)) { if (GETPOST('error') || GETPOST('error_description')) { setEventMessages($langs->trans("Error").' '.GETPOST('error_description'), null, 'errors'); } else { @@ -208,7 +208,7 @@ if (!GETPOST('code') && !GETPOST('error')) { if (empty($state) || $state == 'none') { // Generate a random state value to prevent CSRF attack. Store it into session juste after to check it when we will receive the callback from provider. - $state = bin2hex(random_bytes(16)); + $state = 'none-'.bin2hex(random_bytes(16)); } // If we enter this page without 'code' parameter, it means we click on the link from login page ($forlogin is set) or from setup page and we want to get the redirect @@ -301,24 +301,57 @@ if (!GETPOST('code') && !GETPOST('error')) { $db->begin(); - // This requests the token from the received OAuth code (call of the endpoint) - // Result is stored into object managed by class DoliStorage into includes/OAuth/Common/Storage/DoliStorage.php and into database table llx_oauth_token - $token = $apiService->requestAccessToken(GETPOST('code'), $state); - '@phan-var-force OAuth\Common\Token\AbstractToken $token'; + $token = null; + $last_insert_id = 0; + try { + // This requests the token from the received OAuth code (call of the endpoint) + // Result is stored into object managed by class DoliStorage into includes/OAuth/Common/Storage/DoliStorage.php and into database table llx_oauth_token + $token = $apiService->requestAccessToken(GETPOST('code'), $state); + '@phan-var-force OAuth\Common\Token\AbstractToken $token'; + + $storage = $apiService->getStorage(); + if (property_exists($storage, 'last_insert_id')) { + $last_insert_id = $storage->last_insert_id; + } + } catch (Exception $e) { + dol_syslog("Failed to get token with requestAccessToken: ".$e->getMessage(), LOG_ERR); + setEventMessages("Failed to get token with requestAccessToken: ".$e->getMessage(), null, 'errors'); + $errorincheck++; + } + + // The refresh token is inside the object token if the prompt was forced only. Otherwise, it may be found into extraParams section. + //$refreshtoken = $token->getRefreshToken(); + //var_dump($refreshtoken); + dol_syslog("requestAccessToken complete"); // The refresh token is inside the object token if the prompt was forced only. //$refreshtoken = $token->getRefreshToken(); //var_dump($refreshtoken); // Note: The extraparams has the 'id_token' than contains a lot of information about the user. - $extraparams = $token->getExtraParams(); + if ($token) { + $extraparams = $token->getExtraParams(); - $scope = empty($extraparams['scope']) ? '' : $extraparams['scope']; - $tokenstring = $token->getAccessToken(); - // Update entry in llx_oauth_token to store the scope associated to the token into field "state" (field should be renamed). - // It is not stored by default by DoliStorage. - // TODO Update using $scope and $tokenstring + $scope = empty($extraparams['scope']) ? '' : $extraparams['scope']; + $tokenstring = $token->getAccessToken(); + // Update entry in llx_oauth_token to store the scope associated to the token into field "state" (field should be renamed). + // It is not stored by default by DoliStorage. + // TODO Update using $scope and $tokenstring and $last_insert_id + $refreshtoken = empty($extraparams['refresh_token']) ? '' : $extraparams['refresh_token']; + if (empty($refreshtoken)) { + $refreshtoken = $token->getRefreshToken(); + } + if ($last_insert_id) { + $sqlupdate = "UPDATE ".MAIN_DB_PREFIX."oauth_token"; + $sqlupdate .= " SET state = '".(empty($scope) ? '' : $db->escape($scope))."', tokenstring = '".$db->escape($tokenstring)."', tokenstring_refresh = '".$db->escape($refreshtoken)."'"; + $sqlupdate .= " WHERE rowid = ".((int) $last_insert_id); + + $db->query($sqlupdate); + + //var_dump($scope, $token, $refreshtoken, $last_insert_id, $sqlupdate);exit; + } + } $username = ''; $useremail = ''; diff --git a/htdocs/includes/OAuth/Common/Storage/DoliStorage.php b/htdocs/includes/OAuth/Common/Storage/DoliStorage.php index 61ccec7d284..38e041a6640 100644 --- a/htdocs/includes/OAuth/Common/Storage/DoliStorage.php +++ b/htdocs/includes/OAuth/Common/Storage/DoliStorage.php @@ -66,6 +66,7 @@ class DoliStorage implements TokenStorageInterface public $date_modification; public $userid; // ID of user for user specific OAuth entries + public $last_insert_id; // The ID of last inserted record /** @@ -82,6 +83,7 @@ class DoliStorage implements TokenStorageInterface $this->tokens = array(); $this->states = array(); $this->tenant = $tenant; + $this->last_insert_id = 0; //$this->key = $key; //$this->stateKey = $stateKey; } @@ -144,7 +146,9 @@ class DoliStorage implements TokenStorageInterface $resql = $this->db->query($sql); if (!$resql) { dol_print_error($this->db); - } + } else { + $this->last_insert_id = ((int) $obj['rowid']); + } } else { // save $sql = "INSERT INTO ".MAIN_DB_PREFIX."oauth_token (service, token, entity, datec)"; @@ -154,7 +158,9 @@ class DoliStorage implements TokenStorageInterface $resql = $this->db->query($sql); if (!$resql) { dol_print_error($this->db); - } + } else { + $this->last_insert_id = $this->db->last_insert_id(MAIN_DB_PREFIX."oauth_token"); + } } //print $sql;