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 EventAuditLogger::instance()->newEvent("logout", $_SESSION['authUser'], $_SESSION['authProvider'], 1, "success");
90 authLoginScreen(true);
92 // Check if session is valid (already logged in user)
93 if (AuthUtils::authCheckSession()) {
95 if (isset($_SESSION['pid']) && empty($GLOBALS['DAEMON_FLAG'])) {
96 require_once("{$GLOBALS['srcdir']}/patient.inc");
99 // Session is not valid (this should only happen if a user's password is changed via another session while the user is logged in)
100 EventAuditLogger::instance()->newEvent("logout", $_SESSION['authUser'], $_SESSION['authProvider'], 0, "authCheckSession() check failed, so force logout");
102 authLoginScreen(true);
106 // Ensure user has not timed out, if applicable
107 // Have a mechanism to skip the timeout and timeout reset mechanisms if a skip_timeout_reset parameter exists. This
108 // can be used by scripts that continually request information from the server; for example the Messages
109 // and Reminders automated intermittent requests.
110 // Also skipping this all on login since entry in session_tracker is not ready yet
111 if (empty($skipSessionExpirationCheck)) {
112 if (!SessionTracker::isSessionExpired()) {
113 if (empty($_REQUEST['skip_timeout_reset']) && empty($GLOBALS['DAEMON_FLAG'])) {
114 SessionTracker::updateSessionExpiration();
117 // User has timed out.
118 EventAuditLogger::instance()->newEvent("logout", $_SESSION['authUser'], $_SESSION['authProvider'], 0, "timeout, so force logout");
120 authLoginScreen(true);
124 require_once(dirname(__FILE__) . "/../src/Common/Session/SessionUtil.php");
125 function authCloseSession()
127 // Before destroying the session, save its site_id so that the next
128 // login will default to that same site.
129 global $incoming_site_id;
130 $incoming_site_id = $_SESSION['site_id'];
131 OpenEMR\Common\Session\SessionUtil::coreSessionDestroy();
134 function authLoginScreen($timed_out = false)
136 // See comment in authCloseSession().
137 global $incoming_site_id;
140 // Find the top level window for this instance of OpenEMR, set a flag indicating
141 // session timeout has occurred, and reload the login page into it. This is so
142 // that beforeunload event handlers will not obstruct the process in this case.
144 while (w.opener) { // in case we are in a dialog window
149 <?php if ($timed_out) { ?>
150 w.top.timed_out = true;
152 w.top.location.href = '<?php echo "{$GLOBALS['login_screen']}?error=1&site=$incoming_site_id"; ?>';