4 * Authorization functions.
7 * @link https://www.open-emr.org
8 * @author Rod Roark <rod@sunsetsystems.com>
9 * @author Brady Miller <brady.g.miller@gmail.com>
10 * @author Kevin Yeh <kevin.y@integralemr.com>
11 * @author ViCarePlus <visolve_emr@visolve.com>
12 * @author Ken Chapple <ken@mi-squared.com>
14 * @copyright Copyright (c) 2019 Brady Miller <brady.g.miller@gmail.com>
15 * @copyright Copyright (c) 2021 Ken Chapple <ken@mi-squared.com>
16 * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
19 use OpenEMR\Common\Auth\AuthUtils
;
20 use OpenEMR\Common\Logging\EventAuditLogger
;
21 use OpenEMR\Common\Session\SessionTracker
;
22 use OpenEMR\Services\UserService
;
24 $incoming_site_id = '';
25 // This is the conditional that ensures that the submission has the required parameters to attempt a login
28 && ($_GET['auth'] == "login")
29 && isset($_POST['new_login_session_management'])
31 // Either normal login or google sign-in
32 (isset($_POST['authUser']) && isset($_POST['clearPass']))
33 ||
(!empty($GLOBALS['google_signin_enabled']) && !empty($GLOBALS['google_signin_client_id']) && !empty($_POST['used_google_signin']) && !empty($_POST['google_signin_token']))
39 if (!empty($_POST['languageChoice'])) {
40 $_SESSION['language_choice'] = $_POST['languageChoice'];
42 $_SESSION['language_choice'] = 1;
45 // set language direction according to language choice. Later in globals.php we'll override main theme name if needed.
46 $_SESSION['language_direction'] = getLanguageDir($_SESSION['language_choice']);
48 // Note we are purposefully keeping $_POST['clearPass'], which is needed for MFA to work. It is cleared from memory after a
49 // unsuccessful or successful login
50 $passTemp = $_POST['clearPass'];
52 $login_success = false;
54 !empty($GLOBALS['google_signin_enabled']) &&
55 !empty($GLOBALS['google_signin_client_id']) &&
56 !empty($_POST['used_google_signin']) &&
57 !empty($_POST['google_signin_token'])
60 $login_success = AuthUtils
::verifyGoogleSignIn($_POST['google_signin_token']);
63 $login_success = (new AuthUtils('login'))->confirmPassword($_POST['authUser'], $passTemp);
66 if ($login_success !== true) {
67 // login attempt failed
68 $_SESSION['loginfailure'] = 1;
69 if (function_exists('sodium_memzero')) {
70 sodium_memzero($_POST["clearPass"]);
72 $_POST["clearPass"] = '';
77 // login attempt success
78 $_SESSION['loginfailure'] = null;
79 unset($_SESSION['loginfailure']);
81 // skip the session expiration check below since the entry in session_tracker is not ready yet
82 $skipSessionExpirationCheck = true;
83 } elseif ((isset($_GET['auth'])) && ($_GET['auth'] == "logout")) {
85 // If session has timed out / been destroyed, logout record for null user/provider will be invalid.
86 if (!empty($_SESSION['authUser']) && !empty($_SESSION['authProvider'])) {
87 if ((isset($_GET['timeout'])) && ($_GET['timeout'] == "1")) {
88 EventAuditLogger
::instance()->newEvent("logout", $_SESSION['authUser'], $_SESSION['authProvider'], 0, "timeout, so force logout");
90 EventAuditLogger
::instance()->newEvent("logout", $_SESSION['authUser'], $_SESSION['authProvider'], 1, "success");
94 authLoginScreen(true);
96 // Check if session is valid (already logged in user)
97 if (!AuthUtils
::authCheckSession()) {
98 // Session is not valid (this should only happen if a user's password is changed via another session while the user is logged in)
99 EventAuditLogger
::instance()->newEvent("logout", $_SESSION['authUser'] ??
'', $_SESSION['authProvider'] ??
'', 0, "authCheckSession() check failed, so force logout");
101 authLoginScreen(true);
105 // Ensure user has not timed out, if applicable
106 // Have a mechanism to skip the timeout and timeout reset mechanisms if a skip_timeout_reset parameter exists. This
107 // can be used by scripts that continually request information from the server; for example the Messages
108 // and Reminders automated intermittent requests.
109 // Also skipping this all on login since entry in session_tracker is not ready yet
110 if (empty($skipSessionExpirationCheck) && empty($_REQUEST['skip_timeout_reset'])) {
111 if (!SessionTracker
::isSessionExpired()) {
112 SessionTracker
::updateSessionExpiration();
114 // User has timed out.
115 EventAuditLogger
::instance()->newEvent("logout", $_SESSION['authUser'], $_SESSION['authProvider'], 0, "timeout, so force logout");
117 authLoginScreen(true);
121 require_once(dirname(__FILE__
) . "/../src/Common/Session/SessionUtil.php");
122 function authCloseSession()
124 // Before destroying the session, save its site_id so that the next
125 // login will default to that same site.
126 global $incoming_site_id;
127 $incoming_site_id = $_SESSION['site_id'];
128 OpenEMR\Common\Session\SessionUtil
::coreSessionDestroy();
131 function authLoginScreen($timed_out = false)
133 // See comment in authCloseSession().
134 global $incoming_site_id;
137 // Find the top level window for this instance of OpenEMR, set a flag indicating
138 // session timeout has occurred, and reload the login page into it. This is so
139 // that beforeunload event handlers will not obstruct the process in this case.
141 while (w
.opener
) { // in case we are in a dialog window
146 <?php
if ($timed_out) { ?
>
147 w
.top
.timed_out
= true;
149 w
.top
.location
.href
= '<?php echo "{$GLOBALS['login_screen
']}?error=1&site=$incoming_site_id"; ?>';