3 * App Based TOTP Support
6 * @link http://www.open-emr.org
7 * @author Anthony Zullo <anthonykzullo@gmail.com>
8 * @author Rod Roark <rod@sunsetsystems.com>
9 * @author Brady Miller <brady.g.miller@gmail.com>
10 * @copyright Copyright (c) 2019 Anthony Zullo <anthonykzullo@gmail.com>
11 * @copyright Copyright (c) 2018 Rod Roark <rod@sunsetsystems.com>
12 * @copyright Copyright (c) 2018 Brady Miller <brady.g.miller@gmail.com>
13 * @license https://github.com/openemr/openemr/blob/master/LICENSE CNU General Public License 3
17 require_once('../globals.php');
18 require_once("$srcdir/classes/Totp.class.php");
19 require_once("$srcdir/options.inc.php");
21 use OpenEMR\Common\Crypto\CryptoGen
;
22 use OpenEMR\Core\Header
;
23 use OpenEMR\OeUI\OemrUI
;
25 $userid = $_SESSION['authId'];
26 $action = $_REQUEST['action'];
27 $user_name = getUserIDInfo($userid);
28 $user_full_name = $user_name['fname'] . " " . $user_name['lname'];
33 <?php Header
::setupHeader(); ?
>
34 <title
><?php
echo xlt('TOTP Registration'); ?
></title
>
37 function doregister(step
, error
) {
38 var f
= document
.forms
[0];
39 f
.action
.value
= step
;
41 f
.error
.value
= error
;
43 f
.action
.value
= step
;
49 var redirectUrl
= 'mfa_registrations.php';
50 window
.location
.href
= 'mfa_registrations.php';
54 $
('#clearPass').focus();
67 $arrOeUiSettings = array(
68 'heading_title' => xl('Register Time Based One Time Password Key') . " - " . xl('TOTP'),
69 'include_patient_name' => false,
70 'expandable' => false,
71 'expandable_files' => array(),//all file names need suffix _xpd
72 'action' => "",//conceal, reveal, search, reset, link or back
74 'action_href' => "",//only for actions - reset, link or back
75 'show_help_icon' => false,
76 'help_file_name' => ""
78 $oemr_ui = new OemrUI($arrOeUiSettings);
81 <body
class="body_top">
82 <div id
="container_div" class="<?php echo $oemr_ui->oeContainer();?>">
84 // If current step is reg1 or reg2, display the header
85 if ($action == 'reg1' ||
$action == 'reg2') { ?
>
86 <div id
="container_div" class="<?php echo $oemr_ui->oeContainer();?>">
88 <div
class="col-sm-12">
89 <div
class="page-header">
90 <?php
echo $oemr_ui->pageHeading() . "\r\n"; ?
>
95 } ?
> <div
class="row">
96 <div
class="col-sm-12">
97 <form method
='post' class="form-horizontal" action
='mfa_totp.php' onsubmit
='return top.restoreSession()'>
98 <input type
="hidden" name
="csrf_token_form" value
="<?php echo attr(collectCsrfToken()); ?>" />
104 // step 1 is to verify the password
105 if ($action == 'reg1') {
106 $error = (isset($_GET["error"])) ?
$_GET["error"] : false;
110 <legend
><?php
echo xlt('Provide Password for') . " " . $user_full_name; ?
></legend
>
112 <div
class="col-sm-12">
113 <?php
if ($error == "auth") { ?
>
114 <div
class="alert alert-danger alert-msg login-failure m-1">
115 <?php
echo xlt('Invalid password'); ?
>
118 <p
><?php
echo xlt('In order to register your device, please provide your OpenEMR login password'); ?
></p
>
119 <div
class="col-sm-4 col-sm-offset-4">
120 <input type
="password" class="form-control" id
="clearPass" name
="clearPass" placeholder
="<?php echo xla('Password'); ?>:" >
125 <div
class="form-group clearfix">
126 <div
class="col-sm-12 text-left position-override">
127 <button type
="button" class="btn btn-default btn-save" value
="<?php echo xla('Submit'); ?>" onclick
="doregister('reg2')"><?php
echo xlt('Submit'); ?
></button
>
128 <button type
="button" class="btn btn-link btn-cancel btn-separate-left" value
="<?php echo xla('Cancel'); ?>" onclick
="docancel()" ><?php
echo xlt('Cancel'); ?
></button
>
133 // step 2 is to validate password and display qr code
134 } elseif ($action == 'reg2') {
135 if (!verifyCsrfToken($_POST["csrf_token_form"])) {
139 // Redirect back to step 1 if user password is incorrect
140 if (!confirm_user_password($_SESSION['authUser'], $_POST['clearPass'])) {
141 header("Location: mfa_totp.php?action=reg1&error=auth");
145 // Determines whether existing TOTP method exists already
146 $existingSecret = privQuery(
147 "SELECT var1 FROM login_mfa_registrations WHERE " .
148 "`user_id` = ? AND `method` = 'TOTP'",
151 if (empty($existingSecret['var1'])) {
155 $cryptoGen = new CryptoGen();
156 $secret = $cryptoGen->decryptStandard($existingSecret['var1']);
160 // Generate a new QR code or existing QR code
161 $googleAuth = new Totp($secret, $_SESSION['authUser']);
162 $qr = $googleAuth->generateQrCode();
165 // if secret did not exist previously, stores secret in session variable for saving
167 $_SESSION['totpSecret'] = $googleAuth->getSecret();
171 <legend
><?php
echo xlt('Register TOTP Key for') . " " . $user_full_name; ?
></legend
>
173 <div
class="col-sm-12">
174 <?php
if (!$doesExist) { ?
>
176 <?php
echo xlt('Scan the following QR code with your preferred authenticator app to register a new TOTP key.'); ?
>
178 <?php
} else { // $doesExist ?>
180 <?php
echo xlt('Your current TOTP key QR code is displayed below.'); ?
>
184 <img src
="<?php echo attr($qr); ?>" class="img-responsive center-block" style
="height:200px !Important"/>
186 <p
><?php
echo xlt('Example authenticator apps include:'); ?
></p
>
187 <div
class="col-sm-4 col-sm-offset-4">
189 <li
><?php
echo xlt('Google Auth'); ?
>
190 (<a href
="https://itunes.apple.com/us/app/google-authenticator/id388497605?mt=8" target
="_blank" rel
="noopener">
191 <?php
echo xlt('ios'); ?
>
193 <a href
="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&hl=en" target
="_blank" rel
="noopener">
194 <?php
echo xlt('android'); ?
>
196 <li
><?php
echo xlt('Authy'); ?
>
197 (<a href
="https://itunes.apple.com/us/app/authy/id494168017?mt=8" target
="_blank" rel
="noopener"><?php
echo xlt('ios'); ?
></a
>, <a href
="https://play.google.com/store/apps/details?id=com.authy.authy&hl=en" target
="_blank" rel
="noopener"><?php
echo xlt('android'); ?
></a
>)</li
>
203 <div
class="form-group clearfix">
204 <div
class="col-sm-12 text-left position-override">
205 <?php
if (!$doesExist) { ?
>
206 <button type
="button" class="btn btn-default btn-save" value
="<?php echo xla('Register'); ?>" onclick
="doregister('reg3')"><?php
echo xlt('Register'); ?
></button
>
207 <button type
="button" class="btn btn-link btn-cancel btn-separate-left" value
="<?php echo xla('Cancel'); ?>" onclick
="docancel()" ><?php
echo xlt('Cancel'); ?
></button
>
208 <?php
} else { // $doesExist ?>
209 <button type
="button" class="btn btn-link btn-back btn-separate-left" value
="<?php echo xla('Back'); ?>" onclick
="docancel()" ><?php
echo xlt('Back'); ?
></button
>
215 // step 3 is to save the qr code
216 } elseif ($action == 'reg3') {
217 if (!verifyCsrfToken($_POST["csrf_token_form"])) {
223 // Verify that no TOTP method exists already
225 "SELECT COUNT(*) AS count FROM login_mfa_registrations WHERE " .
226 "`user_id` = ? AND `method` = 'TOTP'",
231 if (empty($row['count']) && isset($_SESSION['totpSecret'])) {
232 $cryptoGen = new CryptoGen();
234 "INSERT INTO login_mfa_registrations " .
235 "(`user_id`, `method`, `name`, `var1`, `var2`) VALUES " .
236 "(?, 'TOTP', 'App Based 2FA', ?, '')",
237 array($userid, $cryptoGen->encryptStandard($_SESSION['totpSecret']))
239 unset($_SESSION['totpSecret']);
241 echo " alert(" . xlj('TOTP Method already exists and is enabled. Try again.') . ");\n";
244 echo "window.location.href = 'mfa_registrations.php';\n";
249 <input type
='hidden' name
='action' value
='' />
250 <input type
='hidden' name
='error' value
='' />
254 </div
><!--end of container div
-->
255 <?php
$oemr_ui->oeBelowContainerDiv();?
>