J. Doe J. Doe - 4 months ago 13
PHP Question

Do not submit form after wrong code

I am working on a WordPress plugin which adds an auth code to the login form.

This is the process of checking if the auth code is valid:

add_action( 'wp_authenticate', 'authcode_check', 5 );
function authcode_check($username) {
$options = get_option( 'authcode_settings' );
if(!empty($options['code'])) {
global $wpdb;

if ( !username_exists( $username ) ) {
return;
}

$set_code = $options['code'];
$submit_code = $_POST['auth_key'];

if(empty($submit_code)) {
add_filter( 'login_errors', function( $error ) {$error = '<strong>ERROR</strong>: Authentication code cannot be empty.';return $error;} );
return;
} elseif ( ! ( $set_code == $submit_code ) ) {
add_filter( 'login_errors', function( $error ) {$error = '<strong>ERROR</strong>: Authentication code is invalid.';return $error;} );
return;
}
}
}


The problem is; when the user enters their WordPress name and password correctly, but not the auth code, the form still submits and log the user in.

I tried
return false
but that didn't work.

Is there any way to prevent the form from logging the user in when they have entered a wrong auth code?

Answer

Working Example

Updated after chat with @J.Doe

We can hook into the login_form hook, to display the input for the authentication code:

/**
 * 'Authentication Code' Input
 */
add_action( 'login_form', function() 
{
    // Fetch the stored code
    $options = get_option( 'authcode_settings' );

    // Display code input
    if( ! empty( $options['code'] ) ) printf(
        '<p class="login-authenticate">
            <label for="auth_key">%s</label>
            <input type="text" name="so38551606_auth_key" id="so38551606_auth_key" 
                   class="input" value="" size="20" autocomplete="off" />
        </p>',
        esc_html__( 'Authentication Code', 'mydomain' )
    );
} );

You can hook into the authenticate filter, within the wp_authenticate() function, for the validating part:

/**
 * Validate 'Authentication Code' Input
 */
add_filter( 'authenticate', function( $user )
{
    // Fetch stored code value
    $options = get_option( 'authcode_settings' );

    // Fetch the user's code input
    $submit_code = filter_input( INPUT_POST, 'so38551606_auth_key',
        FILTER_SANITIZE_STRING );

    // Validation's logic       
    $is_valid_auth_code = ! empty( $options['code'] ) 
        && ( $options['code'] === $submit_code );

    // Valid auth code  
    if( $is_valid_auth_code )
        return $user;

    // Add an unvalid auth code error
    if( is_wp_error( $user ) )
        $user->add( 
            'invalid_auth_code', 
            sprintf(
            '<strong>%s</strong>: %s',
            esc_html__( 'ERROR', 'mydomain' ),
            esc_html__( 'Authentication code is invalid.', 'mydomain' )
        )
    ); 
    // Create a new auth code error
    else
        $user = new WP_Error(
            'invalid_auth_code', 
            sprintf(
                '<strong>%s</strong>: %s',
                esc_html__( 'ERROR', 'mydomain' ),
                esc_html__( 'Authentication code is invalid.', 'mydomain' )
            )
        ); 

    return $user;
}, 100 );

Here we use a priority of 100 because we want to run it after the default callbacks of:

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

We prefixed the POST variable with so38551606_ to avoid possible name collision.

Output example:

auth input key validation example

Comments