Emailed Login Code for Staff Accounts
Adds a second step for administrators and editors: after the correct password, WordPress emails a 6-digit code that must be entered to finish logging in.
Opens WPIDE on
Code
// Roles that need the emailed code.
$wpide_code_roles = array( 'administrator', 'editor' );
$wpide_code_minutes = 10;
add_action( 'login_form', function () {
if ( isset( $_POST['log'] ) || isset( $_GET['wpide_code'] ) ) {
echo '<p><label for="wpide_login_code">Login code (if we emailed you one)</label><input type="text" name="wpide_login_code" id="wpide_login_code" class="input" inputmode="numeric" autocomplete="one-time-code" pattern="[0-9]*" maxlength="6"></p>';
}
} );
add_filter( 'authenticate', function ( $user ) use ( $wpide_code_roles, $wpide_code_minutes ) {
if ( ! $user instanceof WP_User || ! array_intersect( $wpide_code_roles, (array) $user->roles ) ) {
return $user;
}
// Only the interactive login form gets the second step (not XML-RPC or application passwords).
if ( ! isset( $_POST['log'], $_POST['pwd'] ) ) {
return $user;
}
$entered = isset( $_POST['wpide_login_code'] ) ? preg_replace( '/\D/', '', (string) wp_unslash( $_POST['wpide_login_code'] ) ) : '';
$stored = get_user_meta( $user->ID, 'wpide_login_code', true );
if ( $entered && is_array( $stored ) && time() < (int) $stored['expires'] && (int) $stored['tries'] < 5 ) {
if ( hash_equals( $stored['hash'], wp_hash( $entered ) ) ) {
delete_user_meta( $user->ID, 'wpide_login_code' );
return $user;
}
$stored['tries']++;
update_user_meta( $user->ID, 'wpide_login_code', $stored );
return new WP_Error( 'wpide_code_wrong', '<strong>Error:</strong> That login code is not correct. Enter your password and the code again.' );
}
$code = (string) wp_rand( 100000, 999999 );
update_user_meta( $user->ID, 'wpide_login_code', array( 'hash' => wp_hash( $code ), 'expires' => time() + $wpide_code_minutes * MINUTE_IN_SECONDS, 'tries' => 0 ) );
wp_mail(
$user->user_email,
sprintf( '[%s] Your login code: %s', wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES ), $code ),
sprintf( "Your login code is %s\n\nIt expires in %d minutes. If you did not try to log in, change your password now.", $code, $wpide_code_minutes )
);
return new WP_Error( 'wpide_code_sent', '<strong>Check your email.</strong> We sent you a 6-digit login code. Enter your password again together with the code.' );
}, 99 );
Notes
Make sure staff can receive email from the site before turning this on. Codes expire after 10 minutes and are locked after 5 wrong tries.
How to add this snippet
- Install WPIDE on your WordPress site (it is free) and open Code Snippets.
- Use snippet above adds it from the library in one click. Or copy the code into a new snippet with the same code type.
- It is added inactive: review the code and settings, then switch it on.