Skip to content

Application password UX improvements - #11079

Open
johnbillion wants to merge 22 commits into
WordPress:trunkfrom
johnbillion:application-password-ux
Open

johnbillion wants to merge 22 commits into
WordPress:trunkfrom
johnbillion:application-password-ux

Conversation

@johnbillion

@johnbillion johnbillion commented Feb 27, 2026 •

Copy link
Copy Markdown
Member

A collection of UX enhancements for the authorize-application.php screen, with a particular focus on hardening against phishing.

  • Greatly increase clarity about the target URL that will receive credentials if the request is approved
    • Shows the target host name front and centre without the rest of the URL
    • Gates approval behind a checkbox
  • Removes support for username:password@ credentials in URLs
    • These are almost exclusively used for phishing
    • Despite being deprecated in modern browsers for many years, they're still technically functional, but there is no need to support them
  • Removes support for reject URLs
    • If a user rejects approval, it is unexpected that they still get sent to the requesting site
    • This is an open redirect that can still result in phishing
    • Replaces the redirect with a message stating that the request was not approved
  • Adds a "Copy" button to the generated application password after approval
    • Matches the one used on the Application Passwords section of the user profile screen

Screenshots

Ticket

Trac ticket: https://core.trac.wordpress.org/ticket/66009

@github-actions

Copy link
Copy Markdown

Test using WordPress Playground

The changes in this pull request can previewed and tested using a WordPress Playground instance.

WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser.

Some things to be aware of

  • All changes will be lost when closing a tab with a Playground instance.
  • All changes will be lost when refreshing the page.
  • A fresh instance is created each time the link below is clicked.
  • Every time this pull request is updated, a new ZIP file containing all changes is created. If changes are not reflected in the Playground instance,
    it's possible that the most recent build failed, or has not completed. Check the list of workflow runs to be sure.

For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation.

Test this pull request with WordPress Playground.

@domenico-ruggiano

Copy link
Copy Markdown

Great improvements here :)

# Conflicts:
#	src/wp-admin/includes/user.php
#	tests/phpunit/tests/admin/Admin_Includes_User_WpIsAuthorizeApplicationRedirectUrlValid_Test.php
@johnbillion
johnbillion marked this pull request as ready for review August 31, 2026 20:05
@rollybueno

Copy link
Copy Markdown
Member

I liked this - comparing with the current UX, this has better improvements 👍

2026-09-01_21-18 2026-09-01_21-21

plus the additional confirmation step for external redirects.

One thing I'm wondering about: should confirm_external_redirect also be validated server-side when approving a request with a success_url?

The checkbox is required in the form and the approve button is gated in JS, but the no-JS PHP approval handler doesn't check whether confirm_external_redirect was submitted before creating the application password.

That means a direct POST with a valid nonce could bypass the confirmation step. Since this change is intended to make users explicitly acknowledge where their credentials will be sent, would it make sense to enforce that acknowledgement server-side as well (at least when success_url is present)?

@github-actions

github-actions Bot commented Sep 1, 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.

Unlinked Accounts

The following contributors have not linked their GitHub and WordPress.org accounts: @domenico-ruggiano.

Contributors, please read how to link your accounts to ensure your work is properly credited in WordPress releases.

Core Committers: Use this line as a base for the props when committing in SVN:

Props johnbillion, joedolson, whyisjake, rollybueno, hanneslsm.

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

@johnbillion

Copy link
Copy Markdown
Member Author

I think a direct POST is fine, the checkbox is a safety net more than an enforcement.

@joedolson joedolson left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Only one of these requested changes (the implicit label) is solely within new code; if you choose not to make the other changes, let me know, and I'll open those as a separate ticket.

Comment thread src/js/_enqueues/admin/auth-app.js Outdated

setTimeout( function() {
$successElement.addClass( 'hidden' );
}, 3000 );

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

For me, this feels like too rapid a timeout. I'd prefer if it was dismissable, instead of timing out, but barring that more substantial change, a time out of at least 20 seconds would be preferable.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

(This is also true of the existing copy button when creating application passwords in a user profile.)

.append( $( '<p></p>' ).text( wp.i18n.__( 'You have not approved this connection. No data has been shared with the application.' ) ) );

$form.replaceWith( $notice );
$notice.trigger( 'focus' );

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Setting focus to a container that also has role="alert" will usually result in double-announcing the notification. (This is also true in the existing code, line 129.)

Since focus has to be changed, due to the removal of the form contents and current focus location, I'd recommend removing the alert role to prevent the duplicate announcement.

.append( ' <button type="button" class="button copy-button">' + wp.i18n.__( 'Copy' ) + '</button>' )
.append( '<span class="success hidden" aria-hidden="true"> ' + wp.i18n.__( 'Copied!' ) + '</span>' )
)
.append( '<p>' + wp.i18n.__( 'Be sure to save this in a safe location. You will not be able to retrieve it.' ) + '</p>' );

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is an existing issue, but this text should be connected to the password input via aria-describedby.

Comment thread src/wp-admin/authorize-application.php Resolved

@whyisjake whyisjake left a comment •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The direction here is right and the reject-branch open redirect is genuinely closed — authApp.reject is gone, the PHP reject branch is an unconditional admin_url(), and I could not get past the new userinfo rejection with literal @, %40, doubled @, backslashes or whitespace. CSRF, capability checks and output escaping all survive the rewrite intact.

Five things below, two of which are regressions this PR introduces rather than pre-existing issues.

One further point I'd like to raise with you directly rather than here — I'll follow up separately.

Note also that joedolson's CHANGES_REQUESTED from yesterday is still open with five inline comments and no commits since, so some of this may already be on your list.

AI assistance: Yes. Tool(s): Claude Code. Used for: reviewing the diff against trunk, empirically verifying parse_url() behaviour, and checking the no-JS form path.

Comment thread src/wp-admin/authorize-application.php Resolved
Comment thread src/wp-admin/authorize-application.php Resolved
Comment thread src/wp-admin/authorize-application.php Resolved
Comment thread src/js/_enqueues/admin/auth-app.js Resolved

// If redirecting to an external site, gate the approve button behind the confirmation checkbox.
if ( authApp.successHost ) {
var $checkbox = $( 'input[name="confirm_external_redirect"]' );

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Minor, but it makes the gate weaker than it looks: the approve button is disabled with .prop( 'aria-disabled', true ).

jQuery's .prop() with a hyphenated, non-native name sets an expando property on the element rather than an attribute, so nothing is reflected into the DOM and assistive technology never sees the disabled state. The click guard further down then returns silently with no feedback.

.attr() / .removeAttr() throughout would make it real. Alternatively drop the manual disabled state entirely and lean on the existing reportValidity() call, which already surfaces the required-checkbox message natively.

@hanneslsm

hanneslsm commented Sep 21, 2026 •

Copy link
Copy Markdown

Judging from the screenshot I'd like to propose some changes in the order and copy, to make things even clearer and easier scannable:


An application would like to connect to your account.

New Application Password Name:
[ ________ ]

An application would like to connect to your account. You should only do this if you trust the application in question.
The application identifies itself as: Google Analytics
The application will get access to:

  • your username
  • new application password

Caution

The data will send to: evil.com

  • I understand that my username and a new application password will be sent to evil.com

And then, when a application password was created, I think we should add links to the success screen:

  • Delete password
  • Show all application passwords

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.

6 participants