{"id":367633,"date":"2026-09-14T17:55:47","date_gmt":"2026-09-14T17:55:47","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/two-step-login-form\/"},"modified":"2026-09-14T22:38:16","modified_gmt":"2026-09-14T22:38:16","slug":"t2f-two-screen-login","status":"publish","type":"plugin","link":"https:\/\/wordpress.org\/plugins\/t2f-two-screen-login\/","author":23564579,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"1.1.0","stable_tag":"1.1.0","tested":"7.1","requires":"5.6","requires_php":"7.4","requires_plugins":null,"header_name":"T2F Two-Screen Login Form","header_author":"T2F","header_description":"An Amazon-style two-screen login form: the visitor enters their email first, then their password on a second screen. Drop it on any page with the [two_step_login] shortcode.","assets_banners_color":"2271b1","last_updated":"2026-09-14 22:38:16","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"https:\/\/t2f.tech","header_author_uri":"https:\/\/t2f.tech\/","rating":0,"author_block_rating":0,"active_installs":0,"downloads":53,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"1.0.0":{"tag":"1.0.0","author":"t2ftech","date":"2026-09-14 17:55:36","revision":3695702},"1.1.0":{"tag":"1.1.0","author":"t2ftech","date":"2026-09-14 22:38:16","revision":3696011}},"upgrade_notice":[],"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3695701,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3695701,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256},"icon.svg":{"filename":"icon.svg","revision":3695701,"resolution":false,"location":"assets","locale":false}},"assets_banners":{"banner-1544x500-pt_BR.png":{"filename":"banner-1544x500-pt_BR.png","revision":3695701,"resolution":"1544x500","location":"assets","locale":"pt_BR","width":1544,"height":500},"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3695701,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250-pt_BR.png":{"filename":"banner-772x250-pt_BR.png","revision":3695701,"resolution":"772x250","location":"assets","locale":"pt_BR","width":772,"height":250},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3695701,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250},"banner.svg":{"filename":"banner.svg","revision":3695701,"resolution":false,"location":"assets","locale":false}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":["1.0.0","1.1.0"],"block_files":[],"assets_screenshots":{"screenshot-1.png":{"filename":"screenshot-1.png","revision":3695701,"resolution":"1","location":"assets","locale":"","width":969,"height":419},"screenshot-2.png":{"filename":"screenshot-2.png","revision":3695701,"resolution":"2","location":"assets","locale":"","width":1133,"height":558},"screenshot-3.png":{"filename":"screenshot-3.png","revision":3695701,"resolution":"3","location":"assets","locale":"","width":1385,"height":603}},"screenshots":{"1":"Step 1 \u2014 the visitor enters their email address.","2":"Step 2 \u2014 the password screen, with a \"Change\" link back to the email step.","3":"Settings -&gt; Two-Screen Login."}},"plugin_section":[],"plugin_tags":[710,13276,602,2331,80],"plugin_category":[38,43],"plugin_contributors":[280702,280703],"plugin_business_model":[],"class_list":["post-367633","plugin","type-plugin","status-publish","hentry","plugin_tags-authentication","plugin_tags-front-end-login","plugin_tags-login","plugin_tags-login-form","plugin_tags-shortcode","plugin_category-authentication","plugin_category-customization","plugin_contributors-t2ftech","plugin_contributors-thaissamendes","plugin_committers-t2ftech"],"banners":{"banner":"https:\/\/ps.w.org\/t2f-two-screen-login\/assets\/banner-772x250.png?rev=3695701","banner_2x":"https:\/\/ps.w.org\/t2f-two-screen-login\/assets\/banner-1544x500.png?rev=3695701","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":"https:\/\/ps.w.org\/t2f-two-screen-login\/assets\/icon.svg?rev=3695701","icon":"https:\/\/ps.w.org\/t2f-two-screen-login\/assets\/icon.svg?rev=3695701","icon_2x":false,"generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/t2f-two-screen-login\/assets\/screenshot-1.png?rev=3695701","caption":"Step 1 \u2014 the visitor enters their email address."},{"src":"https:\/\/ps.w.org\/t2f-two-screen-login\/assets\/screenshot-2.png?rev=3695701","caption":"Step 2 \u2014 the password screen, with a \"Change\" link back to the email step."},{"src":"https:\/\/ps.w.org\/t2f-two-screen-login\/assets\/screenshot-3.png?rev=3695701","caption":"Settings -&gt; Two-Screen Login."}],"raw_content":"<!--section=description-->\n<p>Most front-end login forms put the email and password on one screen. Big consumer\nsites (Amazon, Google, PayPal...) split it in two: you type your email, press\n<strong>Continue<\/strong>, and only then are you asked for your password.<\/p>\n\n<p><strong>T2F Two-Screen Login Form<\/strong> brings that flow to WordPress as a shortcode you can drop on\nany page:<\/p>\n\n<pre><code>[two_step_login]\n<\/code><\/pre>\n\n<h4>How it works<\/h4>\n\n<ol>\n<li><strong>Step 1 \u2014 identifier.<\/strong> The visitor enters their email (optionally username too)\nand presses <strong>Continue<\/strong>. By default this transition happens entirely in the\nbrowser \u2014 no server request \u2014 so the endpoint only ever sees the final submit.<\/li>\n<li><strong>Step 2 \u2014 password.<\/strong> The email is shown with a <strong>Change<\/strong> link back to step 1.\nThe visitor enters their password, optionally ticks <strong>Remember me<\/strong>, and signs in.<\/li>\n<li>On success the browser is redirected (to <code>?redirect_to=<\/code> if present and on-site,\notherwise to the page you configure \u2014 the WooCommerce account page by default,\nor the site home).<\/li>\n<\/ol>\n\n<p>Everything happens without a full page reload until the final redirect.<\/p>\n\n<h4>Keeping the load down<\/h4>\n\n<p>Because step 1 is resolved client-side, a normal sign-in makes exactly one\n    admin-ajax.php call. Failed password attempts are rate-limited per IP address\nand per email in a short rolling window <em>before<\/em> WordPress authentication runs,\nand an off-screen honeypot field plus a minimum fill-time check drop obvious bot\nsubmissions without the (CPU-heavy) password hash. Together these keep\ncredential-stuffing traffic from turning the login page into a load problem.<\/p>\n\n<p>(When <strong>Unknown accounts<\/strong> is set to reveal missing accounts, step 1 still needs a\nserver round trip, since that answer can only come from the database.)<\/p>\n\n<h4>Privacy<\/h4>\n\n<p>By default the two possible step-1 responses are identical whether or not an account\nexists, and failed logins return a single generic message \u2014 so the form cannot be\nused to discover which email addresses have accounts. A setting lets you turn on\nexplicit \"no account found\" messages if you prefer Amazon's behaviour.<\/p>\n\n<h4>Settings (Settings \u2192 Two-Screen Login)<\/h4>\n\n<ul>\n<li><strong>First step accepts<\/strong> \u2014 email only, or email <em>or<\/em> username.<\/li>\n<li><strong>Unknown accounts<\/strong> \u2014 stay silent (default) or say when no account matches.<\/li>\n<li><strong>Rate limiting<\/strong> \u2014 throttle repeated failed logins per IP \/ email (on by default).<\/li>\n<li><strong>Redirect after login<\/strong> \u2014 a URL, or blank for the account page \/ home.<\/li>\n<li><strong>Lost-password URL<\/strong> \u2014 a URL, or blank for the default WordPress reset page.<\/li>\n<\/ul>\n\n<h4>For developers<\/h4>\n\n<pre><code>add_filter( 'tslf_redirect_url', function ( $url ) { return home_url( '\/dashboard\/' ); } );\nadd_filter( 'tslf_lostpassword_url', function ( $url ) { return '\/forgot\/'; } );\nadd_filter( 'tslf_template', function ( $path, $name, $args ) { return $path; }, 10, 3 );\nadd_filter( 'tslf_logged_in_notice', function ( $html, $user ) { return $html; }, 10, 2 );\nadd_action( 'tslf_logged_in', function ( $user ) { \/* ... *\/ } );\n\n\/\/ Abuse mitigation.\nadd_filter( 'tslf_throttle', function ( $c ) { $c['id_max'] = 5; return $c; } ); \/\/ window, ip_max, id_max\nadd_filter( 'tslf_min_fill_ms', function () { return 2000; } );\nadd_filter( 'tslf_client_ip', function ( $ip ) { return $_SERVER['HTTP_CF_CONNECTING_IP'] ?? $ip; } );\n<\/code><\/pre>\n\n<p>The two step templates (<code>templates\/step-identifier.php<\/code>, <code>templates\/step-password.php<\/code>)\ncan be swapped with the <code>tslf_template<\/code> filter. Style hooks are plain classes\n(<code>.tslf<\/code>, <code>.tslf-form<\/code>, <code>.tslf-step<\/code>, <code>.tslf-error<\/code>, <code>.tslf-submit<\/code>) and CSS custom\nproperties (<code>--tslf-accent<\/code>, <code>--tslf-border<\/code>, \u2026).<\/p>\n\n<h4>Notes &amp; limitations<\/h4>\n\n<ul>\n<li>This is a <strong>front-end<\/strong> form for a page of your choosing. It does not replace\n  wp-login.php or change wp-admin.<\/li>\n<li>No social login, no 2FA \u2014 those are separate concerns handled by other plugins.<\/li>\n<li>If you serve the login page from a full-page cache, exclude it (or its nonce may\nage out for logged-out visitors after ~12 hours).<\/li>\n<li>Translations (including Brazilian Portuguese) are managed on\ntranslate.wordpress.org, not bundled with the plugin. The text domain is\n  t2f-two-screen-login.<\/li>\n<\/ul>\n\n<!--section=installation-->\n<ol>\n<li>Install and activate the plugin.<\/li>\n<li>Create a page (e.g. \"Sign in\") and add the shortcode <code>[two_step_login]<\/code>.<\/li>\n<li>Optionally review <strong>Settings \u2192 Two-Screen Login<\/strong>.<\/li>\n<li>Point your theme's \"Log in\" links at that page.<\/li>\n<\/ol>\n\n<!--section=faq-->\n<dl>\n<dt id=\"does%20it%20work%20with%20woocommerce%3F\"><h3>Does it work with WooCommerce?<\/h3><\/dt>\n<dd><p>Yes. If WooCommerce is active and no redirect is configured, users land on the\nMy Account page after logging in. It does not otherwise depend on WooCommerce.<\/p><\/dd>\n<dt id=\"can%20visitors%20log%20in%20with%20their%20username%20instead%20of%20email%3F\"><h3>Can visitors log in with their username instead of email?<\/h3><\/dt>\n<dd><p>Yes \u2014 set <strong>First step accepts<\/strong> to \"Email address or username\".<\/p><\/dd>\n<dt id=\"javascript%20is%20required%3F\"><h3>JavaScript is required?<\/h3><\/dt>\n<dd><p>The stepped experience is JavaScript-driven, but the form also works with JS\ndisabled or blocked: it falls back to a full-page, step-by-step flow instead of\nthe in-browser transition. The bot timing check is skipped for no-JS submissions\n(the honeypot and rate limiting still apply).<\/p><\/dd>\n<dt id=\"does%20it%20support%20%22remember%20me%22%3F\"><h3>Does it support \"Remember me\"?<\/h3><\/dt>\n<dd><p>Yes, via a checkbox on the password step.<\/p><\/dd>\n<dt id=\"is%20the%20password%20ever%20exposed%3F\"><h3>Is the password ever exposed?<\/h3><\/dt>\n<dd><p>No. It is sent once, over your site's normal (HTTPS) connection, to WordPress's\nstandard <code>wp_signon()<\/code> authentication \u2014 the same function core uses.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>1.1.0<\/h4>\n\n<ul>\n<li>Fix: the login form's <code>&lt;form&gt;<\/code> has no <code>action<\/code> attribute, so with JavaScript disabled or blocked it silently failed to sign anyone in \u2014 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.<\/li>\n<li>Fix (security): a failed password attempt could lock a <em>known<\/em> 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.<\/li>\n<li>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.<\/li>\n<li>Fix: <code>?redirect_to=<\/code> 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.<\/li>\n<li>Fix: the non-JS fallback's redirect could 404 on a subdirectory install (e.g. <code>example.com\/blog<\/code>) by doubling the site's own path prefix.<\/li>\n<li>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.<\/li>\n<li>Fix: <code>uninstall.php<\/code> only 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.<\/li>\n<\/ul>\n\n<h4>1.0.0<\/h4>\n\n<ul>\n<li>Initial release.<\/li>\n<\/ul>","raw_excerpt":"An Amazon-style two-screen login: enter your email first, then your password on a second screen. Added to any page with a shortcode.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/367633","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=367633"}],"author":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/t2ftech"}],"wp:attachment":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=367633"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=367633"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=367633"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=367633"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=367633"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=367633"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}