Bricks 2FA Bridge
Makes Bricks Builder login forms work with your two-factor authentication plugin.
by Matt Bosak · github.com/mattbosak/bricks-2fa-bridge · website
Install
No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:
wp plugin install https://github.com/mattbosak/bricks-2fa-bridge/archive/refs/heads/main.zipMakes Bricks Builder login forms work with your two-factor authentication plugin. Your users enter their 2FA code in a field right on your custom login form - no page reload, no broken logins, no bypassed second factor.
Setup is quick. Install the plugin, add one field to your existing login form, and you're done - most people are up and running in a few minutes, with no separate settings screen to configure.
If it saves you an afternoon, you can buy me a coffee. Entirely optional - the plugin is free and GPL either way.
Contents
- What it fixes
- How it looks to your users
- Requirements
- Installation
- FAQ
- For developers
- Support the project
- License
What it fixes
Bricks' Form element has a "User Login" action for building custom login pages. Combine it with a 2FA plugin and the login breaks in one of two ways:
- It fails silently - no error, no code prompt, no login. The spinner just stops.
- Or the second factor is skipped entirely - the account logs in the moment the password checks out, 2FA or not.
Both come from the same root cause. Most 2FA plugins expect a full-page form
submission, and respond to a pending second factor by printing a challenge page and
exiting - which Bricks' AJAX request can't parse. Bricks, meanwhile, completes the
login the moment wp_signon() succeeds, without checking whether a 2FA plugin still
wants a say. This plugin closes both gaps: it intercepts WordPress's own authenticate
filter before any 2FA plugin runs, so a pending second factor can never be bypassed,
and it moves code entry onto your own login form, so nothing ever tries to render a
challenge page inside the AJAX request.
How it looks to your users
- They enter username and password on your Bricks login form, as always.
- If their account has 2FA, a code field appears on the same form (it stays hidden for everyone else), with a hint like "Check your email for a code."
- They enter the code and submit again - logged in, never having left the page.
The code field is one you add in the Bricks builder yourself, so it inherits your form's styling like any other field.
Requirements
- WordPress 6.8+ (tested up to 7.0.4), PHP 7.4+
- The Bricks theme
- One of the supported 2FA plugins:
- Two-Factor (WordPress.org)
- WP 2FA (Melapress)
- Wordfence (built-in 2FA) or Wordfence Login Security
- Kadence Security (formerly Solid Security / iThemes Security; 2FA is in the free edition)
- All-In-One Security (AIOS)
- Two Factor Authentication (UpdraftPlus)
Installation
-
Install and activate a supported 2FA plugin (list above), if you haven't already.
-
Install this plugin: download the latest release zip and upload it under Plugins → Add New → Upload Plugin, then activate.
-
Add the code field to your login form in the Bricks builder:
- Add a Text field.
- Set Attribute: Name (in the field's own settings panel, under Attributes) to
exactly
b2fa_code. - Leave Required off.
The field needs to be submittable empty on the first attempt, since the plugin doesn't know a code is needed until then - and once added, it stays hidden until a login actually needs one.
-
Optional - add a method selector, to let users choose between their enabled 2FA methods (e.g. Authenticator App / Email / Backup Codes) instead of always using their primary one:
- Add a Select field with Attribute: Name set to exactly
b2fa_method. - Set its Options to one line, e.g.
Loading methods…- just avoid the literal wordstotp,email, orbackup_codes.
Bricks won't render a
<select>field at all if its Options list is empty, which is why one placeholder line is required - this plugin replaces it with the real options before a user ever sees it, so the placeholder text itself doesn't matter. It does matter that it isn'ttotp,email, orbackup_codes, though: those are real method ids, and using one as the placeholder would pre-select that specific method instead of correctly falling back to the primary one.Skip this field entirely and everyone simply uses their primary method.
- Add a Select field with Attribute: Name set to exactly
FAQ
General
Does this plugin provide two-factor authentication itself? No. It's a compatibility bridge - it makes your existing 2FA plugin work correctly with Bricks' AJAX login forms.
Does it work with a custom "Login error message" set on my form? Yes. The reveal signal travels via an HTTP response header, independent of whatever message text Bricks displays.
Do brute-force protections still apply? Yes. Code attempts through the form get an increasing delay after each failure plus a hard attempt ceiling (both filterable - see below), failures still feed your security plugin's own counters where it keeps them, and where the 2FA plugin has its own protection (like Two-Factor's compromised-password reset), that fires too.
A user has a passkey - can they log in through the Bricks form? No, and they are told so rather than left guessing. A passkey is a browser-side WebAuthn exchange that cannot be completed by typing a code into a form field, so those users see a message asking them to contact the site administrator. They can still log in through the normal WordPress login screen. The same applies to any method a future 2FA plugin release adds that this bridge does not yet drive.
The form asks for a code but there is nowhere to type it.
Almost always the Text field is missing from the form, or its Name attribute is not
exactly b2fa_code - the plugin only activates on a submission carrying that field.
If the field is correct and the form still will not progress, the next likeliest cause
is a security plugin, proxy or CDN stripping the response headers this plugin uses to
signal that a code is required; enable WP_DEBUG and look for a bricks-2fa-bridge
entry in the error log.
Plugin-specific notes
What if I run two supported 2FA plugins at once? It works - this mainly matters mid-migration between 2FA plugins. Each user is challenged by the first plugin (in the order Two-Factor, WP 2FA, Wordfence, Kadence Security, AIOS/Simba) that has 2FA enabled for them - one code per login, and the other plugin won't double-challenge or break the response. A plugin that doesn't consider someone a 2FA user keeps all its own policies for them.
I use Kadence Security - what about its other login interstitials? Only the two-factor step is bridged into the Bricks form. Kadence's other interstitials (e.g. a forced password change) are separate features and keep their normal behavior.
I use AIOS or the Two Factor Authentication plugin - does "trust this device" work?
- Existing trusted devices: honored - a trusted browser skips the code, same as upstream.
- Marking a new device as trusted: not possible from a Bricks form - users do that once via wp-login.php.
I use WP 2FA with enforced 2FA - what about users who haven't set it up yet?
- Users with 2FA configured: work fully through the Bricks form.
- Users enforced but not yet configured: get WP 2FA's setup-wizard redirect, which can't render inside an AJAX login - they complete their one-time setup via wp-login.php, and the Bricks form works for them from then on.
I use Wordfence's login CAPTCHA - does that work with a Bricks login form?
- With 2FA active: yes - a valid code satisfies the login, mirroring Wordfence's own rule that a valid 2FA code exempts the CAPTCHA check.
- Without 2FA: Wordfence falls back to its email verification link (a Bricks form has no reCAPTCHA integration) - that's Wordfence's own behavior, unchanged by this plugin.
For developers
If a user's only second factor is one the form cannot complete - a passkey, or a
provider added by another plugin - they are shown "Your two-factor authentication
method cannot be completed on this login form. Please contact the site
administrator." rather than a code box that can never be satisfied. The
bricks_tfa_bridge_unsupported_method action fires so you can log or alert on it;
with WP_DEBUG on, a line also goes to the error log. Such a user can still log in
through your site's normal WordPress login screen.
Filters, all optional:
| Hook | Purpose | Default |
|---|---|---|
bricks_tfa_bridge_code_field_id |
POST name of the code field | b2fa_code |
bricks_tfa_bridge_method_field_id |
POST name of the method selector | b2fa_method |
bricks_tfa_bridge_header_name |
Header signaling "code required" | X-Bricks-TFA-Required |
bricks_tfa_bridge_methods_header_name |
Header carrying the methods list | X-Bricks-TFA-Methods |
bricks_tfa_bridge_base_delay |
Backoff base, in seconds (delay = base × 2ⁿ) | 1 or adapter value |
bricks_tfa_bridge_max_delay |
Backoff cap, in seconds | 15 min or adapter value |
bricks_tfa_bridge_failed_attempt_limit |
Hard ceiling on total failures | 30 or adapter value |
bricks_tfa_bridge_adapters |
Register custom adapters | built-in adapters |
The bricks_tfa_bridge_attempt_limit_reached action fires when a user hits the hard
failure ceiling, in case you want to notify someone.
Support for another 2FA plugin is one class implementing
src/AdapterInterface.php, registered via the bricks_tfa_bridge_adapters filter.
Read CLAUDE.md first - it documents the architecture, the per-plugin gotchas already
solved, and the source-verification process each existing adapter went through.
Compiled .mo files are not committed; they are built from the .po sources during
release. Installing from a clone therefore gives you an untranslated (English) plugin
until you run msgfmt over languages/*.po yourself. Use a release zip for anything
other than development.
Support the project
This plugin is free and always will be. If it saved you time, a coffee is a nice way to say so: ko-fi.com/mattbosak.
Bug reports and pull requests are just as welcome, and cost nothing.
License
GPLv2 or later. See the license header in bricks-2fa-bridge.php.