Application passwords in WordPress 5.6

Before the release of WordPress 5.6, WordPress core had no feature that external services could use to authenticate themself against a WordPress installation. WordPress 5.6 brought Application Passwords into core and with that a user flow, which can be used by third-party services to ask for authorization for later requests.

Authorization flow

On make.wordpress.org there is a guide on how to use the application passwords feature, I used that as a starting point. The process can be split into a few steps that need to be done by the service that wants to send authenticated requests to a WordPress installation:

  1. Check if the WordPress installation supports the application passwords feature.
  2. Redirect the user to the WordPress installation endpoint which handles the creation of new application passwords. If the user is not logged in on his WordPress site, he has to log in first.
  3. Check and save the credentials.
  4. Use the credentials for authenticated requests.

Check if the installation supports the application passwords feature

To ensure that a WordPress site supports the application passwords feature, we can send a request to the root REST endpoint of the site, which is accessible under https://example.com/wp-json/. The first requirement is that we get information back and no plugin is used to disable the REST API.

If we get a JSON response, the path authentication.application-passwords.endpoints.authorization contains the URL for redirecting the user to create an application password – in my case that would be https://florianbrinkmann.com/en/wp-admin/authorize-application.php. If the JSON object does not contain that information, the feature is either disabled or the installed WordPress version is older than 5.6. The situation when WP is too old could be fixed by using the application passwords plugin. One more scenario when the authorization URL will not be part of the JSON response is when the request is done via http instead of https.

Redirect user to the authorization endpoint

If we have an authorization endpoint URL from the first request, we can redirect the user to that URL after adding a few GET parameters:

  • app_name is the name of our service, which is shown in the list of application passwords in the backend view of a WordPress user. This value can be changed by the user before creating the application password.
  • app_id (optional) is a UUID, which could be used, for example, by the website admin after a data breach of the third-party service, to remove all application passwords with the UUID of that service. For that to work, the external service has to use the same UUID for each authorization, of course.
  • success_url (optional) is the URL where the user is redirected after an application password was created successfully. The success URL will get the three GET params site_url, user_login, and password. If not set, the created password is shown to the user so that he can enter it manually on the third-party service site.
  • reject_url (optional) is the URL to redirect the user to after the creation of an application password failed. If not set, the success_url gets the parameter ?success=false and is used for the redirect. If that also does not exist, the user is redirected to the WordPress dashboard.

success_url and reject_url need to be https:// URLs or app-specific protocols. http:// is not allowed.

Because the user flow is a not-so-great flow without the success_url, we have an URL with the following schema which we use to redirect the user to:

https://example.com/wp-admin/authorize-application.php?app_name=Beispielname&success_url=https://app.example.com/success
Code language: Bash (bash)

In case of success, the user comes back to our service with the following URL:

https://app.example.com/success?site_url=https://example.com&user_login=username&password=abcd EFGH 1234 ijkl MNOP 6789
Code language: Bash (bash)

The value of user_login is the WordPress login of the user, the password is 24 characters long without special characters. For better readability the password is split into groups of four characters, you can remove the spaces or leave them, that does not matter when making requests against the REST API with the credentials.

Checking and storing the application passwords

To make sure that the transferred credentials are correct, I chose the following process for a project of mine:

  • Before creating the application password, the user needs to enter the website’s URL of course.
  • I store the URL as a website entry in the database.
  • After authorization, the user is redirected to a URL that contains a hint to the website entry. This URL is only accessible by the user if he is logged into my service and owns that website entry.
  • I check if the URL from the site_url param matches the URL in the database.
  • I make a test request to the REST endpoint /wp-json/wp/v2/users/me?context=edit of the WordPress installation with the credentials I got via the URL.

If everything works and I get a REST response with data about the account, I also can check the user permissions on the WordPress site, if needed.

After that checks, I can save the credentials. And here is a problem: to make authenticated requests against the REST API, the credentials need to be sent in plaintext. So we need to be able to decrypt the stored credentials after storing them encrypted, not like for normal passwords, where no decryption is needed.

The post about implementing the feature names libsodium and Vault in that context. But if someone unauthorized gets access to the server of the third-party service, he has access to everything he needs to decrypt the credentials.

After reading more about libsodium and Vault, I decided that Vault is a little over the top for my use case, and I will build something with libsodium. My current approach is:

  • a server the is used for encrypting and decrypting the application passwords and a database that is used for storing the encrypted application passwords, and that is only used by the encrypting/decrypting server. That database does not store URLs of websites, so if someone has access to that database, he cannot do much with the information.
  • a small API that is used by my service to send requests to the website via the application passwords server.
  • maybe some secret information is sent with the request from the service to the application password server that is needed for decryption, so one would need access to both servers to decrypt the application passwords.

So the server that handles the user interface would only process the application passwords once, directly after authorization, and from this server, there would be no way to get application passwords and to encrypt them.

Using the application passwords

To use the application passwords for requests against the REST API, the basic auth method can be used. An example request from the make.wordpress.org guide looks like this:

curl --user "USERNAME:PASSWORD" https://example.com/wp-json/wp/v2/users?context=edit
Code language: Bash (bash)

In Laravel, that request could look like the following code:

<?php $response = Http::withBasicAuth( 'USERNAME', 'PASSWORD' )->get('https://example.com/wp-json/wp/v2/users?context=edit');
Code language: PHP (php)

Planned features for the future

The current state is that authenticating via application passwords results in the same capabilities that the user has who created the application password. Which users can use application passwords can be modified with the wp_is_application_passwords_available filter.

In the future, it should be possible to limit the capabilities to content editing or to the capabilities of a specific lower user role so that editors could create application passwords with author capabilities.

Conclusion

For me, application passwords made it to core just at the right moment. I already worked on my own solution to authenticate REST requests, but using application passwords is a lot better.

How about you, do you have already existing projects that could benefit from application passwords or projects that can only be built now that application passwords are in core?

3 reactions on »Application passwords in WordPress 5.6«

Bookmarks

Reposts

Leave a Reply

Your email address will not be published.