PluginProbe ʕ •ᴥ•ʔ
WP 2FA – Two-factor authentication for WordPress / 4.0.0
WP 2FA – Two-factor authentication for WordPress v4.0.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 / passkeys / class-passkeys-wizard-steps.php
wp-2fa / includes / classes / Admin / Methods / passkeys Last commit date
assets 1 week ago format 1 week ago helpers 1 week ago class-ajax-passkeys.php 1 week ago class-api-register.php 1 week ago class-api-signin.php 1 week ago class-attestation-object.php 1 week ago class-authenticate-server.php 1 week ago class-authenticator-data.php 1 week ago class-byte-buffer.php 1 week ago class-chor-decoder.php 1 week ago class-passkeys-endpoints.php 1 week ago class-passkeys-user-profile.php 1 week ago class-passkeys-wizard-steps.php 1 week ago class-passkeys.php 1 week ago class-pending-2fa-helper.php 1 week ago class-source-repository.php 1 week ago class-web-authn-exception.php 1 week ago class-web-authn.php 1 week ago index.php 1 week ago
class-passkeys-wizard-steps.php
262 lines
1 <?php
2 /**
3 * Responsible for WP2FA user's Passkeys manipulation.
4 *
5 * @package wp2fa
6 * @subpackage methods-wizard
7 * @since 3.0.0
8 * @copyright 2026 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\Passkeys;
20 use WP2FA\Utils\Settings_Utils;
21 use WP2FA\Admin\Helpers\WP_Helper;
22 use WP2FA\Admin\Views\Wizard_Steps;
23 use WP2FA\Admin\Helpers\User_Helper;
24 use WP2FA\Admin\Methods\Traits\Methods_Wizards_Trait;
25
26 /**
27 * Class for handling passkeys codes.
28 *
29 * @since 3.0.0
30 *
31 * @package WP2FA
32 */
33 if ( ! class_exists( '\WP2FA\Methods\Wizards\PassKeys_Wizard_Steps' ) ) {
34 /**
35 * Passkeys code class, for handling passkeys (app) code generation and such.
36 *
37 * @since 3.0.0
38 */
39 class PassKeys_Wizard_Steps extends Wizard_Steps {
40
41 use Methods_Wizards_Trait;
42
43 /**
44 * Keeps the main class method name, so we can call it when needed.
45 *
46 * @var string
47 *
48 * @since 3.0.0
49 */
50 private static $main_class = Passkeys::class;
51
52 /**
53 * The default value of the method order in the wizards.
54 *
55 * @var integer
56 *
57 * @since 3.0.0
58 */
59 private static $order = 9;
60
61 /**
62 * Inits the class hooks
63 *
64 * @return void
65 *
66 * @since 2.4.0
67 */
68 public static function init() {
69 // \add_filter( WP_2FA_PREFIX . 'methods_modal_options', array( __CLASS__, 'passkeys_option' ), 10, 2 );
70 // \add_filter( WP_2FA_PREFIX . 'methods_re_configure', array( __CLASS__, 'passkeys_re_configure' ), 10, 2 );
71 // \add_filter( WP_2FA_PREFIX . 'methods_settings', array( __CLASS__, 'passkeys_wizard_settings' ), 10, 4 );
72 }
73
74 /**
75 * Shows the option to reconfigure email (if applicable)
76 *
77 * @param array $methods - Array of methods collected.
78 * @param string $role - The name of the role to show option to.
79 *
80 * @since 3.0.0
81 *
82 * @return array
83 */
84 public static function passkeys_re_configure( array $methods, string $role ): array {
85
86 if ( ! Passkeys::is_enabled() ) {
87 return $methods;
88 }
89 \ob_start();
90 ?>
91 <div class="option-pill">
92 <?php echo \wp_kses_post( WP2FA::contextual_reconfigure_text( WP2FA::get_wp2fa_white_label_setting( 'passkeys_reconfigure_intro', true ), User_Helper::get_user_object()->ID, Passkeys::METHOD_NAME ) ); ?>
93 <div class="wp2fa-setup-actions">
94 <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="wp-2fa-wizard-passkeys"><?php \esc_html_e( 'Reset Key', 'wp-2fa' ); ?></a>
95 </div>
96 </div>
97 <?php
98 $output = ob_get_contents();
99 ob_end_clean();
100
101 $methods[ self::get_order( $role, $methods ) ] = array(
102 'name' => self::$main_class::METHOD_NAME,
103 'output' => $output,
104 );
105
106 return $methods;
107 }
108
109 /**
110 * Shows the initial passkeys setup options based on enabled methods
111 *
112 * @param array $methods - Array of methods collected.
113 * @param string $role - The name of the role to show option to.
114 *
115 * @since 3.0.0
116 *
117 * @return array
118 */
119 public static function passkeys_option( array $methods, string $role ): array {
120 if ( Passkeys::is_enabled() ) {
121 \ob_start();
122 ?>
123 <div class="option-pill">
124 <label for="basic">
125 <input id="basic" name="wp_2fa_enabled_methods" type="radio" value="passkeys">
126 <?php echo \wp_kses_post( WP2FA::get_wp2fa_white_label_setting( 'passkeys-option-label', true ) ); ?>
127 <?php
128 $show = ( 'show_help_text' === WP2FA::get_wp2fa_white_label_setting( 'show_help_text' ) && WP2FA::get_wp2fa_white_label_setting( \esc_attr( Passkeys::METHOD_NAME ) . '-option-label-hint', true ) ) ?? false;
129 if ( $show ) {
130 echo '<br><span class="wizard-tooltip" data-tooltip-content="data-' . \esc_attr( Passkeys::METHOD_NAME ) . '-tooltip-content-wrapper">i</span>';
131 }
132 ?>
133 </label>
134 <?php
135 if ( $show ) {
136 echo '<p class="description tooltip-content-wrapper" data-' . \esc_attr( Passkeys::METHOD_NAME ) . '-tooltip-content-wrapper>' . \wp_kses_post( WP2FA::get_wp2fa_white_label_setting( \esc_attr( Passkeys::METHOD_NAME ) . '-option-label-hint', true ) ) . '</p>';
137 }
138 ?>
139 </div>
140 <?php
141 $output = ob_get_contents();
142 ob_end_clean();
143
144 $methods[ self::get_order( $role, $methods ) ] = $output;
145 }
146
147 return $methods;
148 }
149
150 /**
151 * Settings page and first time wizard settings render
152 *
153 * @param array $methods - Array with all the methods in which we have to add this one.
154 * @param boolean $setup_wizard - Is that the first time setup wizard.
155 * @param string $data_role - Additional HTML data attribute.
156 * @param mixed $role - Name of the role.
157 *
158 * @return array - The array with the methods with all the methods wizard steps.
159 *
160 * @since 3.0.0
161 */
162 public static function passkeys_wizard_settings( array $methods, bool $setup_wizard, string $data_role, $role = null ) {
163 $name_prefix = WP_2FA_POLICY_SETTINGS_NAME;
164 $role_id = '';
165 if ( null !== $role && '' !== trim( (string) $role ) ) {
166 $name_prefix .= "[{$role}]";
167 $data_role = 'data-role="' . $role . '"';
168 $role_id = '-' . $role;
169 }
170
171 /**
172 * Filter to check if the interface has to disable this method.
173 *
174 * @param bool - Should we disable the method - default false.
175 * @param Providers - The current method class.
176 * @param null|string - The role name for which the status must be checked.
177 *
178 * @since 2.9.2
179 */
180 $method_disabled_class = \apply_filters( WP_2FA_PREFIX . 'method_settings_disabled', false, self::$main_class, $role );
181
182 if ( $method_disabled_class ) {
183 $method_disabled_class = 'disabled';
184 }
185
186 \ob_start();
187 ?>
188 <div id="<?php echo \esc_attr( Passkeys::METHOD_NAME ); ?>-method-wrapper" class="method-wrapper">
189 <?php echo self::hidden_order_setting( $role ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
190 <label class="<?php echo \esc_html( $method_disabled_class ); ?>" for="passkeys<?php echo \esc_attr( $role_id ); ?>" style="margin-bottom: 0 !important;">
191 <input type="checkbox" id="passkeys<?php echo \esc_attr( $role_id ); ?>" name="<?php echo \esc_attr( $name_prefix ); ?>[enable_passkeys]" value="enable_passkeys"
192 <?php echo $data_role; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
193
194
195 <?php \checked( Passkeys::POLICY_SETTINGS_NAME, Settings_Utils::get_setting_role( $role, Passkeys::POLICY_SETTINGS_NAME ), true ); ?>
196
197 >
198 <?php \esc_html_e( 'Passkeys - ', '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>
199 </label>
200 <?php
201 if ( $setup_wizard ) {
202 echo '<p class="description">';
203 printf(
204 /* translators: link to the knowledge base website */
205 \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 configure a secondary 2FA method is highly recommended. You can configure this in the next step of the wizard. This allows users to log in using an alternative method if they lose access to their primary 2FA device, such as their phone.', 'wp-2fa' ),
206 '<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>'
207 );
208 echo '</p>';
209 }
210 if ( ! $setup_wizard ) {
211 echo '<p class="description">';
212 printf(
213 /* translators: link to the knowledge base website */
214 \esc_html__( 'Refer to the %s for more information on how to setup these apps and which apps are supported.', 'wp-2fa' ),
215 '<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>'
216 );
217 echo '</p>';
218 }
219 ?>
220 </div>
221 <?php
222 $output = ob_get_contents();
223 ob_end_clean();
224
225 $methods[ self::get_order( $role, $methods ) ] = $output;
226
227 return $methods;
228 }
229
230 /**
231 * Prints the form that prompts the user to authenticate.
232 *
233 * @param \WP_User $user - \WP_User object of the logged-in user.
234 *
235 * @since 3.0.0
236 */
237 public static function passkeys_authentication_page( $user ) {
238 require_once ABSPATH . '/wp-admin/includes/template.php';
239 ?>
240 <?php
241 if ( 'use-custom' == WP2FA::get_wp2fa_white_label_setting( 'use_custom_2fa_message' ) ) {
242 echo WP2FA::get_wp2fa_white_label_setting( 'custom-text-app-code-page', true ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
243 } else {
244 echo WP2FA::get_wp2fa_white_label_setting( 'default-text-code-page', true ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
245 }
246 ?>
247 <p>
248 </br>
249 <label for="authcode"><?php \esc_html_e( 'Authentication Code:', 'wp-2fa' ); ?></label>
250 <input type="tel" name="authcode" id="authcode" class="input" value="" size="20" pattern="[0-9]*" autocomplete="off" />
251 <script>
252 const authcode = document.getElementById('authcode');
253 authcode.addEventListener('input', function() {
254 this.value = this.value.trim();
255 });
256 </script>
257 </p>
258 <?php
259 }
260 }
261 }
262