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();