Description
Most front-end login forms put the email and password on one screen. Big consumer
sites (Amazon, Google, PayPal…) split it in two: you type your email, press
Continue, and only then are you asked for your password.
T2F Two-Screen Login Form brings that flow to WordPress as a shortcode you can drop on
any page:
[two_step_login]
How it works
- Step 1 — identifier. The visitor enters their email (optionally username too)
and presses Continue. By default this transition happens entirely in the
browser — no server request — so the endpoint only ever sees the final submit. - Step 2 — password. The email is shown with a Change link back to step 1.
The visitor enters their password, optionally ticks Remember me, and signs in. - On success the browser is redirected (to
?redirect_to=if present and on-site,
otherwise to the page you configure — the WooCommerce account page by default,
or the site home).
Everything happens without a full page reload until the final redirect.
Keeping the load down
Because step 1 is resolved client-side, a normal sign-in makes exactly one
admin-ajax.php call. Failed password attempts are rate-limited per IP address
and per email in a short rolling window before WordPress authentication runs,
and an off-screen honeypot field plus a minimum fill-time check drop obvious bot
submissions without the (CPU-heavy) password hash. Together these keep
credential-stuffing traffic from turning the login page into a load problem.
(When Unknown accounts is set to reveal missing accounts, step 1 still needs a
server round trip, since that answer can only come from the database.)
Privacy
By default the two possible step-1 responses are identical whether or not an account
exists, and failed logins return a single generic message — so the form cannot be
used to discover which email addresses have accounts. A setting lets you turn on
explicit “no account found” messages if you prefer Amazon’s behaviour.
Settings (Settings Two-Screen Login)
- First step accepts — email only, or email or username.
- Unknown accounts — stay silent (default) or say when no account matches.
- Rate limiting — throttle repeated failed logins per IP / email (on by default).
- Redirect after login — a URL, or blank for the account page / home.
- Lost-password URL — a URL, or blank for the default WordPress reset page.
For developers
add_filter( 'tslf_redirect_url', function ( $url ) { return home_url( '/dashboard/' ); } );
add_filter( 'tslf_lostpassword_url', function ( $url ) { return '/forgot/'; } );
add_filter( 'tslf_template', function ( $path, $name, $args ) { return $path; }, 10, 3 );
add_filter( 'tslf_logged_in_notice', function ( $html, $user ) { return $html; }, 10, 2 );
add_action( 'tslf_logged_in', function ( $user ) { /* ... */ } );
// Abuse mitigation.
add_filter( 'tslf_throttle', function ( $c ) { $c['id_max'] = 5; return $c; } ); // window, ip_max, id_max
add_filter( 'tslf_min_fill_ms', function () { return 2000; } );
add_filter( 'tslf_client_ip', function ( $ip ) { return $_SERVER['HTTP_CF_CONNECTING_IP'] ?? $ip; } );
The two step templates (templates/step-identifier.php, templates/step-password.php)
can be swapped with the tslf_template filter. Style hooks are plain classes
(.tslf, .tslf-form, .tslf-step, .tslf-error, .tslf-submit) and CSS custom
properties (--tslf-accent, --tslf-border, …).
Notes & limitations
- This is a front-end form for a page of your choosing. It does not replace
wp-login.php or change wp-admin. - No social login, no 2FA — those are separate concerns handled by other plugins.
- If you serve the login page from a full-page cache, exclude it (or its nonce may
age out for logged-out visitors after ~12 hours). - Translations (including Brazilian Portuguese) are managed on
translate.wordpress.org, not bundled with the plugin. The text domain is
t2f-two-screen-login.
Screenshots



Installation
- Install and activate the plugin.
- Create a page (e.g. “Sign in”) and add the shortcode
[two_step_login]. - Optionally review Settings Two-Screen Login.
- Point your theme’s “Log in” links at that page.
FAQ
-
Does it work with WooCommerce?
-
Yes. If WooCommerce is active and no redirect is configured, users land on the
My Account page after logging in. It does not otherwise depend on WooCommerce. -
Can visitors log in with their username instead of email?
-
Yes — set First step accepts to “Email address or username”.
-
JavaScript is required?
-
The stepped experience is JavaScript-driven, but the form also works with JS
disabled or blocked: it falls back to a full-page, step-by-step flow instead of
the in-browser transition. The bot timing check is skipped for no-JS submissions
(the honeypot and rate limiting still apply). -
Does it support “Remember me”?
-
Yes, via a checkbox on the password step.
-
Is the password ever exposed?
-
No. It is sent once, over your site’s normal (HTTPS) connection, to WordPress’s
standardwp_signon()authentication — the same function core uses.
Reviews
There are no reviews for this plugin.
Contributors & Developers
“T2F Two-Screen Login Form” is open source software. The following people have contributed to this plugin.
ContributorsTranslate “T2F Two-Screen Login Form” into your language.
Interested in development?
Browse the code, check out the SVN repository, or subscribe to the development log by RSS.
Changelog
1.1.0
- Fix: the login form’s
<form>has noactionattribute, so with JavaScript disabled or blocked it silently failed to sign anyone in — there was no server-side handler for that plain POST. A Post/Redirect/Get fallback now handles it and resumes the flow step by step. - Fix (security): a failed password attempt could lock a known account out of the form for every visitor, from a single IP, without ever supplying a password. The per-identifier rate limit is now scoped to (IP, identifier) instead of the identifier alone.
- Fix (security): with “First step accepts” set to email only, an identifier that didn’t resolve to an account could still reach WordPress authentication as a raw username, silently bypassing that setting for anyone who already knew a valid username + password.
- Fix:
?redirect_to=was silently dropped on the default sign-in flow, since the request that authenticates carries no query string of its own. It’s now carried forward as a hidden field. - Fix: the non-JS fallback’s redirect could 404 on a subdirectory install (e.g.
example.com/blog) by doubling the site’s own path prefix. - Fix: the rate limiter’s object-cache storage path had a non-atomic write that could race and undercount concurrent failed attempts under load; removed.
- Fix:
uninstall.phponly cleaned up the site it ran on, and missed the non-JS fallback’s own transients. It’s now multisite-aware and covers both transient prefixes.
1.0.0
- Initial release.
