ID !== get_current_user_id()) { return; } $role_requires_2fa = $this->role_requires_2fa($user); $role_requires_passkey_2fa = $this->role_requires_passkey_2fa($user); $has_registered_passkey = $this->user_has_registered_passkey($user->ID); $is_2fa_enabled = get_user_meta($user->ID, '_acemedia_2fa_enabled', true); $secret = get_user_meta($user->ID, '_acemedia_2fa_secret', true); $selected_method = get_user_meta($user->ID, '_acemedia_2fa_method', true) ?: 'email'; if ($role_requires_passkey_2fa) { $selected_method = 'passkey'; } ?>

/>

validate_2fa_fields($errors, true, $user); if ($errors->has_errors()) { return false; } $role_requires_passkey_2fa = $this->role_requires_passkey_2fa($user); $is_2fa_enabled = isset($_POST['acemedia_2fa_enabled']) ? 1 : 0; if ($this->role_requires_2fa($user)) { $is_2fa_enabled = 1; } update_user_meta($user_id, '_acemedia_2fa_enabled', $is_2fa_enabled); // If 2FA is disabled, reset the setup status if (!$is_2fa_enabled) { delete_user_meta($user_id, '_acemedia_2fa_setup_complete'); delete_user_meta($user_id, '_acemedia_2fa_secret'); delete_user_meta($user_id, '_acemedia_2fa_trusted_devices'); } $selected_method = isset($_POST['acemedia_2fa_method']) ? sanitize_text_field(wp_unslash($_POST['acemedia_2fa_method'])) : 'email'; $allowed_methods = ['email', 'auth_app', 'passkey']; if (!in_array($selected_method, $allowed_methods, true)) { $selected_method = 'email'; } if ($role_requires_passkey_2fa) { $selected_method = 'passkey'; } elseif ($selected_method === 'passkey' && !$this->user_has_registered_passkey($user_id)) { $selected_method = 'email'; } update_user_meta($user_id, '_acemedia_2fa_method', $selected_method); if ($is_2fa_enabled) { update_user_meta($user_id, '_acemedia_2fa_setup_complete', true); } } public function validate_2fa_fields($errors, $update, $user) { if (!isset($_POST['acemedia_2fa_settings_present']) || (int) $user->ID !== get_current_user_id()) { return; } if (!($errors instanceof \WP_Error) || !is_object($user) || empty($user->ID)) { return; } // Core passes the editable stdClass here; load roles from the stored account. $user = get_userdata((int) $user->ID); if (!($user instanceof \WP_User)) { return; } if (!current_user_can('edit_user', $user->ID)) { return; } $requested_enabled = isset($_POST['acemedia_2fa_enabled']); $requested_method = isset($_POST['acemedia_2fa_method']) ? sanitize_text_field(wp_unslash($_POST['acemedia_2fa_method'])) : 'email'; $allowed_methods = ['email', 'auth_app', 'passkey']; if (!in_array($requested_method, $allowed_methods, true)) { $requested_method = 'email'; } if ($this->role_requires_2fa($user) && !$requested_enabled) { $errors->add('acemedia_role_requires_2fa', __('Your role requires two-factor authentication.', 'acemedia-login-block')); } if ($requested_enabled && $requested_method === 'auth_app' && ( !get_user_meta($user->ID, '_acemedia_2fa_setup_complete', true) || get_user_meta($user->ID, '_acemedia_2fa_method', true) !== 'auth_app' )) { $secret = \acemedia_login_block_decrypt(get_user_meta($user->ID, '_acemedia_2fa_secret', true)); $code = isset($_POST['acemedia_2fa_setup_code']) ? sanitize_text_field(wp_unslash($_POST['acemedia_2fa_setup_code'])) : ''; if (!$secret || !Two_Factor::get_instance()->verify_qr_code($secret, $code)) { $errors->add('acemedia_setup_code_required', __('Prepare your authentication app and enter a valid code before saving.', 'acemedia-login-block')); } } $role_requires_passkey_2fa = $this->role_requires_passkey_2fa($user); $has_registered_passkey = $this->user_has_registered_passkey($user->ID); if ($role_requires_passkey_2fa) { if (!$requested_enabled) { $errors->add('acemedia_passkey_role_requires_2fa', __('Your role requires passkey-based 2FA. Keep 2FA enabled for this account.', 'acemedia-login-block')); } if ($requested_method !== 'passkey') { $errors->add('acemedia_passkey_role_requires_method', __('Your role requires passkey-based 2FA. Select Passkey as the 2FA method.', 'acemedia-login-block')); } if (!$has_registered_passkey) { $errors->add('acemedia_passkey_role_requires_registration', __('Your role requires passkey-based 2FA, but no passkey is registered. Add one in the “Passkeys / Security Keys” section and save again.', 'acemedia-login-block')); } return; } if ($requested_enabled && $requested_method === 'passkey' && !$has_registered_passkey) { $errors->add('acemedia_passkey_method_requires_registration', __('Please register at least one passkey in the “Passkeys / Security Keys” section before selecting Passkey as your 2FA method.', 'acemedia-login-block')); } } private function role_requires_2fa($user) { foreach ($user->roles as $role) { if ( get_option("acemedia_2fa_enabled_{$role}", false) || get_option("acemedia_passkey_2fa_required_{$role}", false) ) { return true; } } return false; } private function role_requires_passkey_2fa($user) { foreach ($user->roles as $role) { if (get_option("acemedia_passkey_2fa_required_{$role}", false)) { return true; } } return false; } private function user_has_registered_passkey($user_id) { $passkeys = get_user_meta($user_id, '_acemedia_passkeys', true); return is_array($passkeys) && !empty($passkeys); } } // Initialize the user profile handler new User_Profile();