PluginProbe ʕ •ᴥ•ʔ
WP 2FA – Two-factor authentication for WordPress / 2.8.0
WP 2FA – Two-factor authentication for WordPress v2.8.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 / Methods / class-totp-wizard-steps.php
wp-2fa / includes / classes / Admin / Methods Last commit date
Traits 1 year ago class-backup-codes.php 1 year ago class-email-wizard-steps.php 1 year ago class-email.php 1 year ago class-totp-wizard-steps.php 1 year ago class-totp.php 1 year ago index.php 1 year ago
class-totp-wizard-steps.php
393 lines
1 <?php
2 /**
3 * Responsible for WP2FA user's TOTP manipulation.
4 *
5 * @package wp2fa
6 * @subpackage methods-wizard
7 * @since 2.6.0
8 * @copyright 2024 Melapress
9 * @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
10 *
11 * @see https://wordpress.org/plugins/wp-2fa/
12 */
13
14 declare(strict_types=1);
15
16 namespace WP2FA\Methods\Wizards;
17
18 use WP2FA\WP2FA;
19 use WP2FA\Methods\TOTP;
20 use WP2FA\Admin\Helpers\WP_Helper;
21 use WP2FA\Admin\Views\Wizard_Steps;
22 use WP2FA\Admin\Helpers\User_Helper;
23 use WP2FA\Admin\Controllers\Settings;
24 use WP2FA\Admin\Methods\Traits\Methods_Wizards_Trait;
25 use WP2FA\Authenticator\Authentication;
26 use WP2FA\Extensions\RoleSettings\Role_Settings_Controller;
27
28 /**
29 * Class for handling totp codes.
30 *
31 * @since 2.6.0
32 *
33 * @package WP2FA
34 */
35 if ( ! class_exists( '\WP2FA\Methods\Wizards\TOTP_Wizard_Steps' ) ) {
36 /**
37 * TOTP code class, for handling totp (app) code generation and such.
38 *
39 * @since 2.6.0
40 */
41 class TOTP_Wizard_Steps extends Wizard_Steps {
42
43 use Methods_Wizards_Trait;
44
45 /**
46 * Keeps the main class method name, so we can call it when needed.
47 *
48 * @var string
49 *
50 * @since 2.6.0
51 */
52 private static $main_class = TOTP::class;
53
54 /**
55 * The default value of the method order in the wizards.
56 *
57 * @var integer
58 *
59 * @since 2.6.0
60 */
61 private static $order = 1;
62
63 /**
64 * Inits the class hooks
65 *
66 * @return void
67 *
68 * @since 2.4.0
69 */
70 public static function init() {
71 \add_filter( WP_2FA_PREFIX . 'methods_modal_options', array( __CLASS__, 'totp_option' ), 10, 2 );
72 \add_action( WP_2FA_PREFIX . 'modal_methods', array( __CLASS__, 'totp_modal_configure' ) );
73 \add_filter( WP_2FA_PREFIX . 'methods_re_configure', array( __CLASS__, 'totp_re_configure' ), 10, 2 );
74 \add_filter( WP_2FA_PREFIX . 'methods_settings', array( __CLASS__, 'totp_wizard_settings' ), 10, 4 );
75 }
76
77 /**
78 * Shows the option to reconfigure email (if applicable)
79 *
80 * @param array $methods - Array of methods collected.
81 * @param string $role - The name of the role to show option to.
82 *
83 * @since 2.6.0
84 *
85 * @return array
86 */
87 public static function totp_re_configure( array $methods, string $role ): array {
88
89 if ( ! TOTP::is_enabled() ) {
90 return $methods;
91 }
92 \ob_start();
93 ?>
94 <div class="option-pill">
95 <?php echo \wp_kses_post( WP2FA::contextual_reconfigure_text( WP2FA::get_wp2fa_white_label_setting( 'totp_reconfigure_intro', true ), User_Helper::get_user_object()->ID, TOTP::METHOD_NAME ) ); ?>
96 <div class="wp2fa-setup-actions">
97 <a href="#" class="button button-primary wp-2fa-button-primary" data-name="next_step_setting_modal_wizard" data-trigger-reset-key <?php echo WP_Helper::create_data_nonce( self::json_nonce() ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> data-user-id="<?php echo \esc_attr( User_Helper::get_user_object()->ID ); ?>" data-next-step="2fa-wizard-totp"><?php \esc_html_e( 'Reset Key', 'wp-2fa' ); ?></a>
98 </div>
99 </div>
100 <?php
101 $output = ob_get_contents();
102 ob_end_clean();
103
104 $methods[ self::get_order( $role, $methods ) ] = array(
105 'name' => self::$main_class::METHOD_NAME,
106 'output' => $output,
107 );
108
109 return $methods;
110 }
111
112 /**
113 * Shows the initial totp setup options based on enabled methods
114 *
115 * @param array $methods - Array of methods collected.
116 * @param string $role - The name of the role to show option to.
117 *
118 * @since 2.6.0
119 *
120 * @return array
121 */
122 public static function totp_option( array $methods, string $role ): array {
123 if ( TOTP::is_enabled() ) {
124 \ob_start();
125 ?>
126 <div class="option-pill">
127 <label for="basic">
128 <input id="basic" name="wp_2fa_enabled_methods" type="radio" value="totp">
129 <?php echo \wp_kses_post( WP2FA::get_wp2fa_white_label_setting( 'totp-option-label', true ) ); ?><span class="wizard-tooltip" data-tooltip-content="data-totp-tooltip-content-wrapper">i</span>
130 </label>
131 <?php
132 echo '<p class="description tooltip-content-wrapper" data-totp-tooltip-content-wrapper>';
133 echo \wp_kses_post( WP2FA::get_wp2fa_white_label_setting( 'totp-option-label-hint', true ) );
134 echo '</p>';
135 ?>
136 </div>
137 <?php
138 $output = ob_get_contents();
139 ob_end_clean();
140
141 $methods[ self::get_order( $role, $methods ) ] = $output;
142 }
143
144 return $methods;
145 }
146
147 /**
148 * Shows the TOTP modal configuration.
149 *
150 * @return void
151 *
152 * @since 2.6.0
153 */
154 public static function totp_modal_configure() {
155 if ( TOTP::is_enabled() ) {
156 ?>
157 <div class="wizard-step" id="2fa-wizard-totp">
158 <fieldset>
159 <?php self::totp_configure(); ?>
160 </fieldset>
161 </div>
162 <?php
163 }
164 }
165
166 /**
167 * Reconfigures the totp form
168 *
169 * @since 2.6.0
170 *
171 * @return void
172 */
173 public static function totp_configure() {
174
175 if ( ! TOTP::is_enabled() ) {
176 return;
177 }
178
179 // Regenerate the code if the method is not in use.
180 if ( TOTP::METHOD_NAME !== User_Helper::get_enabled_method_for_user() ) {
181 TOTP::remove_user_totp_key();
182 }
183
184 /**
185 * Active on modal, additional attribute is required on standard HTML (check below)
186 */
187 $add_step_attributes = 'active';
188
189 /**
190 * Closing div for extra modal wrappers see lines above
191 */
192 $close_div = '';
193
194 $qr_code = '<img class="qr-code" src="' . ( TOTP::get_qr_code() ) . '" id="wp-2fa-totp-qrcode" />';
195 $open30_wrapper = '
196 <div class="mb-30 clear-both">
197 ';
198 $open60_wrapper = '
199 <div class="modal-60">
200 ';
201 $open40_wrapper = '
202 <div class="modal-40">
203 ';
204 $close_div = '
205 </div>
206 ';
207
208 ?>
209 <div class="step-setting-wrapper <?php echo \esc_attr( $add_step_attributes ); ?>">
210 <div class="mb-20">
211 <?php echo wp_kses_post( WP2FA::get_wp2fa_white_label_setting( 'method_help_totp_intro', true ) ); ?>
212 </div>
213 <?php echo $open30_wrapper . $open40_wrapper; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
214
215 <div class="qr-code-wrapper">
216 <?php echo $qr_code; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
217 </div>
218 <?php
219 echo $close_div; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
220 echo $open60_wrapper; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
221 ?>
222
223 <div class="radio-cells option-pill mb-0">
224 <ol class="wizard-custom-counter">
225 <li><?php echo \wp_kses_post( WP2FA::get_wp2fa_white_label_setting( 'method_help_totp_step_1', true ) ); ?>
226 <?php
227 if ( ! empty( WP2FA::get_wp2fa_white_label_setting( 'show_help_text' ) ) ) {
228 ?>
229 <span class="wizard-tooltip" data-tooltip-content="data-totp-setup-tooltip-content-wrapper">i</span><?php } ?></li>
230 <li><?php echo \wp_kses_post( WP2FA::get_wp2fa_white_label_setting( 'method_help_totp_step_2', true ) ); ?>
231 <div class="app-key-wrapper">
232 <input type="text" id="app-key-input" readonly value="<?php echo \esc_html( TOTP::get_totp_decrypted() ); ?>" class="app-key">
233 <?php
234 if ( is_ssl() ) {
235 ?>
236 <span class="click-to-copy"><?php \esc_html_e( 'COPY', 'wp-2fa' ); ?></span>
237 <?php } ?>
238 </div>
239 </li>
240 <li><?php echo \wp_kses_post( WP2FA::get_wp2fa_white_label_setting( 'method_help_totp_step_3', true ) ); ?></li>
241 </ol>
242 </div>
243 <?php
244 echo $close_div; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
245 echo $close_div; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
246 ?>
247 <?php if ( ! empty( WP2FA::get_wp2fa_white_label_setting( 'show_help_text' ) ) ) : ?>
248 <div class="tooltip-content-wrapper" data-totp-setup-tooltip-content-wrapper>
249 <p class="description"><?php \esc_html_e( 'Click on the icon of the app that you are using for a detailed guide on how to set it up.', 'wp-2fa' ); ?></p>
250 <div class="apps-wrapper">
251 <?php foreach ( Authentication::get_apps() as $app ) { ?>
252 <a href="https://melapress.com/support/kb/wp-2fa-configuring-2fa-apps/?&utm_source=plugin&utm_medium=link&utm_campaign=wp2fa#<?php echo $app['hash']; ?>" target="_blank" class="app-logo"><img src="<?php echo \esc_url( WP_2FA_URL . 'dist/images/' . $app['logo'] ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>"></a>
253 <?php } ?>
254 </div>
255 </div>
256 <?php endif; ?>
257 <div class="wp2fa-setup-actions">
258 <button class="button wp-2fa-button-primary" name="next_step_setting" value="<?php \esc_attr_e( 'I\'m Ready', 'wp-2fa' ); ?>" type="button"><?php \esc_html_e( 'I\'m Ready', 'wp-2fa' ); ?></button>
259 <a class="button button-primary wp-2fa-button-secondary modal_cancel"><?php \esc_attr_e( 'Cancel', 'wp-2fa' ); ?></a>
260 </div>
261 </div>
262 <div class="step-setting-wrapper" data-step-title="<?php \esc_html_e( 'Verify configuration', 'wp-2fa' ); ?>">
263 <div class="mb-20">
264 <?php echo \wp_kses_post( WP2FA::get_wp2fa_white_label_setting( 'method_verification_totp_pre', true ) ); ?>
265 </div>
266 <fieldset>
267 <label for="2fa-totp-authcode">
268 <?php \esc_html_e( 'Authentication Code', 'wp-2fa' ); ?>
269 <input type="tel" name="wp-2fa-totp-authcode" id="wp-2fa-totp-authcode" class="input" value="" size="20" pattern="[0-9]*" autocomplete="off"/>
270 <script>
271 const totp_authcode = document.getElementById('wp-2fa-totp-authcode');
272 totp_authcode.addEventListener('input', function() {
273 this.value = this.value.trim();
274 });
275 </script>
276 </label>
277 <div class="verification-response"></div>
278 </fieldset>
279 <input type="hidden" name="wp-2fa-totp-key" value="<?php echo \esc_attr( TOTP::get_totp_decrypted() ); ?>" />
280
281 <a href="#" class="modal__btn button button-primary wp-2fa-button-primary" data-validate-authcode-ajax <?php echo WP_Helper::create_data_nonce( 'wp-2fa-validate-authcode' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>><?php \esc_html_e( 'Validate & Save', 'wp-2fa' ); ?></a>
282 <button class="modal__btn wp-2fa-button-secondary button button-secondary wp-2fa-button-secondary" data-close-2fa-modal aria-label="Close this dialog window"><?php \esc_html_e( 'Cancel', 'wp-2fa' ); ?></button>
283 </div>
284
285 <?php
286 }
287
288 /**
289 * Settings page and first time wizard settings render
290 *
291 * @param array $methods - Array with all the methods in which we have to add this one.
292 * @param boolean $setup_wizard - Is that the first time setup wizard.
293 * @param string $data_role - Additional HTML data attribute.
294 * @param mixed $role - Name of the role.
295 *
296 * @return array - The array with the methods with all the methods wizard steps.
297 *
298 * @since 2.6.0
299 */
300 public static function totp_wizard_settings( array $methods, bool $setup_wizard, string $data_role, $role = null ) {
301 $name_prefix = WP_2FA_POLICY_SETTINGS_NAME;
302 $role_id = '';
303 if ( null !== $role && '' !== trim( (string) $role ) ) {
304 $name_prefix .= "[{$role}]";
305 $data_role = 'data-role="' . $role . '"';
306 $role_id = '-' . $role;
307 }
308 \ob_start();
309 ?>
310 <div id="<?php echo \esc_attr( TOTP::METHOD_NAME ); ?>-method-wrapper" class="method-wrapper">
311 <?php echo self::hidden_order_setting( $role ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
312 <label for="totp<?php echo \esc_attr( $role_id ); ?>" style="margin-bottom: 0 !important;">
313 <input type="checkbox" id="totp<?php echo \esc_attr( $role_id ); ?>" name="<?php echo \esc_attr( $name_prefix ); ?>[enable_totp]" value="enable_totp"
314 <?php echo $data_role; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
315
316 <?php if ( null !== $role && ! empty( $role ) ) { ?>
317 <?php \checked( TOTP::POLICY_SETTINGS_NAME, Role_Settings_Controller::get_setting( $role, TOTP::POLICY_SETTINGS_NAME ), true ); ?>
318 <?php
319 } else {
320 $use_role_setting = null;
321 if ( null === $role || '' === trim( (string) $role ) ) {
322 $use_role_setting = \WP_2FA_PREFIX . 'no-user';
323 }
324
325 $enabled_settings = Settings::get_role_or_default_setting( TOTP::POLICY_SETTINGS_NAME, $use_role_setting, $role, true );
326 ?>
327 <?php \checked( $enabled_settings, TOTP::POLICY_SETTINGS_NAME ); ?>
328 <?php } ?>
329 >
330 <?php \esc_html_e( 'One-time code via 2FA App (TOTP) - ', 'wp-2fa' ); ?><a href="https://melapress.com/support/kb/wp-2fa-configuring-2fa-apps/?&utm_source=plugin&utm_medium=link&utm_campaign=wp2fa" target="_blank" rel=noopener><?php \esc_html_e( 'complete list of supported 2FA apps.', 'wp-2fa' ); ?></a>
331 </label>
332 <?php
333 if ( $setup_wizard ) {
334 echo '<p class="description">';
335 printf(
336 /* translators: link to the knowledge base website */
337 \esc_html__( 'When using this method, users will need to configure a 2FA app to get the one-time login code. The plugin supports all standard 2FA apps. Refer to the %s for more information. Allowing users to set up a secondary 2FA method is highly recommended. You can do this in the next step of the wizard. This will allow users to log in using an alternative method should they, for example lose access to their phone.', 'wp-2fa' ),
338 '<a href="https://melapress.com/support/kb/wp-2fa-configuring-2fa-apps/?&utm_source=plugin&utm_medium=link&utm_campaign=wp2fa" target="_blank">' . \esc_html__( 'guide on how to set up 2FA apps', 'wp-2fa' ) . '</a>'
339 );
340 echo '</p>';
341 }
342 if ( ! $setup_wizard ) {
343 echo '<p class="description">';
344 printf(
345 /* translators: link to the knowledge base website */
346 \esc_html__( 'Refer to the %s for more information on how to setup these apps and which apps are supported.', 'wp-2fa' ),
347 '<a href="https://melapress.com/support/kb/wp-2fa-configuring-2fa-apps/?&utm_source=plugin&utm_medium=link&utm_campaign=wp2fa" target="_blank">' . \esc_html__( 'guide on how to set up 2FA apps', 'wp-2fa' ) . '</a>'
348 );
349 echo '</p>';
350 }
351 ?>
352 </div>
353 <?php
354 $output = ob_get_contents();
355 ob_end_clean();
356
357 $methods[ self::get_order( $role, $methods ) ] = $output;
358
359 return $methods;
360 }
361
362 /**
363 * Prints the form that prompts the user to authenticate.
364 *
365 * @param \WP_User $user - \WP_User object of the logged-in user.
366 *
367 * @since 2.6.0
368 */
369 public static function totp_authentication_page( $user ) {
370 require_once ABSPATH . '/wp-admin/includes/template.php';
371 ?>
372 <?php
373 if ( 'use-custom' == WP2FA::get_wp2fa_white_label_setting( 'use_custom_2fa_message' ) ) {
374 echo WP2FA::get_wp2fa_white_label_setting( 'custom-text-app-code-page', true ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
375 } else {
376 echo WP2FA::get_wp2fa_white_label_setting( 'default-text-code-page', true ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
377 }
378 ?>
379 <p>
380 </br>
381 <label for="authcode"><?php \esc_html_e( 'Authentication Code:', 'wp-2fa' ); ?></label>
382 <input type="tel" name="authcode" id="authcode" class="input" value="" size="20" pattern="[0-9]*" autocomplete="off" />
383 <script>
384 const authcode = document.getElementById('authcode');
385 authcode.addEventListener('input', function() {
386 this.value = this.value.trim();
387 });
388 </script>
389 </p>
390 <?php
391 }
392 }
393 }