# 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.

- Type: PHP
- Runs: Run everywhere
- Category: [Login](https://wpide.com/library/category/login/)
- Tags: login, two-factor, security
- Author: WPIDE Team
- Installs: 0
- Updated: 2026-10-28
- Page: https://wpide.com/library/email-login-code-for-staff/

## Code

```php
// 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 use it

Install the free WPIDE plugin (https://wordpress.org/plugins/wpide/), find "Emailed Login Code for Staff Accounts" in its snippet library and add it in one click; or create a new PHP snippet in WPIDE (or any code snippets plugin) and paste the code above. More snippets: https://wpide.com/library/
