Close button-only bypass via XML-RPC system.multicall
The credential-based exemption used did_action( 'application_password_did_authenticate'), which is request-global. In a system.multicall, a first boxcar authenticated with any application password let later boxcars sign in other users with a normal password. The exemption now applies only to the user the application password authenticated in the same authenticate pass (reset at priority 0). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
850f0dcd54
commit
b35f6a867b
1 changed files with 33 additions and 1 deletions
|
|
@ -37,6 +37,13 @@ class M365_Login_Auth {
|
|||
*/
|
||||
private $graph;
|
||||
|
||||
/**
|
||||
* User authenticated by an application password in the current authenticate pass (0 = none).
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $app_password_user = 0;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
|
|
@ -55,6 +62,9 @@ class M365_Login_Auth {
|
|||
add_action( 'init', array( $this, 'maybe_accept_fallback_key' ), 6 );
|
||||
// Runs after core's username/password handlers (priority 20), which would otherwise overwrite an early WP_Error.
|
||||
add_filter( 'authenticate', array( $this, 'block_password_login' ), 99, 3 );
|
||||
// Track application-password sign-ins per authenticate pass (XML-RPC multicall runs several passes per request).
|
||||
add_filter( 'authenticate', array( $this, 'reset_app_password_user' ), 0 );
|
||||
add_action( 'application_password_did_authenticate', array( $this, 'remember_app_password_user' ) );
|
||||
// No session cookies from API contexts (XML-RPC, REST) while password sign-in is disabled.
|
||||
add_filter( 'send_auth_cookies', array( $this, 'block_api_auth_cookies' ), 99, 4 );
|
||||
|
||||
|
|
@ -164,7 +174,9 @@ class M365_Login_Auth {
|
|||
// Exempt by credential, not by request context: application passwords (XML-RPC, REST) and
|
||||
// WP-CLI keep working. A normal password is refused everywhere – also in forms of other
|
||||
// plugins that happen to run inside xmlrpc.php or a REST request.
|
||||
if ( ( defined( 'WP_CLI' ) && WP_CLI ) || ( $user instanceof WP_User && did_action( 'application_password_did_authenticate' ) ) ) {
|
||||
// The application password must have authenticated exactly this user in this pass – did_action()
|
||||
// is request-global and would let a later multicall boxcar through with a normal password.
|
||||
if ( ( defined( 'WP_CLI' ) && WP_CLI ) || ( $user instanceof WP_User && $this->app_password_user && $this->app_password_user === $user->ID ) ) {
|
||||
return $user;
|
||||
}
|
||||
|
||||
|
|
@ -182,6 +194,26 @@ class M365_Login_Auth {
|
|||
return new WP_Error( 'm365_login_button_only', __( 'Password sign-in is disabled on this site. Please use the Microsoft button.', 'm365-login' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a new authenticate pass (runs first on the authenticate filter).
|
||||
*
|
||||
* @param null|WP_User|WP_Error $user Result so far (unchanged).
|
||||
* @return null|WP_User|WP_Error
|
||||
*/
|
||||
public function reset_app_password_user( $user ) {
|
||||
$this->app_password_user = 0;
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remembers which user an application password authenticated in the current pass.
|
||||
*
|
||||
* @param WP_User $user Authenticated user.
|
||||
*/
|
||||
public function remember_app_password_user( $user ) {
|
||||
$this->app_password_user = $user instanceof WP_User ? (int) $user->ID : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* While button-only mode is active, API requests (XML-RPC, REST) never receive session cookies.
|
||||
*
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue