diff --git a/htdocs/conf/conf.php.example b/htdocs/conf/conf.php.example index 2a96e3873c0..d666da96ee1 100644 --- a/htdocs/conf/conf.php.example +++ b/htdocs/conf/conf.php.example @@ -216,7 +216,7 @@ $dolibarr_main_instance_unique_id='84b5bc91f83b56e458db71e0adac2b62'; // $dolibarr_main_authentication='forceuser'; // This need to add also $dolibarr_auto_user='loginforuser'; // $dolibarr_main_authentication='twofactor'; // To use Google Authenticator. This need the non official external module "Two Factor" available on www.dolistore.com // $dolibarr_main_authentication='openid_connect'; // See https://wiki.dolibarr.org/index.php?title=Authentication,_SSO_and_SSL -// $dolibarr_main_authentication='googleoauth'; // +// $dolibarr_main_authentication='googleoauth'; // Still in development // $dolibarr_main_authentication='dolibarr'; diff --git a/htdocs/core/modules/oauth/google_oauthcallback.php b/htdocs/core/modules/oauth/google_oauthcallback.php index 84f213813d5..b945a99ff14 100644 --- a/htdocs/core/modules/oauth/google_oauthcallback.php +++ b/htdocs/core/modules/oauth/google_oauthcallback.php @@ -25,7 +25,12 @@ * \brief Page to get oauth callback */ -if (!defined('NOLOGIN')) { +// Force keyforprovider +if (preg_match('/^forlogin-/', $_GET['state'])) { + $_GET['keyforprovider'] = 'Login'; +} + +if (!defined('NOLOGIN') && $_GET['keyforprovider'] == 'Login') { define("NOLOGIN", 1); // This means this output page does not require to be logged. } @@ -145,6 +150,7 @@ if (GETPOST('code')) { // We are coming from oauth provider page. // We must validate that the $state is the same than the one into $_SESSION['oauthstateanticsrf'], return error if not. if (isset($_SESSION['oauthstateanticsrf']) && $state != $_SESSION['oauthstateanticsrf']) { + //var_dump($_SESSION['oauthstateanticsrf']);exit; print 'Value for state = '.dol_escape_htmltag($state).' differs from value in $_SESSION["oauthstateanticsrf"]. Code is refused.'; unset($_SESSION['oauthstateanticsrf']); } else { @@ -152,9 +158,16 @@ if (GETPOST('code')) { // We are coming from oauth provider page. try { //var_dump($state); //var_dump($apiService); // OAuth\OAuth2\Service\Google + //dol_syslog("_GET=".var_export($_GET, true)); + //dol_syslog("_POST=".var_export($_POST, true)); + + $errorincheck = 0; + + $db->begin(); // This request the token // Result is stored into object managed by class DoliStorage into includes/OAuth/Common/Storage/DoliStorage.php, so into table llx_oauth_token + // TODO Store the token with fk_user = $user->id ? $token = $apiService->requestAccessToken(GETPOST('code'), $state); // Note: The extraparams has the 'id_token' than contains a lot of information about the user. @@ -165,30 +178,68 @@ if (GETPOST('code')) { // We are coming from oauth provider page. if (!empty($jwt[1])) { $userinfo = json_decode(base64_decode($jwt[1]), true); - // TODO - // We should make the 5 steps of validation of id_token - // Verify that the ID token is properly signed by the issuer. Google-issued tokens are signed using one of the certificates found at the URI specified in the jwks_uri metadata value of the Discovery document. - // Verify that the value of the iss claim in the ID token is equal to https://accounts.google.com or accounts.google.com. - // Verify that the value of the aud claim in the ID token is equal to your app's client ID. - // Verify that the expiry time (exp claim) of the ID token has not passed. - // If you specified a hd parameter value in the request, verify that the ID token has a hd claim that matches an accepted G Suite hosted domain. - + //dol_syslog("userinfo=".var_export($userinfo, true)); /* - $useremailuniq = $userinfo['sub']; - $useremail = $userinfo['email']; - $useremailverified = $userinfo['email_verified']; - $username = $userinfo['name']; - $userfamilyname = $userinfo['family_name']; - $usergivenname = $userinfo['given_name']; - $hd = $userinfo['hd']; - */ + $useremailuniq = $userinfo['sub']; + $useremail = $userinfo['email']; + $useremailverified = $userinfo['email_verified']; + $username = $userinfo['name']; + $userfamilyname = $userinfo['family_name']; + $usergivenname = $userinfo['given_name']; + $hd = $userinfo['hd']; + */ + + // We should make the 5 steps of validation of id_token + + // Verify that the ID token is properly signed by the issuer. Google-issued tokens are signed using one of the certificates found at the URI specified in the jwks_uri metadata value of the Discovery document. + // TODO + + // Verify that the value of the iss claim in the ID token is equal to https://accounts.google.com or accounts.google.com. + if ($userinfo['iss'] != 'accounts.google.com' && $userinfo['iss'] != 'https://accounts.google.com') { + setEventMessages($langs->trans('Bad value for returned userinfo[iss]'), null, 'errors'); + $errorincheck++; + } + + // Verify that the value of the aud claim in the ID token is equal to your app's client ID. + $keyforparamid = 'OAUTH_GOOGLE-'.$keyforprovider.'_ID'; + if ($userinfo['aud'] != getDolGlobalString($keyforparamid)) { + setEventMessages($langs->trans('Bad value for returned userinfo[aud]'), null, 'errors'); + $errorincheck++; + } + + // Verify that the expiry time (exp claim) of the ID token has not passed. + if ($userinfo['exp'] <= dol_now()) { + setEventMessages($langs->trans('Bad value for returned userinfo[exp]. Token expired.'), null, 'errors'); + $errorincheck++; + } + + // If you specified a hd parameter value in the request, verify that the ID token has a hd claim that matches an accepted G Suite hosted domain. + // TODO } - setEventMessages($langs->trans('NewTokenStored'), null, 'mesgs'); + if (!$errorincheck) { + // Delete the token with fk_soc IS NULL + //$storage->clearToken('Google'); + + // TODO Insert a token for user + //$storage->storeAccessToken + + $db->commit(); + } else { + $db->rollback(); + } + + //setEventMessages($langs->trans('NewTokenStored'), null, 'mesgs'); $backtourl = $_SESSION["backtourlsavedbeforeoauthjump"]; unset($_SESSION["backtourlsavedbeforeoauthjump"]); + if (empty($backtourl)) { + $backtourl = DOL_URL_ROOT; + } + + dol_syslog("Redirect now on backtourl=".$backtourl); + header('Location: '.$backtourl); exit(); } catch (Exception $e) { diff --git a/htdocs/core/tpl/login.tpl.php b/htdocs/core/tpl/login.tpl.php index ca26ac00ee9..7a0070cbb0d 100644 --- a/htdocs/core/tpl/login.tpl.php +++ b/htdocs/core/tpl/login.tpl.php @@ -356,7 +356,7 @@ if (isset($conf->file->main_authentication) && preg_match('/google/', $conf->fil $oauthstateanticsrf = bin2hex(random_bytes(128/8)); $_SESSION['oauthstateanticsrf'] = $shortscope.'-'.$oauthstateanticsrf; - $urltorenew = $urlwithroot.'/core/modules/oauth/google_oauthcallback.php?keyforprovider=Login&shortscope='.$shortscope.'&state=forlogin-'.$shortscope.'-'.$oauthstateanticsrf; + $urltorenew = $urlwithroot.'/core/modules/oauth/google_oauthcallback.php?shortscope='.$shortscope.'&state=forlogin-'.$shortscope.'-'.$oauthstateanticsrf; $url = $urltorenew; diff --git a/htdocs/install/mysql/tables/llx_oauth_state.sql b/htdocs/install/mysql/tables/llx_oauth_state.sql index 77093f6d1b4..02d9d066b7b 100644 --- a/htdocs/install/mysql/tables/llx_oauth_state.sql +++ b/htdocs/install/mysql/tables/llx_oauth_state.sql @@ -1,5 +1,5 @@ -- ============================================================================ --- Copyright (C) 2013 Laurent Destailleur +-- Copyright (C) 2013-23 Laurent Destailleur -- -- This program is free software; you can redistribute it and/or modify -- it under the terms of the GNU General Public License as published by @@ -15,6 +15,9 @@ -- along with this program. If not, see . -- ============================================================================ + +-- This table is deprecated. Still updated but no more read. + CREATE TABLE llx_oauth_state ( rowid integer AUTO_INCREMENT PRIMARY KEY, service varchar(36), diff --git a/htdocs/theme/eldy/info-box.inc.php b/htdocs/theme/eldy/info-box.inc.php index 8882bf8e220..74208efda17 100644 --- a/htdocs/theme/eldy/info-box.inc.php +++ b/htdocs/theme/eldy/info-box.inc.php @@ -96,7 +96,7 @@ if (!defined('ISLOADEDBYSTEELSHEET')) { padding-bottom: 5px; } .info-box-sm .info-box-icon, .info-box-sm .info-box-img { - height: 96px !important; /* must match height of info-box-sm .info-box-content */ + height: 98px !important; /* must match height of info-box-sm .info-box-content */ width: 78px; font-size: 25px; line-height: 92px; @@ -239,7 +239,7 @@ a.info-box-text-a i.fa.fa-exclamation-triangle { } .info-box-sm .info-box-content { margin-left: 80px; - height: 86px; /* 96 - margins of .info-box-sm .info-box-content */ + height: 88px; /* 96 - margins of .info-box-sm .info-box-content */ } .info-box-sm .info-box-module-enabled { /* background: linear-gradient(0.35turn, #fff, #fff, #f6faf8, #e4efe8) */