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