PluginProbe ʕ •ᴥ•ʔ
WP 2FA – Two-factor authentication for WordPress / 1.2.0
WP 2FA – Two-factor authentication for WordPress v1.2.0
4.0.0 1.7.1 2.0.0 2.0.1 2.1.0 2.2.0 2.2.1 2.3.0 2.4.0 2.4.1 2.4.2 2.5.0 2.6.0 2.6.1 2.6.2 2.6.3 2.6.4 2.7.0 2.8.0 2.9.0 2.9.1 2.9.2 2.9.3 3.0.0 3.0.1 3.1.0 3.1.1 3.1.1.2 trunk 1.2.0 1.3.0 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 1.5.2 1.6.0 1.6.1 1.6.2 1.7.0
wp-2fa / includes / classes / Admin / SetupWizard.php
wp-2fa / includes / classes / Admin Last commit date
SettingsPage.php 6 years ago SetupWizard.php 6 years ago UserNotices.php 6 years ago UserProfile.php 6 years ago UserRegistered.php 6 years ago
SetupWizard.php
1524 lines
1 <?php // phpcs:ignore
2
3 namespace WP2FA\Admin;
4
5 use \WP2FA\Authenticator\Authentication as Authentication;
6 use \WP2FA\WP2FA as WP2FA;
7 use \WP2FA\Core as Core;
8 use \WP2FA\Admin\SettingsPage as SettingsPage;
9
10 /**
11 * Our class for creating a step by step wizard for easy configuration.
12 */
13 class SetupWizard {
14
15 /**
16 * Wizard Steps
17 *
18 * @var array
19 */
20 private $wizard_steps;
21
22 /**
23 * Current Step
24 *
25 * @var string
26 */
27 private $current_step;
28
29 /**
30 * Notices Meta key
31 *
32 * @var array
33 */
34 const NOTICES_META_KEY = 'wp_2fa_totp_notices';
35
36 /**
37 * Method: Constructor.
38 */
39 public function __construct() { }
40
41 /**
42 * Add setup admin page. This is empty on purpose.
43 */
44 public function admin_menus() {
45 add_dashboard_page( '', '', 'read', 'wp-2fa-setup', '' );
46 }
47
48 public function network_admin_menus() {
49 add_dashboard_page( 'index.php', '', 'read', 'wp-2fa-setup', '' );
50 }
51
52 /**
53 * Setup Page Start.
54 */
55 public function setup_page() {
56 // Get page argument from $_GET array.
57 $page = filter_input( INPUT_GET, 'page', FILTER_SANITIZE_STRING );
58 if ( empty( $page ) || 'wp-2fa-setup' !== $page ) {
59 return;
60 }
61
62 // Clear out any old notices.
63 $user = wp_get_current_user();
64 delete_user_meta( $user->ID, self::NOTICES_META_KEY );
65
66 // First lets check if any options have been saved.
67 $settings_saved = true;
68 $settings = WP2FA::get_wp2fa_setting();
69 if ( empty( $settings ) || ! isset( $settings ) ) {
70 $settings_saved = false;
71 }
72
73 /**
74 * Wizard Steps.
75 */
76 $get_array = filter_input_array( INPUT_GET );
77 if ( isset( $get_array['wizard_type'] ) ) {
78 $wizard_type = sanitize_text_field( $get_array['wizard_type'] );
79 } else {
80 $wizard_type = '';
81 }
82 if ( ! empty( $wizard_type ) && 'user_2fa_config' === $get_array['wizard_type'] && current_user_can( 'read' ) ) {
83
84 $wizard_steps = array(
85 'user_choose_2fa_method' => array(
86 'name' => esc_html__( 'Choose Method', 'wp-2fa' ),
87 'content' => array( $this, 'wp_2fa_step_user_choose_method' ),
88 'save' => array( $this, 'wp_2fa_step_user_choose_method_save' ),
89 'wizard_type' => 'welcome_wizard',
90 ),
91 'setup_method' => array(
92 'name' => esc_html__( 'Setup the 2FA method', 'wp-2fa' ),
93 'content' => array( $this, 'wp_2fa_step_setup_authenticator' ),
94 'save' => array( $this, 'wp_2fa_step_setup_authenticator_save' ),
95 'wizard_type' => 'welcome_wizard',
96 ),
97 'backup_codes' => array(
98 'name' => esc_html__( 'Backup Codes', 'wp-2fa' ),
99 'content' => array( $this, 'wp_2fa_step_backup_codes' ),
100 'save' => array( $this, 'wp_2fa_step_backup_codes_save' ),
101 'wizard_type' => array( 'welcome_wizard', 'backup_codes_wizard' ),
102 ),
103 );
104
105 } elseif ( ! empty( $wizard_type ) && 'backup_codes_config' === $wizard_type && current_user_can( 'read' ) ) {
106
107 $wizard_steps = array(
108 'backup_codes' => array(
109 'name' => esc_html__( 'Backup Codes', 'wp-2fa' ),
110 'content' => array( $this, 'wp_2fa_step_backup_codes' ),
111 'save' => array( $this, 'wp_2fa_step_backup_codes_save' ),
112 'wizard_type' => array( 'welcome_wizard', 'backup_codes_wizard' ),
113 ),
114 );
115
116 } elseif ( ! empty( $wizard_type ) && 'user_reconfigure_config' === $wizard_type ) {
117
118 $wizard_steps = array(
119 'reconfigure_method' => array(
120 'name' => esc_html__( 'Setup the Authenticator 2FA', 'wp-2fa' ),
121 'content' => array( $this, 'wp_2fa_step_reconfigure_authenticator' ),
122 'save' => array( $this, 'wp_2fa_step_reconfigure_authenticator_save' ),
123 'wizard_type' => 'reconfigure_wizard',
124 ),
125 );
126
127 } elseif ( current_user_can( 'manage_options' ) && ! $settings_saved ) {
128 $wizard_steps = array(
129 'welcome' => array(
130 'name' => esc_html__( 'Welcome', 'wp-2fa' ),
131 'content' => array( $this, 'wp_2fa_step_welcome' ),
132 'wizard_type' => 'welcome_wizard',
133 ),
134 'choose_2fa_method' => array(
135 'name' => esc_html__( 'Choose Method', 'wp-2fa' ),
136 'content' => array( $this, 'wp_2fa_step_choose_method' ),
137 'save' => array( $this, 'wp_2fa_step_choose_method_save' ),
138 'wizard_type' => 'welcome_wizard',
139 ),
140 'setup_method' => array(
141 'name' => esc_html__( 'Setup the 2FA method', 'wp-2fa' ),
142 'content' => array( $this, 'wp_2fa_step_setup_authenticator' ),
143 'save' => array( $this, 'wp_2fa_step_setup_authenticator_save' ),
144 'wizard_type' => 'welcome_wizard',
145 ),
146 'finish' => array(
147 'name' => esc_html__( 'Setup Finish', 'wp-2fa' ),
148 'content' => array( $this, 'wp_2fa_step_finish' ),
149 'save' => array( $this, 'wp_2fa_step_finish_save' ),
150 'wizard_type' => 'welcome_wizard',
151 ),
152 'settings_configuation' => array(
153 'name' => esc_html__( 'Select 2FA Methods', 'wp-2fa' ),
154 'content' => array( $this, 'wp_2fa_step_global_2fa_methods' ),
155 'save' => array( $this, 'wp_2fa_step_global_2fa_methods_save' ),
156 'wizard_type' => 'welcome_wizard',
157 ),
158 'backup_codes' => array(
159 'name' => esc_html__( 'Backup Codes', 'wp-2fa' ),
160 'content' => array( $this, 'wp_2fa_step_backup_codes' ),
161 'save' => array( $this, 'wp_2fa_step_backup_codes_save' ),
162 'wizard_type' => array( 'welcome_wizard', 'backup_codes_wizard' ),
163 ),
164 'reconfigure_method' => array(
165 'name' => esc_html__( 'Setup the Authenticator 2FA', 'wp-2fa' ),
166 'content' => array( $this, 'wp_2fa_step_reconfigure_authenticator' ),
167 'save' => array( $this, 'wp_2fa_step_reconfigure_authenticator_save' ),
168 'wizard_type' => 'reconfigure_wizard',
169 ),
170 );
171
172 } elseif ( current_user_can( 'read' ) ) {
173
174 $wizard_steps = array(
175 'choose_2fa_method' => array(
176 'name' => esc_html__( 'Choose Method', 'wp-2fa' ),
177 'content' => array( $this, 'wp_2fa_step_choose_method' ),
178 'save' => array( $this, 'wp_2fa_step_choose_method_save' ),
179 'wizard_type' => 'welcome_wizard',
180 ),
181 'setup_method' => array(
182 'name' => esc_html__( 'Setup the 2FA method', 'wp-2fa' ),
183 'content' => array( $this, 'wp_2fa_step_setup_authenticator' ),
184 'save' => array( $this, 'wp_2fa_step_setup_authenticator_save' ),
185 'wizard_type' => 'welcome_wizard',
186 ),
187 'backup_codes' => array(
188 'name' => esc_html__( 'Backup Codes', 'wp-2fa' ),
189 'content' => array( $this, 'wp_2fa_step_backup_codes' ),
190 'save' => array( $this, 'wp_2fa_step_backup_codes_save' ),
191 'wizard_type' => array( 'welcome_wizard', 'backup_codes_wizard' ),
192 ),
193 'reconfigure_method' => array(
194 'name' => esc_html__( 'Setup the Authenticator 2FA', 'wp-2fa' ),
195 'content' => array( $this, 'wp_2fa_step_reconfigure_authenticator' ),
196 'save' => array( $this, 'wp_2fa_step_reconfigure_authenticator_save' ),
197 'wizard_type' => 'reconfigure_wizard',
198 ),
199 );
200
201 }
202
203 /**
204 * Filter: `Wizard Default Steps`
205 *
206 * WSAL filter to filter wizard steps before they are displayed.
207 *
208 * @param array $wizard_steps – Wizard Steps.
209 */
210 $this->wizard_steps = apply_filters( 'wp_2fa_wizard_default_steps', $wizard_steps );
211
212 // Set current step.
213 $current_step = filter_input( INPUT_GET, 'current-step', FILTER_SANITIZE_STRING );
214 $this->current_step = ! empty( $current_step ) ? $current_step : current( array_keys( $this->wizard_steps ) );
215
216 /**
217 * Enqueue Scripts.
218 */
219 wp_enqueue_style(
220 'wp_2fa_setup_wizard',
221 Core\style_url( 'setup-wizard', 'admin' ),
222 array(),
223 WP_2FA_VERSION
224 );
225
226 wp_enqueue_style(
227 'wp_2fa_admin',
228 Core\style_url( 'admin-style', 'admin' ),
229 array(),
230 WP_2FA_VERSION
231 );
232
233 wp_enqueue_script(
234 'wp_2fa_admin',
235 Core\script_url( 'admin', 'admin' ),
236 array( 'jquery-ui-widget', 'jquery-ui-core', 'jquery-ui-autocomplete' ),
237 WP_2FA_VERSION,
238 true
239 );
240
241 wp_enqueue_script(
242 'wp_2fa_micromodal',
243 Core\script_url( 'micro-modal', 'admin' ),
244 WP_2FA_VERSION,
245 true
246 );
247
248 // Data array.
249 $data_array = array(
250 'ajaxURL' => admin_url( 'admin-ajax.php' ),
251 'roles' => WP2FA::wp_2fa_get_roles(),
252 'nonce' => wp_create_nonce( 'wp-2fa-settings-nonce' ),
253 );
254 wp_localize_script( 'wp_2fa_admin', 'wp2faData', $data_array );
255
256 // Data array.
257 $data_array = array(
258 'ajaxURL' => admin_url( 'admin-ajax.php' ),
259 'nonce' => wp_create_nonce( 'wp2fa-verify-wizard-page' ),
260 'codesPreamble' => esc_html__( 'These are the 2FA backup codes for the user', 'wp-2fa' ),
261 'readyText' => esc_html__( 'I\'m ready', 'wp-2fa' ),
262 'codeReSentText' => esc_html__( 'New code sent', 'wp-2fa' ),
263 );
264 wp_localize_script( 'wp_2fa_admin', 'wp2faWizardData', $data_array );
265
266 /**
267 * Save Wizard Settings.
268 */
269 $save_step = filter_input( INPUT_POST, 'save_step', FILTER_SANITIZE_STRING );
270 if ( ! empty( $save_step ) && ! empty( $this->wizard_steps[ $this->current_step ]['save'] ) ) {
271 call_user_func( $this->wizard_steps[ $this->current_step ]['save'] );
272 }
273
274 $this->setup_page_header();
275 $this->setup_page_steps();
276 $this->setup_page_content();
277 $this->setup_page_footer();
278
279 exit;
280 }
281
282 /**
283 * Setup Page Header.
284 */
285 private function setup_page_header() {
286 ?>
287 <!DOCTYPE html>
288 <html <?php language_attributes(); ?>>
289 <head>
290 <meta name="viewport" content="width=device-width" />
291 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
292 <title><?php esc_html_e( 'WP 2FA &rsaquo; Setup Wizard', 'wp-2fa' ); ?></title>
293 <?php wp_print_scripts( 'jquery' ); ?>
294 <?php wp_print_scripts( 'jquery-ui-core' ); ?>
295 <?php wp_print_scripts( 'wp_2fa_setup_wizard' ); ?>
296 <?php wp_print_scripts( 'wp_2fa_micromodal' ); ?>
297 <?php wp_print_scripts( 'wp_2fa_admin' ); ?>
298 <?php wp_print_styles( 'common' ); ?>
299 <?php wp_print_styles( 'forms' ); ?>
300 <?php wp_print_styles( 'buttons' ); ?>
301 <?php wp_print_styles( 'wp-jquery-ui-dialog' ); ?>
302 <?php wp_print_styles( 'wp_2fa_admin' ); ?>
303 <?php do_action( 'admin_print_styles' ); ?>
304 <?php do_action( 'admin_head' ); ?>
305 </head>
306 <body class="wp2fa-setup wp-core-ui">
307 <div class="setup-wizard-wrapper">
308 <h1 id="wp2fa-logo"><a href="https://wpsecurityauditlog.com" target="_blank"><img src="<?php echo esc_url( WP_2FA_URL . '/dist/images/wizard-logo.png' );?>"></a></h1>
309 <?php
310 }
311
312 /**
313 * Setup Page Footer.
314 */
315 private function setup_page_footer() {
316 ?>
317 <div class="wp2fa-setup-footer">
318 <?php if ( 'welcome' !== $this->current_step && 'finish' !== $this->current_step ) : // Don't show the link on the first & last step. ?>
319 <a class="close-wizard-link" href="<?php echo esc_url( admin_url() ); ?>"><?php esc_html_e( 'Close Wizard', 'wp-2fa' ); ?></a>
320 <?php endif; ?>
321 </div>
322 </div>
323 <div class="wp2fa-modal micromodal-slide" id="confirm-change-2fa" aria-hidden="true">
324 <div class="modal__overlay" tabindex="-1" data-micromodal-close>
325 <div class="modal__container" role="dialog" aria-modal="true" aria-labelledby="modal-1-title">
326 <header class="modal__header">
327 <h2 class="modal__title" id="modal-1-title">
328 <?php esc_html_e( 'Change 2FA Method?', 'wp-2fa' ); ?>
329 </h2>
330 <button class="modal__close" aria-label="Close modal" data-micromodal-close></button>
331 </header>
332 <main class="modal__content" id="modal-1-content">
333 <p>
334 <?php esc_html_e( 'By switching to a new method the previously used method will be disabled.', 'wp-2fa' ); ?>
335 </p>
336 </main>
337 <footer class="modal__footer">
338 <a href="#" class="modal__btn modal__btn-primary" data-trigger-submit-form><?php esc_html_e( 'OK', 'wp-2fa' ); ?></a>
339 <button class="modal__btn" data-micromodal-close aria-label="Close this dialog window"><?php esc_html_e( 'No thanks', 'wp-2fa' ); ?></button>
340 </footer>
341 </div>
342 </div>
343 </div>
344 </body>
345 </html>
346 <?php
347 }
348
349 /**
350 * Setup Page Steps.
351 */
352 private function setup_page_steps() {
353 ?>
354 <ul class="steps">
355 <?php
356 foreach ( $this->wizard_steps as $key => $step ) :
357 if ( 'welcome_wizard' === $step['wizard_type'] || is_array( $step['wizard_type'] ) && in_array( 'welcome_wizard', $step['wizard_type'], true ) ) :
358 if ( $key === $this->current_step ) :
359 ?>
360 <li class="is-active"><?php echo esc_html( $step['name'] ); ?></li>
361 <?php
362 else :
363 ?>
364 <li><?php echo esc_html( $step['name'] ); ?></li>
365 <?php
366 endif;
367 endif;
368 endforeach;
369 ?>
370 </ul>
371 <?php
372 }
373
374 /**
375 * Get Next Step URL.
376 *
377 * @return string
378 */
379 private function get_next_step() {
380 // Get current step.
381 $current_step = $this->current_step;
382
383 // Array of step keys.
384 $keys = array_keys( $this->wizard_steps );
385 if ( end( $keys ) === $current_step ) { // If last step is active then return WP Admin URL.
386 return admin_url();
387 }
388
389 // Search for step index in step keys.
390 $step_index = array_search( $current_step, $keys, true );
391 if ( false === $step_index ) { // If index is not found then return empty string.
392 return '';
393 }
394
395 // Return next step.
396 return add_query_arg( 'current-step', $keys[ $step_index + 1 ] );
397 }
398
399 /**
400 * Setup Page Content.
401 */
402 private function setup_page_content() {
403 ?>
404 <div class="wp2fa-setup-content">
405 <?php
406 if ( ! empty( $this->wizard_steps[ $this->current_step ]['content'] ) ) {
407 call_user_func( $this->wizard_steps[ $this->current_step ]['content'] );
408 }
409 ?>
410 </div>
411 <?php
412 }
413
414 /**
415 * Step View: `Welcome`
416 */
417 private function wp_2fa_step_welcome() {
418 // Grab current user.
419 $user = wp_get_current_user();
420
421 ?>
422 <h3><?php esc_html_e( 'Let us help you get started', 'wp-2fa' ); ?></h3>
423 <p><?php esc_html_e( 'Thank you for installing WP 2FA. This wizard will assist you setup two-factor authentication (2FA) for your WordPress user and configure the plugin’s generic settings.', 'wp-2fa' ); ?></p>
424
425 <div class="wp2fa-setup-actions">
426 <a class="button button-primary"
427 href="<?php echo esc_url( $this->get_next_step() ); ?>">
428 <?php esc_html_e( 'Let’s get started!', 'wp-2fa' ); ?>
429 </a>
430 <a class="button button-secondary"
431 href="<?php echo esc_url( admin_url() ); ?>">
432 <?php esc_html_e( 'Skip Wizard - I know how to do this', 'wp-2fa' ); ?>
433 </a>
434 </div>
435 <?php
436 }
437
438 /**
439 * Step View: `Choose Methods`
440 */
441 private function wp_2fa_step_choose_method() {
442 ?>
443 <form method="post" class="wp2fa-setup-form" autocomplete="off">
444 <?php wp_nonce_field( 'wp2fa-step-choose-method' ); ?>
445
446 <?php
447 // Change text if this is the user configuring there 2fa settings
448 // Filter $_GET array for security.
449 $get_array = filter_input_array( INPUT_GET );
450 // If this is the user setting things up, lets show nice message.
451 if ( isset( $get_array['first_time_setup'] ) ) {
452 if ( ! empty( WP2FA::get_wp2fa_setting( 'enable_totp' ) ) && ! empty( WP2FA::get_wp2fa_setting( 'enable_email' ) ) ) {
453 $intro_text = esc_html__( 'Let’s get started; Choose the two-factor authentication method', 'wp-2fa' );
454 $sub_text = esc_html__( 'There are two methods available from which you can choose for 2FA:', 'wp-2fa' );
455 } else {
456 $intro_text = esc_html__( 'Let’s get started', 'wp-2fa' );
457 $sub_text = esc_html__( 'Only the below 2FA method is allowed on this website:', 'wp-2fa' );
458 }
459 ?>
460 <h3><?php echo sanitize_text_field( $intro_text ); ?></h3>
461 <p><?php echo sanitize_text_field( $sub_text ); ?></p>
462 <?php } else { ?>
463 <h3><?php esc_html_e( 'Choose the 2FA authentication method', 'wp-2fa' ); ?></h3>
464 <p><?php esc_html_e( 'There are two methods available from which you can choose for 2FA:', 'wp-2fa' ); ?></p>
465 <?php } ?>
466
467 <fieldset>
468 <?php if ( ! empty( WP2FA::get_wp2fa_setting( 'enable_totp' ) ) ) { ?>
469 <div class="option-pill">
470 <label for="basic">
471 <input id="basic" name="wp_2fa_enabled_methods" type="radio" value="totp" checked>
472 <?php esc_html_e( 'One-time code generated with the Google authenticator app (most reliable and secure)', 'wp-2fa' ); ?>
473 </label>
474 <?php
475 printf( '<p class="description">%1$s <a href="https://www.wpwhitesecurity.com/google-authenticator-app-wordpress-2fa/" target="_blank">%2$s</a></p>', esc_html__( 'Note: this method requires you to install the Google Authenticator app on a smart device. The app is available on Google Play and the Apple Appstore. For more information on this method read our', 'wp-2fa' ), esc_html__( 'our guide for Google Authenticator.', 'wp-2fa' ) );
476 ?>
477 </div>
478 <?php } ?>
479 <?php if ( ! empty( WP2FA::get_wp2fa_setting( 'enable_email' ) ) ) { ?>
480 <div class="option-pill">
481 <label for="geek">
482 <input id="geek" name="wp_2fa_enabled_methods" type="radio" value="email">
483 <?php esc_html_e( 'One-time code sent to you over email', 'wp-2fa' ); ?>
484 </label>
485 </div>
486 <?php } ?>
487 </fieldset>
488 <div class="wp2fa-setup-actions">
489 <button class="button button-primary" type="submit" name="save_step" value="<?php esc_attr_e( 'Next', 'wp-2fa' ); ?>"><?php esc_html_e( 'Next', 'wp-2fa' ); ?></button>
490 </div>
491 </form>
492 <?php
493 }
494
495 /**
496 * Step Save: `Choose Method`
497 */
498 private function wp_2fa_step_choose_method_save() {
499 // Check nonce.
500 check_admin_referer( 'wp2fa-step-choose-method' );
501
502 // Grab current user.
503 $user = wp_get_current_user();
504
505 // Add our enabled methods to the user metadata.
506 if ( isset( $_POST['wp_2fa_enabled_methods'] ) ) {
507 $next = add_query_arg( 'enabled_methods', sanitize_text_field( wp_unslash( $_POST['wp_2fa_enabled_methods'] ) ), $this->get_next_step() );
508 wp_safe_redirect( esc_url_raw( $next ) );
509 }
510
511 exit();
512 }
513
514 /**
515 * Choose user 2FA method
516 */
517 private function wp_2fa_step_user_choose_method() {
518 $user = wp_get_current_user();
519 $enabled_method = get_user_meta( $user->ID, 'wp_2fa_enabled_methods', true );
520
521 ?>
522 <form id="wp2fa-setup-form" method="post" class="wp2fa-setup-form" autocomplete="off">
523 <?php wp_nonce_field( 'wp2fa-step-choose-method' ); ?>
524
525 <?php
526 $nonce = wp_create_nonce( 'wp-2fa-backup-codes-generate-json-' . $user->ID );
527 // Change text if this is the user configuring there 2fa settings
528 // Filter $_GET array for security.
529 $get_array = filter_input_array( INPUT_GET );
530 // If this is the user setting things up, lets show nice message.
531 if ( ! empty( WP2FA::get_wp2fa_setting( 'enable_totp' ) ) && ! empty( WP2FA::get_wp2fa_setting( 'enable_email' ) ) ) {
532 $intro_text = esc_html__( 'Choose the 2FA authentication method', 'wp-2fa' );
533 $sub_text = esc_html__( 'There are two methods available from which you can choose for 2FA:', 'wp-2fa' );
534 } else {
535 $intro_text = esc_html__( 'Choose the 2FA authentication method', 'wp-2fa' );
536 $sub_text = esc_html__( 'Only the below 2FA method is allowed on this website:', 'wp-2fa' );
537 }
538 ?>
539 <h3><?php echo sanitize_text_field( $intro_text ); ?></h3>
540 <p><?php echo sanitize_text_field( $sub_text ); ?></p>
541
542 <fieldset>
543 <?php
544 if ( 'totp' === $enabled_method ) {
545 ?>
546 <div class="option-pill">
547 <label for="basic">
548 <input id="basic" name="wp_2fa_enabled_methods" type="radio" value="totp" checked>
549 <?php esc_html_e( 'Reconfigure the Google Authenticator 2FA', 'wp-2fa' ); ?>
550 </label>
551 <p class="description"><?php esc_html_e( 'Click the below button to reconfigure the current 2FA method. Note that once reset reset you will have to re-scan the QR code on all devices you want this to work on because the previous codes will stop working.', 'wp-2fa' ); ?>
552 </p>
553 <button class="button button-primary" data-trigger-reset-key data-nonce="<?php echo esc_attr( $nonce ); ?>" data-user-id="<?php echo esc_attr( $user->ID ); ?>" type="submit" name="save_step" value="<?php esc_attr_e( 'Reset', 'wp-2fa' ); ?>"><?php esc_html_e( 'Reset', 'wp-2fa' ); ?></button>
554 </div>
555 <div class="option-pill">
556 <p class="description">
557 <label for="geek">
558 <input id="email" name="wp_2fa_enabled_methods" type="radio" value="email">
559 <?php esc_html_e( 'One-time code sent to you over email', 'wp-2fa' ); ?>
560 </label>
561 </p>
562 <a href="#" class="button button-primary change-2fa-confirm" onclick="MicroModal.show('confirm-change-2fa');" data-check-on-click="#email"><?php esc_html_e( 'Configure and use this method instead', 'wp-2fa' ); ?></a>
563 <button class="button button-primary change-2fa-confirm hidden" type="submit" name="save_step" value="<?php esc_attr_e( 'Configure and use this method instead', 'wp-2fa' ); ?>"><?php esc_html_e( 'Submit', 'wp-2fa' ); ?></button>
564 </div>
565 <?php
566 }
567 if ( 'email' === $enabled_method ) {
568 ?>
569 <div class="option-pill">
570 <p class="description">
571 <label for="geek">
572 <input id="geek" name="wp_2fa_enabled_methods" type="radio" value="email" checked>
573 <?php esc_html_e( 'Reconfigure the email address for email 2FA', 'wp-2fa' ); ?>
574 </label>
575 </p>
576 <button class="button button-primary" type="submit" name="save_step" value="<?php esc_attr_e( 'Reconfigure email 2FA', 'wp-2fa' ); ?>"><?php esc_html_e( 'Reconfigure email 2FA', 'wp-2fa' ); ?></button>
577 </div>
578 <div class="option-pill">
579 <p class="description">
580 <label for="wp_2fa_enabled_methods">
581 <input id="totp" name="wp_2fa_enabled_methods" type="radio" value="totp">
582 <?php esc_html_e( 'one-time code generated with the Google authenticator app (most reliable and secure)', 'wp-2fa' ); ?>
583 </label>
584 </p>
585 <a href="#" class="button button-primary change-2fa-confirm" onclick="MicroModal.show('confirm-change-2fa');" data-check-on-click="#totp"><?php esc_html_e( 'Configure and use this method instead', 'wp-2fa' ); ?></a>
586 <button class="button button-primary change-2fa-confirm hidden" type="submit" name="save_step" value="<?php esc_attr_e( 'Configure and use this method instead', 'wp-2fa' ); ?>"><?php esc_html_e( 'Submit', 'wp-2fa' ); ?></button>
587 </div>
588 <?php
589 }
590 ?>
591 </fieldset>
592 <div class="wp2fa-setup-actions"></div>
593 </form>
594 <?php
595 }
596
597 /**
598 * Step Save: `Choose Method`
599 */
600 private function wp_2fa_step_user_choose_method_save() {
601 // Check nonce.
602 check_admin_referer( 'wp2fa-step-choose-method' );
603
604 // Grab current user.
605 $user = wp_get_current_user();
606
607 // Add our enabled methods to the user metadata.
608 if ( isset( $_POST['wp_2fa_enabled_methods'] ) ) {
609 $next = add_query_arg( 'enabled_methods', sanitize_text_field( wp_unslash( $_POST['wp_2fa_enabled_methods'] ) ), $this->get_next_step() );
610 wp_safe_redirect( esc_url_raw( $next ) );
611 }
612
613 exit();
614 }
615
616 /**
617 * Step View: `Setup Authenticator`
618 */
619 private function wp_2fa_step_setup_authenticator() {
620 // Grab current user.
621 $user = wp_get_current_user();
622
623 // Grab key from user meta.
624 $key = Authentication::get_user_totp_key( $user->ID );
625 $enabled_method = get_user_meta( $user->ID, 'wp_2fa_enabled_methods', true );
626
627 // If no key is present, lets make one.
628 if ( empty( $key ) ) {
629 $key = Authentication::generate_key();
630 $update = update_user_meta( $user->ID, 'wp_2fa_totp_key', $key );
631 }
632
633 // Setup site information, used when generating our QR code.
634 $site_name = get_bloginfo( 'name', 'display' );
635 $totp_title = apply_filters( 'wp_2fa_totp_title', $site_name . ':' . $user->user_login, $user );
636
637 // Now lets grab the users enabled 2fa methods.
638 $get_array = filter_input_array( INPUT_GET );
639 $selected_method = $get_array['enabled_methods'];
640
641 // Create a nonce incase we want to reset the key.
642 $nonce = wp_create_nonce( 'wp-2fa-backup-codes-generate-json-' . $user->ID );
643
644 // Grab notices.
645 $notices = get_user_meta( $user->ID, self::NOTICES_META_KEY, true );
646
647 if ( ! isset( $notices['error'] ) && empty( $notices['error'] ) ) {
648 $is_active = 'active';
649 $is_active2 = '';
650 } else {
651 $is_active = '';
652 $is_active2 = 'active';
653 }
654
655 // TOTP is enabled for the user, so lets display the relevant steps.
656 // Here we wrap each "sub step" (a step within a step) in .tep-setting-wrapper, and nudge to next "sub step" with next_step_setting button.
657 if ( 'totp' === $selected_method ) {
658 ?>
659 <div class="step-setting-wrapper <?php echo esc_attr( $is_active ); ?>">
660 <h3><?php esc_html_e( 'Setup the 2FA method', 'wp-2fa' ); ?></h3>
661 <div class="option-pill">
662 <img src="<?php echo esc_url( Authentication::get_google_qr_code( $totp_title, $key, $site_name ) ); ?>" id="wp-2fa-totp-qrcode" />
663 <ol>
664 <li><?php esc_html_e( 'Download the Google Authenticator app', 'wp-2fa' ); ?></li>
665 <li><?php esc_html_e( 'Click the plus sign (add new icon)', 'wp-2fa' ); ?></li>
666 <li><?php esc_html_e( 'Select \'Scan a barcode\'', 'wp-2fa' ); ?></li>
667 <li><?php esc_html_e( 'Scan the QR code to the right.', 'wp-2fa' ); ?></li>
668 </ol>
669 <p><?php esc_html_e( 'Otherwise, select Enter a provided key and type in the key below:', 'wp-2fa' ); ?></p>
670 <code><?php echo esc_html( $key ); ?></code>
671 </div>
672 <div class="wp2fa-setup-actions">
673 <button class="button button-primary" name="next_step_setting" value="<?php esc_attr_e( 'I\'m Ready', 'wp-2fa' ); ?>"><?php esc_html_e( 'I\'m Ready', 'wp-2fa' ); ?></button>
674 </div>
675 </div>
676
677 <div class="step-setting-wrapper <?php echo esc_attr( $is_active2 ); ?>">
678 <form method="post" class="wp2fa-setup-form" autocomplete="off">
679 <?php wp_nonce_field( 'wp2fa-step-login' ); ?>
680 <h3><?php esc_html_e( 'Almost there…', 'wp-2fa' ); ?></h3>
681 <p><?php esc_html_e( 'Please type in the one-time code from your Google Authenticator app to finalize the setup.', 'wp-2fa' ); ?></p>
682 <fieldset>
683 <label for="2fa-totp-authcode">
684 <?php esc_html_e( 'Authentication Code:', 'wp-2fa' ); ?>
685 <input type="tel" name="wp-2fa-totp-authcode" id="wp-2fa-totp-authcode" class="input" value="" size="20" pattern="[0-9]*" />
686 </label>
687 </fieldset>
688 <input type="hidden" name="wp-2fa-totp-key" value="<?php echo esc_attr( $key ); ?>" />
689 <div class="wp2fa-setup-actions">
690 <button class="button button-primary" type="submit" name="save_step" value="<?php esc_attr_e( 'Finish', 'wp-2fa' ); ?>"><?php esc_html_e( 'Finish', 'wp-2fa' ); ?></button>
691 </div>
692 </form>
693 </div>
694
695 <?php
696 // Display any error notices if they are available.
697 if ( isset( $notices['error'] ) && ! empty( $notices['error'] ) ) {
698 foreach ( $notices['error'] as $notice ) {
699 echo '<p class="description error">' . wp_kses_post( $notice ) . '</p>';
700 }
701 }
702 } elseif ( 'email' === $selected_method ) {
703 $setupnonce = wp_create_nonce( 'wp-2fa-send-setup-email' );
704 ?>
705 <div class="step-setting-wrapper <?php echo esc_attr( $is_active ); ?>">
706 <h3><?php esc_html_e( 'Setup the 2FA method', 'wp-2fa' ); ?></h3>
707 <p>
708 <?php esc_html_e( 'Please select the email address where the one-time code should be sent:', 'wp-2fa' ); ?>
709 </p>
710 <fieldset>
711 <label for="use_wp_email">
712 <input type="radio" name="wp_2fa_email_address" id="use_wp_email" value="<?php echo esc_attr( $user->user_email ); ?>" checked>
713 <span><?php esc_html_e( 'Use my WordPress user email (', 'wp-2fa' ); ?><small><?php echo esc_attr( $user->user_email ); ?></small><?php esc_html_e( ')', 'wp-2fa' ); ?></span>
714 </label>
715 <br/>
716 <label for="use_custom_email">
717 <input type="radio" name="wp_2fa_email_address" id="use_custom_email" value="use_custom_email">
718 <span><?php esc_html_e( 'Use a different email address:', 'wp-2fa' ); ?></span>
719 <input type="email" name="custom-email-address" id="custom-email-address" class="input wide" value=""/>
720 </label>
721 </fieldset>
722 <p class="description"><?php esc_html_e( 'Note: you should be able to access the mailbox of the email address to complete the following step.', 'wp-2fa' ); ?></p>
723 <div class="wp2fa-setup-actions">
724 <button class="button button-primary" name="next_step_setting" value="<?php esc_attr_e( 'I\'m Ready', 'wp-2fa' ); ?>" data-trigger-setup-email data-user-id="<?php echo esc_attr( $user->ID ); ?>" data-nonce="<?php echo esc_attr( $setupnonce ); ?>"><?php esc_html_e( 'I\'m Ready', 'wp-2fa' ); ?></button>
725 </div>
726 </div>
727
728 <div class="step-setting-wrapper <?php echo esc_attr( $is_active2 ); ?>">
729 <form method="post" class="wp2fa-setup-form" autocomplete="off">
730 <?php wp_nonce_field( 'wp2fa-step-login' ); ?>
731 <h4><?php esc_html_e( 'Almost there…', 'wp-2fa' ); ?></h4>
732 <p><?php esc_html_e( 'Please type in the one-time code sent to your email address to finalize the setup.', 'wp-2fa' ); ?></p>
733 <fieldset>
734 <label for="2fa-email-authcode">
735 <?php esc_html_e( 'Authentication Code:', 'wp-2fa' ); ?>
736 <input type="tel" name="wp-2fa-email-authcode" id="wp-2fa-email-authcode" class="input" value="" size="20" pattern="[0-9]*" />
737 </label>
738 </fieldset>
739
740 <input type="hidden" name="wp-2fa-totp-key" value="<?php echo esc_attr( $key ); ?>" />
741 <div class="wp2fa-setup-actions">
742 <button class="button button-primary" type="submit" name="save_step" value="<?php esc_attr_e( 'Finish', 'wp-2fa' ); ?>"><?php esc_html_e( 'Finish', 'wp-2fa' ); ?></button>
743 <a href="#" class="button button-secondary resend-email-code" data-trigger-setup-email data-user-id="<?php echo esc_attr( $user->ID ); ?>" data-nonce="<?php echo esc_attr( $setupnonce ); ?>">
744 <span class="resend-inner"><?php esc_html_e( 'Send me another code', 'wp-2fa' ); ?></span>
745 </a>
746 </div>
747 </form>
748 </div>
749 <?php
750 // Display any error notices if they are available.
751 if ( isset( $notices['error'] ) && ! empty( $notices['error'] ) ) {
752 foreach ( $notices['error'] as $notice ) {
753 echo '<p class="description error">' . wp_kses_post( $notice ) . '</p>';
754 }
755 }
756 }
757 }
758
759 /**
760 * Step Save: `Setup Authenticator`
761 */
762 private function wp_2fa_step_setup_authenticator_save() {
763 // Check nonce.
764 check_admin_referer( 'wp2fa-step-login' );
765
766 // Grab current user.
767 $user = wp_get_current_user();
768
769 // Setup some empty arrays which will may fill later, should an error arise along the way.
770 $notices = array();
771 $errors = array();
772
773 // Grab key from the $_POST.
774 if ( isset( $_POST['wp-2fa-totp-key'] ) ) {
775 $current_key = sanitize_text_field( wp_unslash( $_POST['wp-2fa-totp-key'] ) );
776 }
777
778 // Grab authcode and ensure its a number.
779 if ( isset( $_POST['wp-2fa-totp-authcode'] ) ) {
780 $_POST['wp-2fa-totp-authcode'] = (int) $_POST['wp-2fa-totp-authcode'];
781 }
782
783 // Check if we are dealing with totp or email, if totp validate and store a new secret key.
784 if ( ! empty( $_POST['wp-2fa-totp-authcode'] ) && ! empty( $current_key ) ) {
785 if ( Authentication::is_valid_key( $current_key ) || ! is_numeric( $_POST['wp-2fa-totp-authcode'] ) ) {
786 if ( ! Authentication::is_valid_authcode( $current_key, sanitize_text_field( wp_unslash( $_POST['wp-2fa-totp-authcode'] ) ) ) ) {
787 $errors[] = esc_html__( 'Invalid Two Factor Authentication code.', 'wp-2fa' );
788 }
789 } else {
790 $errors[] = esc_html__( 'Invalid Two Factor Authentication secret key.', 'wp-2fa' );
791 }
792
793 // If its not totp, is it email.
794 } elseif ( ! empty( $_POST['wp-2fa-email-authcode'] ) ) {
795 if ( ! Authentication::validate_token( $user->ID, sanitize_text_field( wp_unslash( $_POST['wp-2fa-email-authcode'] ) ) ) ) {
796 $errors[] = __( 'Invalid Email Authentication code.', 'wp-2fa' );
797 }
798 } else {
799 $errors[] = __( 'Please enter the code to finalize the 2FA setup.', 'wp-2fa' );
800 }
801
802 if ( ! empty( $errors ) ) {
803 $notices['error'] = $errors;
804 }
805
806 if ( ! empty( $notices ) ) {
807 update_user_meta( $user->ID, self::NOTICES_META_KEY, $notices );
808 }
809
810 // If no errors found, lets continue to next step and clear the notices, should any be present from previous attempts.
811 if ( empty( $notices ) ) {
812 if ( isset( $_POST['use_wp_email'] ) ) {
813 update_user_meta( $user->ID, 'wp_2fa_nominated_email_address', $user->user_email );
814 } elseif ( isset( $_POST['use_custom_email'] ) && isset( $_POST['custom-email-address'] ) ) {
815 update_user_meta( $user->ID, 'wp_2fa_nominated_email_address', sanitize_email( wp_unslash( $_POST['custom-email-address'] ) ) );
816 }
817
818 // Now lets grab the users enabled 2fa methods.
819 $get_array = filter_input_array( INPUT_GET );
820 $selected_method = sanitize_text_field( $get_array['enabled_methods'] );
821 // Check its one of our options.
822 if ( 'totp' === $selected_method || 'email' === $selected_method ) {
823 update_user_meta( $user->ID, 'wp_2fa_enabled_methods', sanitize_text_field( wp_unslash( $selected_method ) ) );
824 delete_user_meta( $user->ID, 'wp_2fa_grace_period_expiry' );
825 }
826
827 wp_safe_redirect( esc_url_raw( $this->get_next_step() ) );
828 exit();
829 }
830 }
831
832 /**
833 * Step View: `Finish`
834 */
835 private function wp_2fa_step_finish() {
836 ?>
837
838 <?php
839 $get_array = filter_input_array( INPUT_GET );
840 if ( isset( $get_array['first_time_setup'] ) ) {
841 $first_time_setup = sanitize_text_field( $get_array['first_time_setup'] );
842 } else {
843 $first_time_setup = '';
844 }
845 if ( ! empty( $first_time_setup ) ) :
846 ?>
847 <h3><?php esc_html_e( 'Your login just got more secure', 'wp-2fa' ); ?></h3>
848 <p><?php esc_html_e( 'Congratulations! You have enabled two-factor authentication for your user. You’ve just helped towards making this website more secure!', 'wp-2fa' ); ?></p>
849
850 <p><?php esc_html_e( 'You can exit this wizard now or continue to create backup codes.', 'wp-2fa' ); ?></p>
851
852 <form method="post" class="wp2fa-setup-form" autocomplete="off">
853 <?php wp_nonce_field( 'wp2fa-step-finish' ); ?>
854 <div class="wp2fa-setup-actions">
855 <a class="button button-primary" href="<?php echo esc_url( admin_url( 'options-general.php?page=wp-2fa-setup&current-step=backup_codes' ) ); ?>">
856 <?php esc_html_e( 'Continue & configure backup codes', 'wp-2fa' ); ?>
857 </a>
858 <a href="<?php echo esc_url( admin_url() ); ?>" class="button button-secondary">
859 <?php esc_html_e( 'Close wizard, I’ll configure them later', 'wp-2fa' ); ?>
860 </a>
861 </div>
862 </form>
863
864 <?php else : ?>
865
866 <h3><?php esc_html_e( 'Your website just got more secure!', 'wp-2fa' ); ?></h3>
867 <p><?php esc_html_e( 'Congratulations! You have enabled two-factor authentication for your user. You’ve just helped towards making this website more secure!', 'wp-2fa' ); ?></p>
868
869 <p><?php esc_html_e( 'You can exit this wizard now or continue to configure the plugin’s general settings. ', 'wp-2fa' ); ?></p>
870
871 <form method="post" class="wp2fa-setup-form" autocomplete="off">
872 <?php wp_nonce_field( 'wp2fa-step-finish' ); ?>
873 <div class="wp2fa-setup-actions">
874 <button class="button button-primary" type="submit" name="save_step" value="<?php esc_attr_e( 'Continue & configure the settings', 'wp-2fa' ); ?>">
875 <?php esc_html_e( 'Continue & configure the settings', 'wp-2fa' ); ?>
876 </button>
877 <a href="<?php echo esc_url( admin_url() ); ?>" class="button button-secondary">
878 <?php esc_html_e( 'Close wizard, I’ll configure them later', 'wp-2fa' ); ?>
879 </a>
880 </div>
881 </form>
882
883 <p class="description"><?php esc_html_e( 'Note: all the settings can be configured from the Settings > Two-factor Authentication entry of your WordPress menu.', 'wp-2fa' ); ?></p>
884
885 <?php
886 endif;
887 }
888
889 /**
890 * Step Save: `Finish`
891 */
892 private function wp_2fa_step_finish_save() {
893 // Verify nonce.
894 check_admin_referer( 'wp2fa-step-finish' );
895 wp_safe_redirect( esc_url_raw( $this->get_next_step() ) );
896 exit();
897 }
898
899 /**
900 * Step View: `Finish`
901 */
902 private function wp_2fa_step_backup_codes() {
903 // Grab current user.
904 $user = wp_get_current_user();
905 // Create a nonce for use in ajax call to generate codes.
906 $nonce = wp_create_nonce( 'wp-2fa-backup-codes-generate-json-' . $user->ID );
907 ?>
908 <div class="step-setting-wrapper active">
909 <h3><?php esc_html_e( 'Generate backup codes', 'wp-2fa' ); ?></h3>
910 <p><?php esc_html_e( 'It is recommended to generate and print some backup codes in case you lose access to your primary 2FA method. ', 'wp-2fa' ); ?></p>
911
912 <form method="post" class="wp2fa-setup-form" autocomplete="off">
913 <?php wp_nonce_field( 'wp2fa-step-finish' ); ?>
914 <div class="wp2fa-setup-actions">
915 <button class="button button-primary" name="next_step_setting" value="<?php esc_attr_e( 'Generate backup codes', 'wp-2fa' ); ?>" data-trigger-generate-backup-codes data-nonce="<?php echo esc_attr( $nonce ); ?>" data-user-id="<?php echo esc_attr( $user->ID ); ?>">
916 <?php esc_html_e( 'Generate backup codes', 'wp-2fa' ); ?>
917 </button>
918 <a href="<?php echo esc_url( admin_url() ); ?>" class="button button-secondary" type="submit" name="save_step" value="<?php esc_attr_e( 'I’ll generate them later', 'wp-2fa' ); ?>">
919 <?php esc_html_e( 'I’ll generate them later', 'wp-2fa' ); ?>
920 </a>
921 </div>
922 </form>
923 </div>
924
925 <div class="step-setting-wrapper align-center">
926 <h3><?php esc_html_e( 'Backup codes generated', 'wp-2fa' ); ?></h3>
927 <p><?php esc_html_e( 'Here are your backup codes:', 'wp-2fa' ); ?></p>
928 <code id="backup-codes-wrapper"></code>
929 <div class="wp2fa-setup-actions">
930 <button class="button button-primary" type="submit" value="<?php esc_attr_e( 'Download', 'wp-2fa' ); ?>" data-trigger-backup-code-download data-user="<?php echo esc_attr( $user->display_name ); ?>" data-website-url="<?php echo esc_attr( get_home_url() ); ?>">
931 <?php esc_html_e( 'Download', 'wp-2fa' ); ?>
932 </button>
933 <button class="button button-secondary" type="submit" value="<?php esc_attr_e( 'Print', 'wp-2fa' ); ?>" data-trigger-print data-nonce="<?php echo esc_attr( $nonce ); ?>" data-user-id="<?php echo esc_attr( $user->display_name ); ?>" data-website-url="<?php echo esc_attr( get_home_url() ); ?>">
934 <?php esc_html_e( 'Print', 'wp-2fa' ); ?>
935 </button>
936 </div>
937 </div>
938 <style>
939 .close-wizard-link { display: none; }
940 </style>
941 <?php
942 }
943
944 /**
945 * Step Save: `Finish`
946 */
947 private function wp_2fa_step_backup_codes_save() {
948 // Verify nonce.
949 check_admin_referer( 'wp2fa-step-finish' );
950 }
951
952 /**
953 * Step View: `Choose Methods`
954 */
955 private function wp_2fa_step_global_2fa_methods() {
956 $enforced_roles = trim( WP2FA::get_wp2fa_setting( 'enforced_roles' ) );
957 $enforced_users = trim( WP2FA::get_wp2fa_setting( 'enforced_users' ) );
958 $excluded_users = trim( WP2FA::get_wp2fa_setting( 'excluded_users' ) );
959 $excluded_roles = trim( WP2FA::get_wp2fa_setting( 'excluded_roles' ) );
960 ?>
961 <form method="post" class="wp2fa-setup-form" autocomplete="off">
962 <?php wp_nonce_field( 'wp2fa-step-choose-method' ); ?>
963 <div class="step-setting-wrapper active">
964 <h3><?php esc_html_e( 'Which two-factor authentication methods can your users use on this website?', 'wp-2fa' ); ?></h3>
965 <p><?php esc_html_e( 'When you disable one of the below 2FA methods none of your users can use it.', 'wp-2fa' ); ?></p>
966 <fieldset>
967 <div class="option-pill">
968 <label for="basic">
969 <input id="basic" name="wp_2fa_settings[enable_totp]" type="checkbox" value="enable_totp"
970 <?php checked( 'enable_totp', WP2FA::get_wp2fa_setting( 'enable_totp' ), true ); ?>
971 >
972 <?php esc_html_e( 'One-time code generated with the Google authenticator app (most reliable and secure)', 'wp-2fa' ); ?>
973 </label>
974 <?php
975 printf( '<p class="description">%1$s <a href="https://www.wpwhitesecurity.com/google-authenticator-app-wordpress-2fa/" target="_blank">%2$s</a></p>', esc_html__( 'Note: this method requires you to install the Google Authenticator app on a smart device. The app is available on Google Play and the Apple Appstore. For more information on this method read our', 'wp-2fa' ), esc_html__( 'our guide for Google Authenticator.', 'wp-2fa' ) );
976 ?>
977 </p>
978 </div>
979 <div class="option-pill">
980 <label for="geek">
981 <input id="geek" name="wp_2fa_settings[enable_email]" type="checkbox" value="enable_email"
982 <?php checked( WP2FA::get_wp2fa_setting( 'enable_email' ), 'enable_email' ); ?>
983 >
984 <?php esc_html_e( 'One-time code sent to user over email', 'wp-2fa' ); ?>
985 </label>
986 </div>
987 </fieldset>
988 <div class="wp2fa-setup-actions">
989 <a class="button button-primary" name="next_step_setting" value="<?php esc_attr_e( 'Continue Setup', 'wp-2fa' ); ?>"><?php esc_html_e( 'Continue Setup', 'wp-2fa' ); ?></a>
990 </div>
991 </div>
992 <div class="step-setting-wrapper">
993 <h3><?php esc_html_e( 'Do you want to enforce 2FA for some, or all the users?', 'wp-2fa' ); ?></h3>
994 <p><?php esc_html_e( 'When you enforce 2FA the users will be prompted to configure 2FA the next time they login. Users have a grace period for configuring 2FA. You can configure the grace period and also exclude user(s) or role(s) in this settings page.', 'wp-2fa' ); ?></p>
995 <fieldset class="contains-hidden-inputs">
996 <label for="all-users">
997 <input type="radio" name="wp_2fa_settings[enforcment-policy]" id="all-users" value="all-users"
998 <?php checked( WP2FA::get_wp2fa_setting( 'enforcment-policy' ), 'all-users' ); ?>
999 >
1000 <span><?php esc_html_e( 'All users', 'wp-2fa' ); ?></span>
1001 </label>
1002
1003 <br/>
1004 <label for="certain-roles-only">
1005 <input type="radio" name="wp_2fa_settings[enforcment-policy]" id="certain-roles-only" value="certain-roles-only"
1006 <?php checked( WP2FA::get_wp2fa_setting( 'enforcment-policy' ), 'certain-roles-only' ); ?>
1007 data-unhide-when-checked=".certain-roles-only-inputs">
1008 <span><?php esc_html_e( 'Only on users with these roles', 'wp-2fa' ); ?></span>
1009 </label>
1010 <fieldset class="hidden certain-roles-only-inputs">
1011 <br/>
1012 <input type="text" id="enforced_roles_search" placeholder="Search roles">
1013 <input type="hidden" id="enforced_roles" name="wp_2fa_settings[enforced_roles]" value="<?php echo esc_attr( $enforced_roles ); ?>">
1014 <div id="enforced_roles_buttons"></div>
1015 </fieldset>
1016
1017 <br/>
1018 <label for="certain-users-only">
1019 <input type="radio" name="wp_2fa_settings[enforcment-policy]" id="certain-users-only" value="certain-users-only"
1020 <?php checked( WP2FA::get_wp2fa_setting( 'enforcment-policy' ), 'certain-users-only' ); ?>
1021 data-unhide-when-checked=".certain-users-only-inputs">
1022 <span><?php esc_html_e( 'Only on these users', 'wp-2fa' ); ?></span>
1023 </label>
1024 <fieldset class="hidden certain-users-only-inputs">
1025 <br/>
1026 <input type="text" id="enforced_users_search" placeholder="Search users">
1027 <input type="hidden" id="enforced_users" name="wp_2fa_settings[enforced_users]" value="<?php echo esc_attr( $enforced_users ); ?>">
1028 <div id="enforced_users_buttons"></div>
1029 </fieldset>
1030
1031 <br/>
1032 <label for="do-not-enforce">
1033 <input type="radio" name="wp_2fa_settings[enforcment-policy]" id="do-not-enforce" value="do-not-enforce"
1034 <?php checked( WP2FA::get_wp2fa_setting( 'enforcment-policy' ), 'do-not-enforce' ); ?>
1035 >
1036 <span><?php esc_html_e( 'Do not enforce 2FA on any users', 'wp-2fa' ); ?></span>
1037 </label>
1038 <br/>
1039 </fieldset>
1040 <div class="wp2fa-setup-actions">
1041 <a class="button button-primary" name="next_step_setting" value="<?php esc_attr_e( 'Continue Setup', 'wp-2fa' ); ?>"><?php esc_html_e( 'Continue Setup', 'wp-2fa' ); ?></a>
1042 </div>
1043 </div>
1044 <div class="step-setting-wrapper">
1045 <h3><?php esc_html_e( 'Do you want to exclude any users or roles from 2FA?', 'wp-2fa' ); ?></h3>
1046 <p><?php esc_html_e( 'If you are enforcing 2FA on all users but for some reason you would like to exclude individual user(s) or users with a specific role, you can exclude them below', 'wp-2fa' ); ?></p>
1047 <fieldset>
1048 <div class="option-pill">
1049 <label for="basic"><?php esc_html_e( 'Exclude the following users', 'wp-2fa' ); ?>
1050 <input type="text" class="input wide" id="excluded_users_search" placeholder="Search user name">
1051 <input type="hidden" id="excluded_users" name="wp_2fa_settings[excluded_users]" value="<?php echo esc_attr( $excluded_users ); ?>">
1052 <div id="excluded_users_buttons"></div>
1053 </label>
1054 <label for="geek"><?php esc_html_e( 'Exclude the following roles', 'wp-2fa' ); ?>
1055 <input type="text" class="input wide" id="excluded_roles_search" placeholder="Search roles">
1056 <input type="hidden" id="excluded_roles" name="wp_2fa_settings[excluded_roles]" value="<?php echo esc_attr( $excluded_roles ); ?>">
1057 <div id="excluded_roles_buttons"></div>
1058 </label>
1059 </div>
1060 </fieldset>
1061 <div class="wp2fa-setup-actions">
1062 <a class="button button-primary" name="next_step_setting" value="<?php esc_attr_e( 'Continue Setup', 'wp-2fa' ); ?>"><?php esc_html_e( 'Continue Setup', 'wp-2fa' ); ?></a>
1063 </div>
1064 </div>
1065
1066 <?php if ( WP2FA::is_this_multisite() ) : ?>
1067 <div class="step-setting-wrapper">
1068 <h3><?php esc_html_e( 'Do you want to exclude all the users of a site from 2FA?', 'wp-2fa' ); ?></h3>
1069 <p><?php esc_html_e( 'If you are enforcing 2FA on all users but for some reason you do not want to enforce it on a specific sub site, specify the sub site name below:', 'wp-2fa' ); ?></p>
1070 <fieldset>
1071 <div class="option-pill">
1072 <label for="excluded_sites_search"><?php esc_html_e( 'Exclude the following sites', 'wp-2fa' ); ?>
1073 <input type="text" id="excluded_sites_search" placeholder="Search network">
1074 <input type="hidden" id="excluded_sites" name="wp_2fa_settings[excluded_sites]"
1075 value="<?php echo trim( sanitize_text_field( WP2FA::get_wp2fa_setting( 'excluded_sites' ) ) ); ?>">
1076 <div id="excluded_sites_buttons"></div>
1077 </label>
1078 </div>
1079 </fieldset>
1080 <div class="wp2fa-setup-actions">
1081 <a class="button button-primary" name="next_step_setting" value="<?php esc_attr_e( 'Continue Setup', 'wp-2fa' ); ?>"><?php esc_html_e( 'Continue Setup', 'wp-2fa' ); ?></a>
1082 </div>
1083 </div>
1084 <?php endif; ?>
1085
1086 <div class="step-setting-wrapper">
1087 <h3><?php esc_html_e( 'How long should the grace period for your users be?', 'wp-2fa' ); ?></h3>
1088 <p><?php esc_html_e( 'When you enforce 2FA on user(s) they have a grace period to configure 2FA. If they fail to configure it within the configured stipulated time, their account will be locked and have to be unlocked manually.', 'wp-2fa' ); ?></p>
1089 <fieldset>
1090 <input type="text" id="grace-period" name="wp_2fa_settings[grace-period]" value="3">
1091 <label class="radio-inline">
1092 <input type="radio" name="wp_2fa_settings[grace-period-denominator]" value="hours"><?php esc_html_e( 'Hours', 'wp-2fa' ); ?>
1093 </label>
1094 <label class="radio-inline">
1095 <input type="radio" name="wp_2fa_settings[grace-period-denominator]" value="days" checked><?php esc_html_e( 'Days', 'wp-2fa' ); ?>
1096 </label>
1097 </fieldset>
1098 <div class="wp2fa-setup-actions">
1099 <button class="button button-primary" type="submit" name="save_step" value="<?php esc_attr_e( 'All done', 'wp-2fa' ); ?>"><?php esc_html_e( 'All done', 'wp-2fa' ); ?></button>
1100 </div>
1101 </div>
1102 </form>
1103 <?php
1104 }
1105
1106 /**
1107 * Step Save: `Choose Method`
1108 */
1109 private function wp_2fa_step_global_2fa_methods_save() {
1110 // Check nonce.
1111 check_admin_referer( 'wp2fa-step-choose-method' );
1112
1113 // Grab current user.
1114 $user = wp_get_current_user();
1115
1116 if ( isset( $_POST['wp_2fa_settings'] ) && current_user_can( 'manage_options' ) ) {
1117 $settings = array_map( 'esc_attr', wp_unslash( $_POST['wp_2fa_settings'] ) );
1118 if ( WP2FA::is_this_multisite() ) {
1119 $users_args = array( 'blog_id' => 0 );
1120 } else {
1121 $users_args = array();
1122 }
1123 // Flush the old expiry away from ALL users, we will re-apply them based on the current setup at the end of this.
1124 $users = get_users( $users_args );
1125 if ( isset( $settings['enforcment-policy'] ) && 'do-not-enforce' !== $settings['enforcment-policy'] ) {
1126 foreach ( $users as $user ) {
1127 delete_user_meta( $user->ID, 'wp_2fa_grace_period_expiry' );
1128 }
1129 }
1130
1131 if ( ! isset( $settings['enable_totp'] ) && ! isset( $settings['enable_email'] ) ) {
1132 add_settings_error(
1133 'wp_2fa_settings',
1134 esc_attr( 'settings_error' ),
1135 esc_html__( 'At least one 2FA method should be enabled.', 'wp-2fa' ),
1136 'error'
1137 );
1138 }
1139
1140 // Compare current to old value to see if a method which was once enabled, has now been disabled.
1141 if ( ! isset( $settings['enable_totp'] ) && 'enable_totp' === WP2FA::get_wp2fa_setting( 'enable_totp' ) ) {
1142 foreach ( $users as $user ) {
1143 $enabled = get_user_meta( $user->ID, 'wp_2fa_enabled_methods', true );
1144 if ( 'totp' === $enabled ) {
1145 delete_user_meta( $user->ID, 'wp_2fa_enabled_methods', 'totp' );
1146 update_user_meta( $user->ID, 'wp_2fa_user_needs_to_reconfigure_2fa', true );
1147 }
1148 }
1149 }
1150 if ( ! isset( $settings['enable_email'] ) && 'enable_email' === WP2FA::get_wp2fa_setting( 'enable_email' ) ) {
1151 foreach ( $users as $user ) {
1152 $enabled = get_user_meta( $user->ID, 'wp_2fa_enabled_methods', true );
1153 if ( 'email' === $enabled ) {
1154 delete_user_meta( $user->ID, 'wp_2fa_enabled_methods', 'email' );
1155 update_user_meta( $user->ID, 'wp_2fa_user_needs_to_reconfigure_2fa', true );
1156 }
1157 }
1158 }
1159
1160 if ( isset( $settings['enable_totp'] ) && 'enable_totp' === $settings['enable_totp'] ) {
1161 $output['enable_totp'] = sanitize_text_field( $settings['enable_totp'] );
1162 }
1163
1164 if ( isset( $settings['enable_email'] ) && 'enable_email' === $settings['enable_email'] ) {
1165 $output['enable_email'] = sanitize_text_field( $settings['enable_email'] );
1166 }
1167
1168 if ( isset( $settings['enforcment-policy'] ) && 'all-users' === $settings['enforcment-policy'] || isset( $settings['enforcment-policy'] ) && 'certain-users-only' === $settings['enforcment-policy'] || isset( $settings['enforcment-policy'] ) && 'certain-roles-only' === $settings['enforcment-policy'] || isset( $settings['enforcment-policy'] ) && 'do-not-enforce' === $settings['enforcment-policy'] ) {
1169 $output['enforcment-policy'] = $settings['enforcment-policy'];
1170 }
1171
1172 if ( isset( $settings['enforced_roles'] ) ) {
1173 $output['enforced_roles'] = trim( sanitize_text_field( $settings['enforced_roles'] ) );
1174 }
1175
1176 if ( isset( $settings['enforced_users'] ) ) {
1177 $output['enforced_users'] = trim( sanitize_text_field( $settings['enforced_users'] ) );
1178 }
1179
1180 if ( isset( $settings['excluded_users'] ) ) {
1181 $output['excluded_users'] = trim( sanitize_text_field( $settings['excluded_users'] ) );
1182
1183 // Wipe user 2fa data.
1184 $user_array = explode( ',', $output['excluded_users'] );
1185 foreach ( $user_array as $user ) {
1186 if ( ! empty( $user ) ) {
1187 $user_to_wipe = get_user_by( 'login', $user );
1188 $wipe_totp_key = delete_user_meta( $user_to_wipe->ID, 'wp_2fa_totp_key' );
1189 $wipe_backup_codes = delete_user_meta( $user_to_wipe->ID, 'wp_2fa_backup_codes' );
1190 $wipe_enabled_methods = delete_user_meta( $user_to_wipe->ID, 'wp_2fa_enabled_methods' );
1191 $wipe_grace_period = delete_user_meta( $user_to_wipe->ID, 'wp_2fa_grace_period_expiry' );
1192 }
1193 }
1194 }
1195
1196 if ( isset( $settings['excluded_roles'] ) ) {
1197 $output['excluded_roles'] = trim( sanitize_text_field( $settings['excluded_roles'] ) );
1198
1199 // Wipe user 2fa data.
1200 $excluded_roles_array = array_filter( explode( ',', strtolower( $output['excluded_roles'] ) ) );
1201 foreach ( $users as $user ) {
1202 // Compare the user roles to the ones we are excluding and see if we get a match.
1203 $result = array_intersect( $excluded_roles_array, $user->roles );
1204 // If we do, lets wipe!
1205 if ( ! empty( $result ) ) {
1206 $wipe_totp_key = delete_user_meta( $user->ID, 'wp_2fa_totp_key' );
1207 $wipe_backup_codes = delete_user_meta( $user->ID, 'wp_2fa_backup_codes' );
1208 $wipe_enabled_methods = delete_user_meta( $user->ID, 'wp_2fa_enabled_methods' );
1209 $wipe_grace_period = delete_user_meta( $user->ID, 'wp_2fa_grace_period_expiry' );
1210 }
1211 }
1212 }
1213
1214 if ( WP2FA::is_this_multisite() ) {
1215 if ( isset( $input['excluded_sites'] ) ) {
1216 $output['excluded_sites'] = trim( sanitize_text_field( $input['excluded_sites'] ) );
1217 } else {
1218 $output['excluded_sites'] = '';
1219 }
1220 } else {
1221 $output['excluded_sites'] = '';
1222 }
1223
1224 if ( isset( $settings['grace-period'] ) ) {
1225 if ( 0 === (int) $settings['grace-period'] ) {
1226 add_settings_error(
1227 'wp_2fa_settings',
1228 esc_attr( 'settings_error' ),
1229 esc_html__( 'Grace period must be at least 1 day/hour', 'wp-2fa' ),
1230 'error'
1231 );
1232 $output['grace-period'] = 1;
1233 } else {
1234 $output['grace-period'] = (int) $settings['grace-period'];
1235 }
1236 }
1237
1238 if ( isset( $settings['grace-period-denominator'] ) && 'days' === $settings['grace-period-denominator'] || isset( $settings['grace-period-denominator'] ) && 'hours' === $settings['grace-period-denominator'] || isset( $settings['grace-period-denominator'] ) && 'seconds' === $settings['grace-period-denominator'] ) {
1239 $output['grace-period-denominator'] = sanitize_text_field( $settings['grace-period-denominator'] );
1240 }
1241
1242 if ( isset( $settings['enable_grace_cron'] ) ) {
1243 $output['enable_grace_cron'] = (bool) $input['enable_grace_cron'];
1244 }
1245
1246 if ( isset( $input['2fa_main_user'] ) ) {
1247 $output['2fa_settings_last_updated_by'] = (int) $settings['2fa_main_user'];
1248 }
1249
1250 if ( isset( $settings['limit_access'] ) ) {
1251 $output['limit_access'] = (bool) $settings['limit_access'];
1252 }
1253
1254 if ( isset( $settings['grace-period'] ) && isset( $settings['grace-period-denominator'] ) ) {
1255 // Turn inputs into a useable string.
1256 $create_a_string = $output['grace-period'] . ' ' . $output['grace-period-denominator'];
1257 // Turn that string into a time.
1258 $grace_expiry = strtotime( $create_a_string );
1259 $output['grace-period-expiry-time'] = sanitize_text_field( $grace_expiry );
1260 }
1261
1262 // Fetch users and apply the grace period tp their user meta.
1263 if ( isset( $settings['enforcment-policy'] ) && 'do-not-enforce' !== $settings['enforcment-policy'] && ! isset( $settings['grace-period-expiry-time'] ) ) {
1264 // Flush the old expiry away from ALL users, we will re-apply them based on the current setup at the end of this.
1265 foreach ( $users as $user ) {
1266 delete_user_meta( $user->ID, 'wp_2fa_grace_period_expiry' );
1267 }
1268 // If we are specifying to enforce 2fa for specific users, we have no need to check if they are eligble or excluded, so we dont.
1269 if ( isset( $settings['enforcment-policy'] ) && 'certain-users-only' === $settings['enforcment-policy'] && isset( $settings['enforced_users'] ) && WP2FA::get_wp2fa_setting( 'enforced_users' ) !== $settings['enforced_users'] ) {
1270 $enforced_users_array = array_filter( explode( ',', $settings['enforced_users'] ) );
1271 foreach ( $enforced_users_array as $enforced_user ) {
1272 update_user_meta( $user->ID, 'wp_2fa_grace_period_expiry', $grace_expiry );
1273 $user = get_user_by( 'login', $enforced_user );
1274 SettingsPage::send_2fa_enforced_email( $user->ID );
1275 }
1276 } else {
1277 foreach ( $users as $user ) {
1278 $is_needed = Authentication::is_user_eligible_for_2fa( $user->ID, $settings['enforcment-policy'], $output['excluded_users'], $output['excluded_roles'], $output['enforced_users'], $output['enforced_roles'] );
1279 $is_user_excluded = WP2FA::is_user_excluded( $user, $output['excluded_users'], $output['excluded_roles'], $output['excluded_sites'] );
1280 if ( $is_needed && ! $is_user_excluded ) {
1281 update_user_meta( $user->ID, 'wp_2fa_grace_period_expiry', $grace_expiry );
1282 SettingsPage::send_2fa_enforced_email( $user->ID );
1283 }
1284 }
1285 }
1286 }
1287
1288 if ( WP2FA::is_this_multisite() ) {
1289 update_network_option( null, 'wp_2fa_settings', $output );
1290 add_network_option( null, 'wp_2fa_setup_wizard_complete', true );
1291 } else {
1292 update_option( 'wp_2fa_settings', $output );
1293 add_option( 'wp_2fa_setup_wizard_complete', true );
1294 }
1295 }
1296
1297 wp_safe_redirect( esc_url_raw( $this->get_next_step() ) );
1298 exit();
1299 }
1300
1301 /**
1302 * Send email with fresh code, or to setup email 2fa.
1303 *
1304 * @param int $user_id User id we want to send the message to.
1305 * @param string $nonce The nonce.
1306 */
1307 public static function send_authentication_setup_email( $user_id, $nonce = '' ) {
1308
1309 // If we have a nonce posted, check it.
1310 if ( wp_doing_ajax() && isset( $_POST['nonce'] ) ) {
1311 $nonce_check = wp_verify_nonce( sanitize_text_field( $_POST['nonce'] ), 'wp-2fa-send-setup-email' );
1312 if ( ! $nonce_check ) {
1313 return false;
1314 exit();
1315 }
1316 }
1317
1318 if ( isset( $_POST['user_id'] ) ) {
1319 $user = get_userdata( intval( $_POST['user_id'] ) );
1320 } else {
1321 $user = get_userdata( $user_id );
1322 }
1323
1324 // Seeing as we got this far, we need to clear notices to make way for anything fresh.
1325 delete_user_meta( $user->ID, self::NOTICES_META_KEY );
1326
1327 // Grab email address is its provided.
1328 if ( isset( $_POST['email_address'] ) ) {
1329 $email = sanitize_email( $_POST['email_address'] );
1330 } else {
1331 $email = sanitize_email( $user->user_email );
1332 }
1333
1334 if ( wp_doing_ajax() && isset( $_POST['nonce'] ) ) {
1335 update_user_meta( $user->ID, 'wp_2fa_nominated_email_address', $email );
1336 }
1337
1338 $enabled_email_address = get_user_meta( $user->ID, 'wp_2fa_nominated_email_address', true );
1339
1340 // Generate a token and setup email.
1341 $token = Authentication::generate_token( $user->ID );
1342 $subject = wp_strip_all_tags( WP2FA::replace_email_strings( WP2FA::get_wp2fa_email_templates( 'login_code_email_subject' ), $user->ID ) );
1343 $message = wpautop( WP2FA::replace_email_strings( WP2FA::get_wp2fa_email_templates( 'login_code_email_body' ), $user->ID, $token ) );
1344
1345 if ( ! empty( $enabled_email_address ) ) {
1346 $email_address = $enabled_email_address;
1347 } else {
1348 $email_address = $user->user_email;
1349 }
1350 $headers = 'Content-type: text/html;charset=utf-8' . "\r\n";
1351
1352 if ( 'use-custom-email' === WP2FA::get_wp2fa_email_templates( 'email_from_setting' ) ) {
1353 $headers .= "From: ". WP2FA::get_wp2fa_email_templates( 'custom_from_display_name' ) ." <". WP2FA::get_wp2fa_email_templates( 'custom_from_email_address' ) .">" . "\r\n";
1354 }
1355
1356 return wp_mail( $email_address, $subject, $message, $headers );
1357 }
1358
1359 /**
1360 * Send email to setup authentication
1361 */
1362 public function regenerate_authentication_key() {
1363 // Grab current user.
1364 $user = wp_get_current_user();
1365
1366 // Delete the key
1367 Authentication::delete_user_totp_key( $user->ID );
1368
1369 // Return something, not sure why
1370 return true;
1371 }
1372
1373 /**
1374 * Step View: `Setup Authenticator`
1375 */
1376 private function wp_2fa_step_reconfigure_authenticator() {
1377 // Grab current user
1378 $user = wp_get_current_user();
1379
1380 // Grab key from user meta
1381 $key = Authentication::get_user_totp_key( $user->ID );
1382
1383 // If no key is present, lets make one
1384 if ( empty( $key ) ) {
1385 $key = Authentication::generate_key();
1386 $update = update_user_meta( $user->ID, 'wp_2fa_totp_key', $key );
1387 }
1388
1389 // Setup site information, used when generating our QR code
1390 $site_name = get_bloginfo( 'name', 'display' );
1391 $totp_title = apply_filters( 'wp_2fa_totp_title', $site_name . ':' . $user->user_login, $user );
1392
1393 // Now lets grab the users enabled 2fa methods.
1394 $selected_method = get_user_meta( $user->ID, 'wp_2fa_enabled_methods', true );
1395
1396 // Create a nonce incase we want to reset the key
1397 $nonce = wp_create_nonce( 'wp-2fa-backup-codes-generate-json-' . $user->ID );
1398
1399 if ( ! isset( $notices['error'] ) && empty( $notices['error'] ) ) {
1400 $is_active = 'active';
1401 $is_active2 = '';
1402 } else {
1403 $is_active = '';
1404 $is_active2 = 'active';
1405 }
1406
1407 // TOTP is enabled for the user, so lets display the relevant steps.
1408 // Here we wrap each "sub step" (a step within a step) in .tep-setting-wrapper, and nudge to next "sub step" with next_step_setting button.
1409 if ( ! empty( WP2FA::get_wp2fa_setting( 'enable_totp' ) ) ) {
1410 ?>
1411 <div class="step-setting-wrapper <?php echo esc_attr( $is_active ); ?>">
1412 <h3>
1413 <?php esc_html_e( 'Reconfigure the Google Authenticator 2FA', 'wp-2fa' ); ?>
1414 </h3>
1415 <p>
1416 <?php esc_html_e( 'Click the below button to reconfigure the current 2FA method. Note that once reset reset you will have to re-scan the QR code on all devices you want this to work on because the previous codes will stop working.', 'wp-2fa' ); ?>
1417 </p>
1418 <div class="wp2fa-setup-actions">
1419 <a href="<?php echo esc_url( admin_url( 'options-general.php?page=wp-2fa-setup&current-step=setup_method' ) ); ?>" class="button button-primary" data-trigger-reset-key data-nonce="<?php echo esc_attr( $nonce ); ?>" data-user-id="<?php echo esc_attr( $user->ID ); ?>"><?php esc_html_e( 'Reset Key', 'wp-2fa' ); ?></a>
1420 </div>
1421 </div>
1422
1423 <?php
1424 } elseif ( ! empty( WP2FA::get_wp2fa_setting( 'enable_email' ) ) ) {
1425 $setupnonce = wp_create_nonce( 'wp-2fa-send-setup-email' );
1426 ?>
1427 <div class="step-setting-wrapper <?php echo esc_attr( $is_active ); ?>">
1428 <h3><?php esc_html_e( 'Setup the 2FA method', 'wp-2fa' ); ?></h3>
1429 <p>
1430 <?php esc_html_e( 'Please select the email address where the one-time code should be sent:', 'wp-2fa' ); ?>
1431 </p>
1432 <fieldset>
1433 <label for="use_wp_email">
1434 <span><?php esc_html_e( 'Type in below the new email address where you want to receive the 2FA one-time codes.', 'wp-2fa' ); ?></span>
1435 <input type="email" name="custom-email-address" id="custom-email-address" class="input wide" value=""/>
1436 </label>
1437 </fieldset>
1438 <div class="wp2fa-setup-actions">
1439 <button class="button button-primary" name="next_step_setting" value="<?php esc_attr_e( 'I\'m Ready', 'wp-2fa' ); ?>" data-trigger-setup-email data-user-id="<?php echo esc_attr( $user->ID ); ?>" data-nonce="<?php echo esc_attr( $setupnonce ); ?>"><?php esc_html_e( 'Change email address', 'wp-2fa' ); ?></button>
1440 </div>
1441 </div>
1442
1443 <div class="step-setting-wrapper <?php echo esc_attr( $is_active2 ); ?>">
1444 <form method="post" class="wp2fa-setup-form" autocomplete="off">
1445 <?php wp_nonce_field( 'wp2fa-step-login' ); ?>
1446 <h4><?php esc_html_e( 'Almost there…', 'wp-2fa' ); ?></h4>
1447 <p><?php esc_html_e( 'Please type in the one-time code sent to your email address to finalize the setup.', 'wp-2fa' ); ?></p>
1448 <fieldset>
1449 <label for="2fa-email-authcode">
1450 <?php esc_html_e( 'Authentication Code:', 'wp-2fa' ); ?>
1451 <input type="tel" name="wp-2fa-email-authcode" id="wp-2fa-email-authcode" class="input" value="" size="20" pattern="[0-9]*" />
1452 </label>
1453 </fieldset>
1454
1455 <input type="hidden" name="2fa-totp-key" value="<?php echo esc_attr( $key ); ?>" />
1456 <div class="wp2fa-setup-actions">
1457 <button class="button button-primary" type="submit" name="save_step" value="<?php esc_attr_e( 'Finish', 'wp-2fa' ); ?>"><?php esc_html_e( 'Finish', 'wp-2fa' ); ?></button>
1458 </div>
1459 </form>
1460 </div>
1461 <?php
1462 // Display any error notices if they are available.
1463 if ( isset( $notices['error'] ) && ! empty( $notices['error'] ) ) {
1464 foreach ( $notices['error'] as $notice ) {
1465 echo '<p class="description error">' . wp_kses_post( $notice ) . '</p>';
1466 }
1467 }
1468 }
1469 }
1470
1471 /**
1472 * Step Save: `Setup Authenticator`
1473 */
1474 private function wp_2fa_step_reconfigure_authenticator_save() {
1475 // Check nonce.
1476 check_admin_referer( 'wp2fa-step-login' );
1477 // Grab current user
1478 $user = wp_get_current_user();
1479
1480 // Setup some empty arrays which will may fill later, should an error arise along the way.
1481 $notices = array();
1482 $errors = array();
1483
1484 // Grab key from the $_POST
1485 if ( isset( $_POST['wp-2fa-totp-key'] ) ) {
1486 $current_key = sanitize_text_field( wp_unslash( $_POST['wp-2fa-totp-key'] ) );
1487 }
1488
1489 // Check if we are dealing with totp or email, if totp validate and store a new secret key.
1490 if ( ! empty( $_POST['wp-2fa-totp-authcode'] ) && ! empty( $current_key ) ) {
1491 if ( Authentication::is_valid_key( $current_key ) || ! is_numeric( $_POST['wp-2fa-totp-authcode'] ) ) {
1492 if ( ! Authentication::is_valid_authcode( $current_key, $_POST['wp-2fa-totp-authcode'] ) ) {
1493 $errors[] = esc_html__( 'Invalid Two Factor Authentication code.', 'wp-2fa' );
1494 }
1495 } else {
1496 $errors[] = esc_html__( 'Invalid Two Factor Authentication secret key.', 'wp-2fa' );
1497 }
1498
1499 // If its not totp, is it email?
1500 } elseif ( ! empty( $_POST['wp-2fa-email-authcode'] ) ) {
1501 if ( ! Authentication::validate_token( $user->ID, $_POST['wp-2fa-email-authcode'] ) ) {
1502 $errors[] = __( 'Invalid Email Authentication code.', 'wp-2fa' );
1503 }
1504 } else {
1505 $errors[] = __( 'Please enter the code to finalize the 2FA setup.', 'wp-2fa' );
1506 }
1507
1508 if ( ! empty( $errors ) ) {
1509 $notices['error'] = $errors;
1510 }
1511
1512 if ( ! empty( $notices ) ) {
1513 update_user_meta( $user->ID, self::NOTICES_META_KEY, $notices );
1514 delete_user_meta( $user->ID, 'wp_2fa_nominated_email_address' );
1515 }
1516
1517 // If no errors found, lets continue to next step and clear the notices, should any be present from previous attempts.
1518 if ( empty( $notices ) ) {
1519 wp_safe_redirect( esc_url_raw( $this->get_next_step() ) );
1520 exit();
1521 }
1522 }
1523 }
1524