Skip to content

fix(core): scope app password gate per user - #989

Merged
kasparsd merged 3 commits into
WordPress:masterfrom
faisalahammad:fix/987-scope-app-password-gate
Sep 25, 2026
Merged

kasparsd merged 3 commits into
WordPress:masterfrom
faisalahammad:fix/987-scope-app-password-gate

Conversation

@faisalahammad

@faisalahammad faisalahammad commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

What?

Scope the application password check for API logins to the user who actually authenticated, instead of the whole request.

Fixes #987

Why?

Two_Factor_Core::is_user_api_login_enabled() used did_action( 'application_password_did_authenticate' ) as its default. did_action() counts actions for the entire request, so once one user authenticated with an application password, the check passed for every other user tested in the same request. With XML-RPC system.multicall, several logins share one request, so a second user with only a password could log in after a first user logged in with an application password.

How?

  • A new app_password_did_authenticate() callback runs on the application_password_did_authenticate action and records the user IDs that authenticated with an application password during the current request.
  • is_user_api_login_enabled() now checks whether the given user ID is in that list. The two_factor_user_api_login_enable filter still works the same.
  • New tests cover that only the authenticated user gets API login enabled, plus an XML-RPC request test for the filter_authenticate() path with two different users.

Testing Instructions

  1. Set up an application password for a user who has Two Factor enabled, and make sure a second user without two factor exists.
  2. Send an XML-RPC wp.getUsersBlogs request for the two factor user using the application password. The login should still work.
  3. Send a system.multicall request that first authenticates the two factor user with the application password and then the second user with only a password. The first call should succeed and the second should fail. On 0.16.0 both succeeded because the first call enabled API login for the whole request.
  4. Automated tests: npm test.

Changelog Entry

Fixed - Scope the application password API login check to the user who authenticated, instead of the whole request.

Open WordPress Playground Preview

The API login check relied on did_action(), which is global for
the whole request. After one user authenticated with an application
password, any other user checked in the same request passed the
same check, for example via XML-RPC system.multicall.

Track the user IDs reported by the application_password_did_authenticate
action and only allow API login for those users.

Fixes WordPress#987
@github-actions

github-actions Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: faisalahammad <faisalahammad@git.wordpress.org>
Co-authored-by: kasparsd <kasparsd@git.wordpress.org>
Co-authored-by: masteradhoc <masteradhoc@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@wordpress/scripts 35.0.0 (bumped by dependabot on master) ships
ESLint v10, which no longer supports eslint-env comments. The
Gruntfile.js has 19 lint errors: one for the eslint-env comment and
18 prettier formatting errors.

Remove the eslint-env comment. Node globals come from the wp-scripts
lint config. Reformat the file per prettier.

Build config only. No plugin behavior changes.

@faisalahammad faisalahammad left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CI fix summary

Problem: The "Lint JS & CSS" job failed. wp-scripts lint-js reported 19 errors in Gruntfile.js. Dependabot bumped @wordpress/scripts to 35.0.0 on master. That version ships ESLint v10, and ESLint v10 no longer supports eslint-env comments. This PR only touches PHP files, so the failure was already there on master.

Fix in commit e645c5a:

Item Detail
Failed job Lint JS & CSS, step "Lint JS"
Errors 19 in Gruntfile.js (1 eslint-env, 18 prettier)
Change 1 Remove /* eslint-env node,es6 */ comment. ESLint v10 does not support it. Node globals come from the wp-scripts config.
Change 2 Reformat the file with npm run format:js to match prettier.
Verified npm run lint:js and npm run lint:css both pass locally.

Build config only. No plugin behavior changes.

Note: PR #788 carries the same fix in commit 55ae3ed. If that PR merges first, this commit can be dropped after a rebase.

Comment thread Gruntfile.js
Comment thread class-two-factor-core.php
@masteradhoc masteradhoc added this to the 0.17.0 milestone Sep 25, 2026

@kasparsd kasparsd left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good, thanks for the fix!

Comment thread Gruntfile.js
@kasparsd
kasparsd merged commit 2168c4c into WordPress:master Sep 25, 2026
27 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Improve the application password gate for skipping two-factor requirement

3 participants