U2 Secured Authenticator adds push-based two-factor authentication to WordPress login. After a user’s
password is verified, WordPress holds the session open (it does not complete it) and challenges for a
second factor:
- Tap-to-approve push — a number-match approval sent to the user’s phone via the U2 Secured app.
- TOTP fallback — a standard six-digit authenticator-app code, for when a push can’t reach the
phone. Verified on your own server: the RFC 6238 check runs in PHP against the secret this site
already holds (encrypted at rest), with the same ±1 window tolerance for clock drift, and no request
leaves the site to do it. Requires the sodium PHP extension and an encryption key (see
Installation); the plugin says so plainly on the profile screen when it isn’t available, rather than
silently dropping the option. Wrong codes are rate-limited per user (five inside fifteen minutes)
so a six-digit code cannot simply be ground through.
-
Recovery codes — ten single-use codes issued when a user links their device, verified entirely
locally against WordPress’s own database.
Both fallbacks are what make the failure mode of an unreachable U2 API survivable: the push path is
dead, but a linked user can still sign in with either, because neither makes an outbound call. They
share one field on the fallback screen, which accepts whichever the user has to hand.
- Role-based enforcement with a grace period — require two-factor for chosen roles (e.g.
Administrator) without locking out every matching user the instant it’s switched on. Each user gets a
configurable number of days to link before they’re actually blocked.
- Step-up re-auth — a small set of sensitive admin capabilities (e.g. installing plugins) can
require a fresh tap before they’re usable, even mid-session.
- Optional signed webhook — U2 Secured can POST the outcome of an approval straight to this site
the moment the user taps, so the login screen can answer from local state instead of asking again.
It is an optimisation and nothing more: every delivery must carry a valid signature to be believed,
and login works identically on a site that never receives one, because the browser asks U2 Secured
for the outcome itself anyway. Sites that aren’t reachable from the public internet should simply
skip it.
- XML-RPC / Application Passwords awareness — XML-RPC authenticates with the raw password and never
reaches the wp_login hook this plugin relies on, so it’s explicitly closed off. Application
passwords are a legitimate WordPress feature, so instead of being silently broken they’re surfaced to
the administrator as an admin notice.
Every enrolment is opt-in until an administrator turns on role-based enforcement. Every error path —
an unreachable U2 API, a missing encryption key, an unrecognised device, a removed or rotated-out API
key — is designed to fail closed: the safe outcome is “ask again” or “refuse,” never “let the
request through.” A user who requires a second factor on a site that can no longer perform one is
refused and told to ask an administrator; they are not quietly let in. See Verification status in
README.md for exactly what is and isn’t proven by the automated tests, and please read it before
enabling enforcement on a production site.
The escape hatch
If a user loses their phone and their recovery codes, the only way back in is an administrator
running, over SSH:
wp u2auth disable <user>
<user> accepts a WordPress user ID, login, or email address. This clears the plugin's local
two-factor state for that user (it does not touch anything on the U2 side, so it works even when the
U2 API is unreachable) and immediately admits them again — they can re-link from their profile once
back in. wp u2auth status <user> reports whether a user is currently linked and how many recovery
codes they have left, without changing anything.
External services
This plugin cannot work on its own: approving a login means asking a phone, and that request
travels through the U2 Secured Authenticator service at https://auth.u2secured.com. Installing
the plugin does not by itself send anything — nothing leaves your site until an administrator
enters an API key and a user links a phone.
When the site contacts the service
- When a user links their phone: to mint a one-time pairing code, then once every few seconds
until the code is redeemed or expires, and once more to read the resulting link.
- When a linked user opens their own profile screen, to read whether their phone is still
linked and able to receive approvals.
- When a linked user signs in, to ask their phone to approve it, and then once every two
seconds until they answer or the request expires.
- When a linked administrator performs a sensitive action (installing a plugin, editing a
user), for the same approval round trip.
- When a user unlinks their phone, to remove the link on the U2 side as well.
- When an administrator presses “Test connection” on the settings screen.
What is sent
- An identifier for the user, in the form
<random site id>|<numeric WordPress user ID>. The
site id is a random string generated once and stored in your database. Your users’ email
addresses, usernames and passwords are never sent, and neither is your site’s URL.
- Your site’s name (from Settings → General), so the person looking at their phone can see
which site is asking.
- For a sensitive admin action, the name of that action — for example “Install a plugin”.
- While a phone is being linked, the one-time pairing code the service itself just issued; while
an approval is outstanding, the id of that approval. Neither is derived from anything about
the user.
- Your site’s API key, to authenticate the request.
What is never sent
- Recovery codes and TOTP codes, and the TOTP secret itself. Both fallback factors are
checked inside PHP on your own server — recovery codes against hashes in your database, TOTP
against the secret your site holds (RFC 6238, ±1 window) — so no request is made to check
either, and the shared secret never leaves the site after enrolment. Earlier releases did send
the TOTP secret and the submitted code to the service to be checked; 0.3.0 stopped.
- Passwords, usernames, email addresses, and your site’s URL.
Service terms: https://auth.u2secured.com/terms
Privacy policy: https://auth.u2secured.com/privacy