diff --git a/providers/class-two-factor-backup-codes.php b/providers/class-two-factor-backup-codes.php index 5d2dcf88..bc443828 100644 --- a/providers/class-two-factor-backup-codes.php +++ b/providers/class-two-factor-backup-codes.php @@ -305,7 +305,13 @@ public function authentication_page( $user ) { */ public function validate_authentication( $user ) { $backup_code = isset( $_POST['two-factor-backup-code'] ) ? sanitize_text_field( wp_unslash( $_POST['two-factor-backup-code'] ) ) : false; - return $this->validate_code( $user, filter_var( $backup_code, FILTER_SANITIZE_STRING ) ); + $success = $this->validate_code( $user, filter_var( $backup_code, FILTER_SANITIZE_STRING ) ); + + if ( ! $success ) { + $this->log_failure( $user, $backup_code ); + } + + return $success; } /** diff --git a/providers/class-two-factor-email.php b/providers/class-two-factor-email.php index 5946c719..3783f5db 100644 --- a/providers/class-two-factor-email.php +++ b/providers/class-two-factor-email.php @@ -320,7 +320,13 @@ public function validate_authentication( $user ) { // Ensure there are no spaces or line breaks around the code. $code = trim( sanitize_text_field( $_REQUEST['two-factor-email-code'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended, handled by the core method already. - return $this->validate_token( $user->ID, $code ); + $success = $this->validate_token( $user->ID, $code ); + + if ( ! $success ) { + $this->log_failure( $user, $code ); + } + + return $success; } /** diff --git a/providers/class-two-factor-fido-u2f.php b/providers/class-two-factor-fido-u2f.php index 330451c2..3c272f53 100644 --- a/providers/class-two-factor-fido-u2f.php +++ b/providers/class-two-factor-fido-u2f.php @@ -212,10 +212,16 @@ public function validate_authentication( $user ) { self::update_security_key( $user->ID, $reg ); - return true; + $success = true; } catch ( Exception $e ) { - return false; + $success = false; + } + + if ( ! $success ) { + $this->log_failure( $user, $response ); } + + return $success; } /** diff --git a/providers/class-two-factor-provider.php b/providers/class-two-factor-provider.php index a2f9be06..6ff66c23 100644 --- a/providers/class-two-factor-provider.php +++ b/providers/class-two-factor-provider.php @@ -72,6 +72,43 @@ public function pre_process_authentication( $user ) { */ abstract public function validate_authentication( $user ); + /** + * Logs the failed authentication. + * + * @param WP_User $user WP_User object of the user trying to login. + * @param string|false $code The code used to authenticate, if available. + * + * @since 0.9.0 + * + * @return void + */ + public function log_failure( $user, $code = false ) { + /** + * This action is triggered when a Two Factor validation fails. + * + * @param WP_User $user WP_User object of the user trying to login. + * @param string|false $code The code used to authenticate, if available. + * @param Two_Factor_Provider $this The Provider class used during the authentication. + */ + do_action( 'two_factor_user_login_failed', $user, $code, $this ); + + /* translators: %1$d: the user's ID %2$s: the code used to authenticate */ + $log_message = sprintf( esc_html__( 'The user %1$s (ID: %2$d) failed to login using the code "%3$s"', 'two-factor' ), esc_html( $user->user_login ), $user->ID, esc_html( $code ) ); + + /** + * This filter is triggered to checke whether it's needed to log the authentication failure. + * + * @param boolean $should_log Whether or not the authentication failure should be logged. + * @param WP_User $user WP_User object of the user trying to login. + * @param string|false $code The code used to authenticate, if available. + * @param string $log_message The generated log message. + * @param Two_Factor_Provider $this The Provider class used during the authentication. + */ + if ( apply_filters( 'two_factor_log_failure', true, $user, $code, $log_message, $this ) ) { + error_log( $log_message ); + } + } + /** * Whether this Two Factor provider is configured and available for the user specified. * diff --git a/providers/class-two-factor-totp.php b/providers/class-two-factor-totp.php index 4d5a2828..046e6a65 100644 --- a/providers/class-two-factor-totp.php +++ b/providers/class-two-factor-totp.php @@ -288,14 +288,19 @@ public function admin_notices( $user_id ) { * @return bool Whether the user gave a valid code */ public function validate_authentication( $user ) { + $success = false; if ( ! empty( $_REQUEST['authcode'] ) ) { - return $this->is_valid_authcode( + $success = $this->is_valid_authcode( $this->get_user_totp_key( $user->ID ), sanitize_text_field( $_REQUEST['authcode'] ) ); } - return false; + if ( ! $success ) { + $this->log_failure( $user, ! empty( $_REQUEST['authcode'] ) ? sanitize_text_field( $_REQUEST['authcode'] ) : false ); + } + + return $success; } /**