Skip to content

Two Factor

Two plugins work together here: instance-force-active-plugins keeps WP 2FA running, and instance-wp-2fa-key-guard refuses to let it run misconfigured.

WP 2FA lives in web/app/plugins rather than mu-plugins because it resolves its own paths against WP_PLUGIN_DIR and breaks as a must-use plugin. Anything in that directory can be switched off from the admin, which is not acceptable for the plugin standing between an attacker and the site.

instance-force-active-plugins adds it to the active list on read, so nothing is written to the database, and:

  • removes the Deactivate link, so the screen matches reality
  • refuses the deactivation action itself, in case it is reached directly
// Add your own, per project
add_filter('instance_force_active_plugins', function (array $plugins): array {
$plugins[] = 'some-plugin/some-plugin.php';
return $plugins;
});

Nothing is forced when WP_ENV is development, so 2FA does not get in the way of local work:

add_filter('instance_force_active_plugins_exempt_environments', function (): array {
return ['development', 'local'];
});

WP 2FA encrypts its two factor secrets with a key it looks up in the WP2FA_ENCRYPT_KEY constant. When that constant is missing it generates one, tries to write it into wp-config.php, and falls back to the options table when that write fails.

Bedrock keeps wp-config.php read only, so the fallback is exactly what would happen here: the key ending up in the same database as the secrets it protects.

instance-wp-2fa-key-guard stops that. Without a key from the environment it:

  • keeps WP 2FA out of the load order entirely, so it cannot write anything
  • blocks writes to the wp_2fa_secret_key option as a second line of defence
  • stops admin and login requests with an explanation, and warns rather than errors under WP-CLI so deploys and fixes still run
  1. Generate one:

    Terminal window
    openssl rand -base64 32
  2. Add it to .env:

    WP2FA_ENCRYPT_KEY='the-generated-key'
  3. Reload. config/application.php turns it into the constant WP 2FA reads.