All 22 Login Security sections

See every section

A second step at sign-in

Updated Login Security 1.6.0Tested with WordPress 7.1

Six digits from an authenticator app, after the password. It is the only thing here that helps against a password somebody else already has — reused somewhere that leaked, or typed into a convincing copy of your sign-in page. Counting wrong guesses cannot help with a right one.

Nothing is sent anywhere. No text messages, no account with anybody, no service. The app on the phone and this site share a secret and both look at the clock; that is the whole of it.

Setting one up#

Users → Profile, the Two-step sign-in section. Not on this plugin's own menu, because the people most likely to be asked to do this are editors and shop managers who cannot see a settings screen — and Profile is where anybody already goes to change their password. On a WooCommerce shop, customers have the same section in My Account → Two-step sign-in; see On a WooCommerce shop below.

  1. Press Set up a second step.
  2. Open an authenticator app and point it at the QR code. Any app will do: Google Authenticator, 1Password, Aegis, Bitwarden, the one built into your phone. If you cannot scan, the key is printed underneath to type in.
  3. Type the six digits the app is showing, and press Turn the second step on.

It is not switched on until a real code has been checked. A button that simply said "on" is how somebody ends up locked out having scanned nothing.

Then ten recovery codes are shown, once, at the top of the section, where the page opens. Print them or put them in a password manager. Each works once, and any of them signs you in without your phone. Make new recovery codes replaces all ten, and the old ones stop working.

Make new recovery codes and Turn the second step off each ask for your current password first, and a wrong one changes nothing. That stops somebody who finds a computer left signed in from switching it off, or from making recovery codes of their own and voiding the ones you printed. A wrong password there is counted like one typed at sign-in, so it is no way round the limit on guessing. It cannot help where the browser fills in your saved password for whoever is using it. Where another service handles sign-in and WordPress shows no password fields, it is not asked.

Every one of these is a button, and what it did is said at the top of the section. In earlier versions they were links, so opening one in a new tab could start or stop a set-up with nothing pressed. A link saved from then changes nothing now: it comes back to the section and says to use the button.

Once the six digits are right, the sign-in finishes as it would have without a second step: anything else on the site that does something when somebody signs in does it then. On a shop, that is WooCommerce bringing back the cart saved to the customer's account.

Requiring it of other people#

Anybody who can open wp-admin may set one up for their own account at any time, with nothing switched on anywhere. A shop's customers cannot open it, so they set one up on the My Account tab, which is there while a second step is required of anybody (see On a WooCommerce shop). Settings → A second step is about requiring it, which is a decision about other people:

  • People who can change the site — anybody who can edit a post or a setting. Your customers are never made to set one up. This is the default;
  • Everybody with an account — including customers. On a WooCommerce shop this reaches customers only through the My Account tab (below). WooCommerce keeps customers out of wp-admin, so before the tab existed a customer was never asked, whatever this said.

The settings screen tells you whether you have set one up, and says so plainly if you have not, because requiring it of everybody except yourself is the wrong way round.

What happens to somebody who has not. They are not refused entry — somebody who cannot get in cannot set one up either. They sign in as normal and are then sent to their profile, and kept there, until they have. The front of your site is untouched, so a shop that requires this of its staff has not asked anybody to stop serving customers.

Three things stay reachable throughout: their own profile, signing out, and — for anybody who can change settings — this plugin's own screens. That last one matters: an administrator who switches this on with no phone to hand can always switch it straight back off. Without it, the setting would be a trap whose only exit is the database.

On a WooCommerce shop#

WooCommerce sends anybody who cannot edit a post, manage the shop or view the admin dashboard away from wp-admin to My Account. So the plugin puts the second step there too: a Two-step sign-in tab after Account details, with the same controls as the profile, the QR code drawn on your own server.

  • Who sees the tab. Everybody, while a second step is required of anybody (Settings → A second step). With People who can change the site, a customer may set one up without being made to. While the tab is switched on, anybody who already has a second step, or is part way through setting one up, keeps it whatever Required of says, so they can manage it or switch it off. With the tab switched off nobody has it, and an administrator switches a customer's second step off from their profile.
  • Who is kept on it. With Everybody with an account, a customer who has not set one up is sent to the tab whenever they open My Account. Account details and Log out stay open. The cart, the checkout and the rest of the shop are left alone, so nobody is stopped from paying for an order over this. With Make them change it before doing anything else on, a customer whose password the rules refuse changes it on Account details first.
  • Signing in at My Account. A customer with a second step who signs in on the My Account page is asked for the six digits on the sign-in page (or the moved one), and then goes where WooCommerce's form would have sent them: the page it names, or the page the form was on, or My Account. Earlier versions showed them WordPress's own sign-in form instead, with the message that the username field was empty, and they had to sign in again there. Five wrong codes, a code left too long, or Start again take them back to the My Account sign-in form, not to a sign-in page they have never seen. On a network, a customer from another site of the network who signs in to this shop becomes a customer here, as WooCommerce does without a second step.
  • Its buttons. They post to the tab's own address in My Account, not to wp-admin, so they keep working on a shop that keeps customers out of wp-admin with another plugin or a password of its own.
  • Switching it on. Settings → A second step → Add a Two-step sign-in tab to My Account, shown on a shop only. On for a new site. A shop that had the plugin before the tab existed has it off, so no customer is sent anywhere after the update, and the Dashboard offers it under Worth doing next while a second step is required of anybody. A shop that requires it of everybody is told that its customers were not being asked. Any shop can switch it on in Settings at any time. Under the switch, Settings says what happens to customers as things stand: sent to the tab, able to set one up there, or not asked.
  • Its address. WordPress learns the tab's address once, on the next page opened in wp-admin (or the next WP-CLI command) after the tab is switched on. It learns it again the same way after WooCommerce itself has been switched off and on, and after anything has rebuilt the permalinks on a page without WooCommerce. A visitor's page never makes it learn the address, and neither do pages that a plugin such as Perfmatters or Plugin Organizer opens without WooCommerce. On a network, each site does this for itself. If the tab ever opens a page that cannot be found, open any page in wp-admin, then try again; Settings → Permalinks → Save Changes also teaches WordPress the address again.

An administrator can still switch a customer's second step off from their profile in wp-admin, and wp login-security two-factor off <user> still works. Nothing is sent anywhere: no text messages, no third party.

Seeing who has one#

Requiring a second step only reaches somebody the next time they open the admin, so an editor who has not signed in since you switched it on is still protected by a password alone. Three places show you who that is:

  • Users → All Users has a Second step column. Each person shows Set up, with how many recovery codes they have left underneath (in amber at two or fewer), Not set up, or Not set up, required when your setting requires it of them;
  • the No second step link above that list, with a count, narrows it to the people who can change the site and have not set one up. It is there whether or not you require a second step, and goes away once all of them have one. Where WordPress calls the site large (more than ten thousand accounts, and on a network that is counted across the whole network) the link is always there and has no number, the same way WordPress leaves the numbers off its own role links there;
  • Settings → A second step says how many of the people who can change the site have one, with a link to that same list. Where WordPress calls the site large it says there are too many to count and gives the link.

The link and the counts are about the people who can change the site, even when Required of is set to everybody with an account. The column still says Not set up, required for every customer then, and wp login-security two-factor list without --missing lists everybody who has one.

"People who can change the site" means anybody whose role can edit a post or change a setting, the same people Required of means. On a network the count and the list are per site: somebody who is an editor on one site and a subscriber on another is counted on the first only.

On the Dashboard, when a username being tried is a real account with no second step, the row says so and offers Send a password reset, which emails that person a link to choose a new password, and Open profile. The email goes only when the button is pressed: opened in a new tab or from an old link, the Dashboard asks first. A username that is somebody's email address counts as their account. Both buttons appear only for accounts you could edit from the Users screen, which on a network is normally only a super admin: a site's own administrator sees the pill and no buttons, as they see no Send password reset on the Users screen there. That list is about signing in: password reset requests are not in it.

None of these can switch a second step on for somebody else. They show you who to ask.

If somebody loses their phone#

Four ways, in the order they are quickest:

  1. A recovery code. That is what they are for.
  2. An administrator switches it off from that person's own profile, under Two-step sign-in, without their password. They can set it up again afterwards. A shop's customers are no different: their profile is in wp-admin under Users, even though they never see it.
  3. WP-CLI, when the person locked out is the administrator: wp login-security two-factor off rosa
  4. The plugin's way back in does not help here — it is about lockouts, not the second step. This is the one place it does not apply.

There is deliberately no way to set up a second step for somebody else, from the admin or from the command line. Holding somebody else's secret is being able to sign in as them. The one thing that switches it on for other people is bringing over the app they already set up with another plugin (below): that is their own secret, already on their phone, and nobody is ever shown it.

Skipping the code on a browser you choose#

Off on every site. Settings → A second step → Let people skip the code on a browser they choose adds a box to the code screen, on wp-login.php and on a shop's My Account sign-in: Do not ask on this browser for 30 days. Ticked, with the right code (from the app or a recovery code), that browser is remembered, and signing in on it again asks for the password and no code until the days are up. Any other browser is still asked.

It weakens the second step on that browser, which is why it is off and nothing on any screen suggests it: somebody who copies that browser's cookie and also has the password signs in without the phone. What keeps that small:

  • Never for some roles. Never for these roles starts with administrators ticked, because their accounts can change everything. Nobody in a ticked role sees the box, and ticking a role later ends the browsers its people have. On a network, a network administrator counts as an administrator.
  • The days. Remember for (days), 30 to start, anything from 1 to 90, counted from when the box was ticked. Lowering it shortens the ones already made; raising it does not lengthen them.
  • HTTPS only. The box is offered only on a sign-in page served over HTTPS. Settings says so when yours is not.
  • The cookie. Its name starts with __Host-, so the browser keeps it for this host only and sends it only over HTTPS. It is HttpOnly, so no script on a page can read it, and SameSite=Strict, so it goes only with a sign-in that starts on your site. The site keeps a keyed hash of it, never the cookie itself, so a copy of the database cannot be turned into one, and new salts in wp-config.php end every one of them, as they end every sign-in.
  • A copy works once at most. Each time it is used, the browser is given a new one. If an older one turns up after that, two browsers have had it, and that browser is forgotten for both. It is also refused from another kind of browser or system than the one it was made on.
  • It never stands in for the password. It is looked at only after the password was right. XML-RPC and application passwords are not changed by it.

It ends sooner when:

  • the password changes, however it changes: a reset, the profile, WooCommerce, WP-CLI or another plugin;
  • the second step is switched off, or set up again;
  • the person presses Forget (one browser) or Forget all under Remembered browsers on their profile or on the My Account tab. An administrator can do the same on that person's profile;
  • you press Forget every remembered browser in Settings, which asks everybody on the site for the code again;
  • you switch the setting off. Switched on again later, the browsers remembered before it was switched off still ask;
  • somebody at the server runs wp login-security two-factor forget <user>, which forgets that person's browsers on every site of a network.

Signing out keeps a browser remembered: that is the point of it. Log Out Everywhere Else on the profile signs the other browsers out but does not forget them, so for a lost laptop press Forget all as well, or change the password, which does both.

In the log, a sign-in on a remembered browser reads Signed in, with no code asked, on a browser this person asked to be remembered under it, and the one that ticked the box reads with the code, and asked for this browser to be remembered.

What is kept, per person and per site: the hash, a fingerprint of the password it was made under, the kind of browser and system (Firefox on Windows), and when it was made and until when. No internet address. Ten at most for a person on a site; the oldest go. Each site of a network has its own setting, and a browser remembered on one site is asked on the others.

Coming from Wordfence or Two Factor#

People who set up an authenticator app with Wordfence, Wordfence Login Security or Two Factor can keep it. Nobody scans a new QR code.

Settings → Bring second steps from another plugin appears when one of those plugins has left set-ups in the database, whether it is still active or not. It says how many people it found and lists them by username: who can be brought over, who already has a second step here (they keep theirs, untouched), and anybody whose secret could not be read. Bring them over from Wordfence (or Two Factor) asks first, then copies each secret into this plugin, sealed the way this plugin keeps its own. The codes the app shows go on working, and a code the other plugin has just accepted cannot be used here a second time.

  • Nothing of the other plugin's is changed. Its own table and its own user data are read and left exactly as they were, so going back is possible.
  • Running it again changes nothing. Anybody with a second step here is left alone, including the people brought over the first time.
  • Recovery codes do not come across. The old ones do not work here. Until a person has made new ones, every page they open in wp-admin says so, with a Make recovery codes link to their profile. There, Make new recovery codes asks for their password and shows the ten codes in the section, once. Nothing is made just by opening a page, so a page where WordPress hides notices (the block editor, WooCommerce's own pages) costs nothing. When this first came out, the first page opened made them and counted them as shown, even there. On a shop with the My Account tab, every page of My Account says the same and links to the tab, which has the same button. On a shop without the tab, a customer is shown ten new codes once, in My Account itself. They are never emailed.
  • Two set-ups for one person in Wordfence (left when two were made at the same moment): the one Wordfence itself asks for comes across, which is the first one it made.
  • Only authenticator apps come across. Wordfence's passkeys and Two Factor's email codes have to be set up again here.
  • Switch the other plugin's second step off afterwards. Until you do, people are asked for a code twice: the same app answers both.
  • A secret that is not between 10 and 64 bytes, or not readable at all, is left where it is and named on the screen. It is never guessed at.

The other plugin's codes must be the usual kind: six digits, every thirty seconds. Both plugins make only that kind. This plugin accepts a code thirty seconds either side of the right one, and Two Factor allowed two minutes, so a phone whose clock is more than half a minute out should be set right.

On a network, a person's second step is theirs everywhere: it lives with the account, which the whole network shares. With this plugin network-activated the import reaches everybody on the network. Switched on for one site, it reaches that site's members. From the screen it only reaches people whose account you may edit, which on a network means a network administrator.

From the command line: wp login-security two-factor import --from=wordfence --dry-run lists who would come across and changes nothing; without --dry-run it brings them. --from takes wordfence or two-factor.

XML-RPC, and the door that does not ask#

A second step is worth nothing if the password on its own still gets in somewhere else, and xmlrpc.php took it: one request carrying the username and the account password, and you were signed in as an administrator. The second step was true of the sign-in form and false of a URL one directory up.

So when an account has a second step, its account password no longer works over XML-RPC. The refusal is in your log, saying exactly that, because XML-RPC replaces every message with "Incorrect username or password" and would otherwise send somebody hunting for a password problem.

Application passwords still work, deliberately. They are the right mechanism for an app: made on purpose, one per application, revoked one at a time without touching the account password, and no use at all for signing in to the admin. Users → Profile → Application Passwords makes one. Jetpack signs its own XML-RPC calls with a token rather than a password and is unaffected either way.

The REST API was already safe — WordPress only accepts an application password there, never the account password. Checked rather than assumed.

What it is and is not#

  • a wrong code is counted and limited exactly like a wrong password, so guessing at six digits gets the same lockout;
  • a code works once. One read over a shoulder, or typed into a copy of your sign-in page and replayed, is no good a second time;
  • the secret is sealed in the database, using this site's own salts. A database copy on its own is not somebody else's second factor. It is not protection from anybody who has the files as well;
  • it does not help if somebody is already signed in on a machine they should not be. Users → Profile → Log Out Everywhere Else is what does.

Still stuck? Ask the developers who wrote it. We aim to reply within one business day.