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