Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 38 additions & 12 deletions class-two-factor-core.php
Original file line number Diff line number Diff line change
Expand Up @@ -695,18 +695,44 @@ public static function get_available_providers_for_user( $user = null ) {
* Possible enhancement: add a filter to change the fallback method?
*/
if ( empty( $enabled_providers ) && $user_providers_raw ) {
if ( isset( $providers['Two_Factor_Email'] ) ) {
// Force Emailed codes to 'on'.
$enabled_providers[] = 'Two_Factor_Email';
} else {
return new WP_Error(
'no_available_2fa_methods',
__( 'Error: You have Two Factor method(s) enabled, but the provider(s) no longer exist. Please contact a site administrator for assistance.', 'two-factor' ),
array(
'user_providers_raw' => $user_providers_raw,
'available_providers' => array_keys( $providers ),
)
);
// Determine whether the filter intentionally cleared the list, or
// whether the providers are genuinely missing/removed. Only apply
// the fallback filter in the former case — if providers no longer
// exist, the fail-safe always applies to prevent failing open.
$unfiltered = array_intersect( (array) $user_providers_raw, array_keys( $providers ) );

/**
* Filters whether the email provider fallback is applied when a user's
* enabled provider list resolves to empty but they have providers configured
* in user meta. Return false to disable the fallback and allow an empty
* provider list to pass through — for example, to bypass two-factor for
* trusted IP addresses.
*
* This filter only runs when the configured providers still exist. If
* providers are genuinely missing or removed, the fail-safe always applies
* regardless of this filter.
*
* @since 0.17.0
*
* @param bool $apply_fallback Whether to apply the email fallback. Default true.
* @param int $user_id The user ID.
*/
$apply_fallback = empty( $unfiltered ) || apply_filters( 'two_factor_email_fallback_enabled', true, $user->ID );

if ( $apply_fallback ) {
if ( isset( $providers['Two_Factor_Email'] ) ) {
// Force Emailed codes to 'on'.
$enabled_providers[] = 'Two_Factor_Email';
Comment on lines +702 to +725
Copy link

Copilot AI Apr 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new two_factor_email_fallback_enabled behavior changes the outcome of get_available_providers_for_user() when a valid provider list is intentionally cleared via two_factor_enabled_providers_for_user, but there’s no unit test coverage validating (1) fallback remains enabled by default and (2) the fallback can be disabled via the new filter while still preserving the existing fail-safe when providers are genuinely missing. Please add/extend tests (likely in tests/class-two-factor-core.php, near test_deprecated_provider_for_user) to cover these cases so regressions are caught.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nimesh-xecurify would you be able to support on unit test extension here?

} else {
return new WP_Error(
'no_available_2fa_methods',
__( 'Error: You have Two Factor method(s) enabled, but the provider(s) no longer exist. Please contact a site administrator for assistance.', 'two-factor' ),
array(
'user_providers_raw' => $user_providers_raw,
'available_providers' => array_keys( $providers ),
)
);
}
}
}

Expand Down
1 change: 1 addition & 0 deletions readme.txt
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ Here is a list of action and filter hooks provided by the plugin:
- `two_factor_providers` filter overrides the available two-factor providers such as email and time-based one-time passwords. Array values are PHP classnames of the two-factor providers.
- `two_factor_providers_for_user` filter overrides the available two-factor providers for a specific user. Array values are instances of provider classes and the user object `WP_User` is available as the second argument.
- `two_factor_enabled_providers_for_user` filter overrides the list of two-factor providers enabled for a user. First argument is an array of enabled provider classnames as values, the second argument is the user ID.
- `two_factor_email_fallback_enabled` filter controls whether the email provider fallback is applied when a user's enabled provider list is empty but providers are configured in user meta. Return `false` to disable the fallback (e.g. to bypass two-factor for trusted IP addresses). The user ID is available as the second argument.
- `two_factor_user_authenticated` action which receives the logged in `WP_User` object as the first argument for determining the logged in user right after the authentication workflow.
- `two_factor_user_api_login_enable` filter restricts authentication for REST API and XML-RPC to application passwords only. Provides the user ID as the second argument.
- `two_factor_email_token_ttl` filter overrides the time interval in seconds that an email token is considered after generation. Accepts the time in seconds as the first argument and the ID of the `WP_User` object being authenticated.
Expand Down
Loading