dolibarr 20.0.0
functions_openid_connect.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2022 Jeritiana Ravelojaona <jeritiana.rav@smartone.ai>
3 * Copyright (C) 2023-2024 Solution Libre SAS <contact@solution-libre.fr>
4 * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
28include_once DOL_DOCUMENT_ROOT.'/core/lib/geturl.lib.php';
29
39function check_user_password_openid_connect($usertotest, $passwordtotest, $entitytotest)
40{
41 global $db, $conf, $langs;
42
43 // Force master entity in transversal mode
44 $entity = $entitytotest;
45 if (isModEnabled('multicompany') && getDolGlobalString('MULTICOMPANY_TRANSVERSE_MODE')) {
46 $entity = 1;
47 }
48
49 $login = '';
50
51 dol_syslog("functions_openid_connect::check_user_password_openid_connect usertotest=".$usertotest." passwordtotest=".preg_replace('/./', '*', $passwordtotest)." entitytotest=".$entitytotest);
52
53 // Step 1 is done by user: request an authorization code
54
55 if (GETPOSTISSET('username')) {
56 // OIDC does not require credentials here: pass on to next auth handler
57 $_SESSION["dol_loginmesg"] = "Not an OpenID Connect flow";
58 dol_syslog("functions_openid_connect::check_user_password_openid_connect not an OIDC flow");
59 } elseif (!GETPOSTISSET('state')) {
60 // No state received
61 $_SESSION["dol_loginmesg"] = "Error in OAuth 2.0 flow (no state received)";
62 } elseif (GETPOSTISSET('code')) {
63 $auth_code = GETPOST('code', 'aZ09');
64 $state = GETPOST('state', 'aZ09');
65 dol_syslog('functions_openid_connect::check_user_password_openid_connect code='.$auth_code.' state='.$state);
66
67 if ($state === hash('sha256', session_id())) {
68 // Step 2: turn the authorization code into an access token, using client_secret
69 $auth_param = [
70 'grant_type' => 'authorization_code',
71 'client_id' => getDolGlobalString('MAIN_AUTHENTICATION_OIDC_CLIENT_ID'),
72 'client_secret' => getDolGlobalString('MAIN_AUTHENTICATION_OIDC_CLIENT_SECRET'),
73 'code' => $auth_code,
74 'redirect_uri' => getDolGlobalString('MAIN_AUTHENTICATION_OIDC_REDIRECT_URL')
75 ];
76
77 $token_response = getURLContent($conf->global->MAIN_AUTHENTICATION_OIDC_TOKEN_URL, 'POST', http_build_query($auth_param));
78 $token_content = json_decode($token_response['content']);
79 dol_syslog("functions_openid_connect::check_user_password_openid_connect /token=".print_r($token_response, true), LOG_DEBUG);
80
81 if (property_exists($token_content, 'access_token')) {
82 // Step 3: retrieve user info using token
83 $userinfo_headers = array('Authorization: Bearer '.$token_content->access_token);
84 $userinfo_response = getURLContent($conf->global->MAIN_AUTHENTICATION_OIDC_USERINFO_URL, 'GET', '', 1, $userinfo_headers);
85 $userinfo_content = json_decode($userinfo_response['content']);
86
87 dol_syslog("functions_openid_connect::check_user_password_openid_connect /userinfo=".print_r($userinfo_response, true), LOG_DEBUG);
88
89 // Get the user attribute (claim) matching the Dolibarr login
90 $login_claim = 'email'; // default
91 if (getDolGlobalString('MAIN_AUTHENTICATION_OIDC_LOGIN_CLAIM')) {
92 $login_claim = getDolGlobalString('MAIN_AUTHENTICATION_OIDC_LOGIN_CLAIM');
93 }
94
95 if (property_exists($userinfo_content, $login_claim)) {
96 // Success: retrieve claim to return to Dolibarr as login
97 $sql = 'SELECT login, entity, datestartvalidity, dateendvalidity';
98 $sql .= ' FROM '.MAIN_DB_PREFIX.'user';
99 $sql .= " WHERE login = '".$db->escape($userinfo_content->$login_claim)."'";
100 $sql .= ' AND entity IN (0,'.(array_key_exists('dol_entity', $_SESSION) ? ((int) $_SESSION["dol_entity"]) : 1).')';
101
102 dol_syslog("functions_openid::check_user_password_openid", LOG_DEBUG);
103
104 $resql = $db->query($sql);
105 if ($resql) {
106 $obj = $db->fetch_object($resql);
107 if ($obj) {
108 // Note: Test on date validity is done later natively with isNotIntoValidityDateRange() by core after calling checkLoginPassEntity() that call this method
109 $login = $obj->login;
110 }
111 }
112 } elseif ($userinfo_content->error) {
113 // Got user info response but content is an error
114 $_SESSION["dol_loginmesg"] = "Error in OAuth 2.0 flow (".$userinfo_content->error_description.")";
115 } elseif ($userinfo_response['http_code'] == 200) {
116 // Claim does not exist
117 $_SESSION["dol_loginmesg"] = "OpenID Connect claim not found: ".$login_claim;
118 } elseif ($userinfo_response['curl_error_no']) {
119 // User info request error
120 $_SESSION["dol_loginmesg"] = "Network error: ".$userinfo_response['curl_error_msg']." (".$userinfo_response['curl_error_no'].")";
121 } else {
122 // Other user info request error
123 $_SESSION["dol_loginmesg"] = "Userinfo request error (".$userinfo_response['http_code'].")";
124 }
125 } elseif ($token_content->error) {
126 // Got token response but content is an error
127 $_SESSION["dol_loginmesg"] = "Error in OAuth 2.0 flow (".$token_content->error_description.")";
128 } elseif ($token_response['curl_error_no']) {
129 // Token request error
130 $_SESSION["dol_loginmesg"] = "Network error: ".$token_response['curl_error_msg']." (".$token_response['curl_error_no'].")";
131 } else {
132 // Other token request error
133 $_SESSION["dol_loginmesg"] = "Token request error (".$token_response['http_code'].")";
134 }
135 } else {
136 // No code received
137 $_SESSION["dol_loginmesg"] = "Error in OAuth 2.0 flow (state does not match)";
138 }
139 } else {
140 // No code received
141 $_SESSION["dol_loginmesg"] = "Error in OAuth 2.0 flow (no code received)";
142 }
143
144 dol_syslog("functions_openid_connect::check_user_password_openid_connect END");
145
146 return !empty($login) ? $login : false;
147}
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
getDolGlobalString($key, $default='')
Return dolibarr global constant string value.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
check_user_password_openid_connect($usertotest, $passwordtotest, $entitytotest)
Check validity of user/password/entity If test is ko, reason must be filled into $_SESSION["dol_login...
getURLContent($url, $postorget='GET', $param='', $followlocation=1, $addheaders=array(), $allowedschemes=array('http', 'https'), $localurl=0, $ssl_verifypeer=-1)
Function to get a content from an URL (use proxy if proxy defined).