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