This commit is contained in:
Javier Casares 2026-08-24 08:18:14 +00:00
commit 511d2fefae
7 changed files with 103 additions and 50 deletions

View file

@ -1,5 +1,29 @@
== Changelog ==
= 1.6.3 =
_Release date: 2026-08-24_
**Changed**
* Manager detection now uses the ecosystem presence constant (`ROBOTSTXT_MANAGER_NOTICED`, defined by Manager 1.6.2+) with a fallback to the plugin-list scan for older Manager versions, so a stale plugin list can no longer produce false "Manager missing" notices.
**Fixed**
* Compatibility with the ALTCHA Spam Protection plugin: when "Protect login" was enabled, submitting the 2FA verification code failed with "[ALTCHA] Sorry, your request could not be processed.". The ALTCHA interceptor is now disabled while the verification screen is shown; the first login step keeps its ALTCHA check.
**Compatibility**
* WordPress: 5.6 7.1
* PHP: 8.0 8.5
**Tests**
* PHP Coding Standards: PHP_CodeSniffer 3.13.6 / WPCS 3.4.1 — 0 errors
* PHPStan: level 9 — 0 errors
* PHPCompatibility: 8.08.5 — 0 issues
* PHPUnit: 9.6.36 — 73 tests, 163 assertions
= 1.6.2 =
_Release date: 2026-08-17_

View file

@ -20,11 +20,6 @@ if ( ! defined( 'ABSPATH' ) ) {
*/
class Manager_Notice {
/**
* Basename of the Manager plugin main file.
*/
private const MANAGER_BASENAME = 'robotstxt-manager/robotstxt-manager.php';
/**
* URL of the Manager plugin page.
*/
@ -56,16 +51,35 @@ class Manager_Notice {
/**
* Whether the Manager (by ROBOTSTXT) plugin is active.
*
* Uses the ecosystem presence constant (Manager 1.6.2+) and falls back
* to a plugin-list scan for older Manager versions.
*
* @since 1.6.2
*
* @return bool True when the manager plugin is active.
* @return bool True when the Manager plugin is present and activated.
*/
public static function is_manager_active(): bool {
if ( ! function_exists( 'is_plugin_active' ) ) {
if ( defined( 'ROBOTSTXT_MANAGER_NOTICED' ) && ROBOTSTXT_MANAGER_NOTICED ) {
return true;
}
if ( ! function_exists( 'get_plugins' ) ) {
require_once ABSPATH . 'wp-admin/includes/plugin.php';
}
return is_plugin_active( self::MANAGER_BASENAME );
foreach ( get_plugins() as $file => $data ) {
$slug = dirname( $file );
if ( '.' === $slug ) {
$slug = basename( $file, '.php' );
}
if ( 'robotstxt-manager' === $slug ) {
return is_plugin_active( $file );
}
}
return false;
}
/**

View file

@ -233,6 +233,46 @@ class Login_Form_Manager {
add_filter( 'login_message', array( $this, 'filter_login_message' ) );
add_filter( 'authenticate', array( $this, 'maybe_complete_verification' ), 5, 3 );
add_filter( 'authenticate', array( $this, 'enforce_verification_challenge' ), 30, 3 );
add_action( 'init', array( $this, 'maybe_remove_altcha_interceptor' ), 0 );
}
/**
* Disable the ALTCHA Spam Protection login interceptor during the 2FA stage.
*
* ALTCHA's "Protect login" feature rejects any wp-login.php POST that does
* not carry a fresh proof-of-work payload (HTTP 403). Its JavaScript only
* produces that payload for classic username + password submissions, while
* the verification stage intentionally omits the password field, so every
* legitimate 2FA submission would otherwise be blocked. Human verification
* already happened on the first login step; this stage is protected by the
* pending-stage token, the stage nonce, and the failed-attempts lockout.
*
* @since 1.6.2
*
* @return void
*/
public function maybe_remove_altcha_interceptor(): void {
if ( ! $this->is_verification_stage() ) {
return;
}
if ( ! has_action( 'init', 'altcha_interceptor' ) ) {
return;
}
global $wp_filter;
if ( ! isset( $wp_filter['init'] ) || ! $wp_filter['init'] instanceof \WP_Hook ) {
return;
}
foreach ( array_keys( $wp_filter['init']->callbacks ) as $priority ) {
if ( isset( $wp_filter['init']->callbacks[ $priority ]['altcha_interceptor'] ) ) {
remove_action( 'init', 'altcha_interceptor', (int) $priority );
return;
}
}
}
/**

View file

@ -4,7 +4,7 @@ Tags: security, two-factor authentication, login, otp
Requires at least: 5.6
Tested up to: 7.0
Requires PHP: 8.0
Stable tag: 1.6.2
Stable tag: 1.6.3
License: GPLv3 or later
License URI: https://www.gnu.org/licenses/gpl-3.0.html
@ -59,6 +59,18 @@ Yes. Activate the plugin at the network level. Network administrators can set an
== Changelog ==
= 1.6.3 =
_Release date: 2026-08-24_
**Changed**
* Manager detection now uses the ecosystem presence constant (`ROBOTSTXT_MANAGER_NOTICED`, defined by Manager 1.6.2+) with a fallback to the plugin-list scan for older Manager versions.
**Fixed**
* Compatibility with the ALTCHA Spam Protection plugin: when "Protect login" was enabled, submitting the 2FA verification code failed with "[ALTCHA] Sorry, your request could not be processed.". The ALTCHA interceptor is now disabled while the verification screen is shown; the first login step keeps its ALTCHA check.
= 1.6.2 =
_Release date: 2026-08-17_
@ -81,21 +93,6 @@ _Release date: 2026-08-14_
* Fatal error during editor autosaves: the `[robotstxt_2fa_profile]` shortcode expanded when WordPress applied content filters to a post revision via REST (for example, an article that merely mentions the shortcode in its text). In that context the wp-admin render helpers are not loaded, causing a fatal error. The shortcode now bails out early on REST requests and loads the required wp-admin includes on demand elsewhere.
= 1.6.0 =
_Release date: 2026-08-07_
**Added**
* REST API for administrators (namespace `robotstxt-2fa/v1`, all endpoints require `manage_options`):
* `GET /wp-json/robotstxt-2fa/v1/settings` — read the full 2FA configuration.
* `PUT /wp-json/robotstxt-2fa/v1/settings` — update the configuration (reuses the same validation as the admin settings form).
* `GET /wp-json/robotstxt-2fa/v1/users?role=<slug>&status=<enabled|required|disabled>` — list every user with their 2FA status: enabled, configured methods, role-required methods, forced flag, frequency, preferred method, whether OTP is set up, and unused recovery code count. No secrets are exposed.
**Changed**
* `wp 2fa list` now shows separate **Enabled**, **Methods** (configured), and **Required** (role-enforced) columns, plus a new `--required` filter to list only users whose role enforces 2FA.
= Previous versions =
For the full changelog see the [changelog](https://www.robotstxt.software/plugins/robotstxt-2fa/) page.

View file

@ -4,7 +4,7 @@
* Plugin URI: https://www.robotstxt.software/plugins/robotstxt-2fa/
* Update URI: https://www.robotstxt.software/plugins/robotstxt-2fa/
* Description: Adds two-factor authentication to the WordPress login flow.
* Version: 1.6.2
* Version: 1.6.3
* Author: ROBOTSTXT
* Author URI: https://www.robotstxt.software/
* Text Domain: robotstxt-2fa
@ -25,7 +25,7 @@ if ( ! defined( 'ABSPATH' ) ) {
}
if ( ! defined( 'ROBOTSTXT_2FA_VERSION' ) ) {
define( 'ROBOTSTXT_2FA_VERSION', '1.6.2' );
define( 'ROBOTSTXT_2FA_VERSION', '1.6.3' );
}
if ( ! defined( 'ROBOTSTXT_2FA_FILE' ) ) {

View file

@ -1,22 +0,0 @@
{
"name": "2FA (by ROBOTSTXT)",
"slug": "robotstxt-2fa",
"version": "1.6.2",
"download_url": "https://git.robotstxt.es/ROBOTSTXT/robotstxt-2fa/releases/download/1.6.2/robotstxt-2fa-1.6.2.zip",
"requires": "6.4",
"requires_php": "8.0",
"tested": "7.1",
"last_updated": "2026-08-14",
"author": "ROBOTSTXT",
"author_profile": "https://www.robotstxt.es/",
"homepage": "https://www.robotstxt.es/plugins/robotstxt-2fa/",
"description": "Adds per-role two-factor authentication to the WordPress login flow. Supports email codes, authenticator apps (TOTP), and recovery codes.",
"changelog": "",
"sections": {
"description": "Adds per-role two-factor authentication to the WordPress login flow. Supports email codes, authenticator apps (TOTP), and recovery codes.",
"changelog": ""
},
"banners": { "low": "", "high": "" },
"icons": { "1x": "", "2x": "" }
}

View file

@ -3,7 +3,7 @@
'name' => 'robotstxt/robotstxt-2fa',
'pretty_version' => 'dev-main',
'version' => 'dev-main',
'reference' => '6ee18abad9ca997d00a8f2397aee3cc77a90a4e3',
'reference' => '2f5ed132e25529d95b04f81bfd33dfdbcb0fc487',
'type' => 'wordpress-plugin',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@ -40,7 +40,7 @@
'robotstxt/robotstxt-2fa' => array(
'pretty_version' => 'dev-main',
'version' => 'dev-main',
'reference' => '6ee18abad9ca997d00a8f2397aee3cc77a90a4e3',
'reference' => '2f5ed132e25529d95b04f81bfd33dfdbcb0fc487',
'type' => 'wordpress-plugin',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),