Yes there is an authenticate
filter you can hook into in the wp_authenticate
function, and return a WP_Error
if the login fails.
/**
* Filters whether a set of user login credentials are valid.
*
* A WP_User object is returned if the credentials authenticate a user.
* WP_Error or null otherwise.
*
* @since 2.8.0
* @since 4.5.0 `$username` now accepts an email address.
*
* @param null|WP_User|WP_Error $user WP_User if the user is authenticated.
* WP_Error or null otherwise.
* @param string $username Username or email address.
* @param string $password User password
*/
$user = apply_filters( 'authenticate', null, $username, $password );
This is exactly how WordPress internally validates username and password, these are the default filters used:
add_filter( 'authenticate', 'wp_authenticate_username_password', 20, 3 );
add_filter( 'authenticate', 'wp_authenticate_email_password', 20, 3 );
add_filter( 'authenticate', 'wp_authenticate_spam_check', 99 );
I recommend setting your filter to priority of 10, or something less than 20 (which is when username/password is checked), so your check is ran first.
If your test passes, just return the first variable passed, for example:
add_filter( 'authenticate', 'smyles_check_custom_auth', 10, 3 );
function smyles_check_custom_auth( $user, $username, $password ){
$otp_check = false;
if( $otp_check ){
return $user;
}
return new WP_Error( __( 'OTP Check failed' ) );
}