{"id":6129,"date":"2021-08-15T17:40:09","date_gmt":"2021-08-15T15:40:09","guid":{"rendered":"https:\/\/florianbrinkmann.com\/en\/?p=6129"},"modified":"2021-08-15T17:40:10","modified_gmt":"2021-08-15T15:40:10","slug":"application-passwords","status":"publish","type":"post","link":"https:\/\/florianbrinkmann.com\/en\/application-passwords-6129\/","title":{"rendered":"Application passwords in WordPress 5.6"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">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 <a href=\"https:\/\/make.wordpress.org\/core\/2020\/09\/23\/proposal-rest-api-authentication-application-passwords\/\">Application Passwords into core<\/a> and with that a user flow, which can be used by third-party services to ask for authorization for later requests.<\/p>\n\n\n\n<!--more-->\n\n\n\n<h2 class=\"wp-block-heading\">Authorization flow<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">On make.wordpress.org there is a <a href=\"https:\/\/make.wordpress.org\/core\/2020\/11\/05\/application-passwords-integration-guide\/\">guide on how to use the application passwords feature<\/a>, 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:<\/p>\n\n\n\n<ol class=\"wp-block-list\"><li>Check if the WordPress installation supports the application passwords feature.<\/li><li>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.<\/li><li>Check and save the credentials.<\/li><li>Use the credentials for authenticated requests.<\/li><\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">Check if the installation supports the application passwords feature<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">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 <code>https:\/\/example.com\/wp-json\/<\/code>. The first requirement is that we get information back and no plugin is used to disable the REST API.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If we get a JSON response, the path <code>authentication.application-passwords.endpoints.authorization<\/code> contains the URL for redirecting the user to create an application password \u2013 in my case that would be <code>https:\/\/florianbrinkmann.com\/en\/wp-admin\/authorize-application.php<\/code>. 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 <code>http<\/code> instead of <code>https<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Redirect user to the authorization endpoint<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">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:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><code>app_name<\/code> 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.<\/li><li><code>app_id<\/code> (optional) is a <a href=\"https:\/\/github.com\/WordPress\/wordpress-develop\/blob\/e3f28a9a11fd2c6eea7862a616738ace01d09d26\/src\/wp-includes\/functions.php#L6990-L7038\">UUID<\/a>, 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.<\/li><li><code>success_url<\/code> (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 <code>site_url<\/code>, <code>user_login<\/code>, and <code>password<\/code>. If not set, the created password is shown to the user so that he can enter it manually on the third-party service site.<\/li><li><code>reject_url<\/code> (optional) is the URL to redirect the user to after the creation of an application password failed. If not set, the <code>success_url<\/code> gets the parameter <code>?success=false<\/code> and is used for the redirect. If that also does not exist, the user is redirected to the WordPress dashboard.<\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><code>success_url<\/code> and <code>reject_url<\/code> need to be <code>https:\/\/<\/code> URLs or app-specific protocols. <code>http:\/\/<\/code> is not allowed.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Because the user flow is a not-so-great flow without the <code>success_url<\/code>, we have an URL with the following schema which we use to redirect the user to:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"Bash\" data-shcb-language-slug=\"bash\"><span><code class=\"hljs language-bash shcb-wrap-lines\">https:&#47;&#47;example.com\/wp-admin\/authorize-application.php?app_name=Beispielname&amp;success_url=https:\/\/app.example.com\/success<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Bash<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">bash<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">In case of success, the user comes back to our service with the following URL:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"Bash\" data-shcb-language-slug=\"bash\"><span><code class=\"hljs language-bash shcb-wrap-lines\">https:\/\/app.example.com\/success?site_url=https:\/\/example.com&amp;user_login=username&amp;password=abcd EFGH 1234 ijkl MNOP 6789<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Bash<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">bash<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">The value of <code>user_login<\/code> 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.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Checking and storing the application passwords<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">To make sure that the transferred credentials are correct, I chose the following process for a project of mine:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Before creating the application password, the user needs to enter the website\u2019s URL of course.<\/li><li>I store the URL as a website entry in the database.<\/li><li>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.<\/li><li>I check if the URL from the <code>site_url<\/code> param matches the URL in the database.<\/li><li>I make a test request to the REST endpoint <code>\/wp-json\/wp\/v2\/users\/me?context=edit<\/code> of the WordPress installation with the credentials I got via the URL.<\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">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.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">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.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The post about implementing the feature names <a href=\"https:\/\/github.com\/jedisct1\/libsodium\">libsodium<\/a> and <a href=\"https:\/\/www.vaultproject.io\/\">Vault<\/a> 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.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">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:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>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.<\/li><li>a small API that is used by my service to send requests to the website via the application passwords server.<\/li><li>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.<\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">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.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Using the application passwords<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">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:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"Bash\" data-shcb-language-slug=\"bash\"><span><code class=\"hljs language-bash shcb-wrap-lines\">curl --user <span class=\"hljs-string\">\"USERNAME:PASSWORD\"<\/span> https:\/\/example.com\/wp-json\/wp\/v2\/users?context=edit<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Bash<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">bash<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">In Laravel, that request could look like the following code:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-meta\">&lt;?php<\/span>\n$response = Http::withBasicAuth(\n    <span class=\"hljs-string\">'USERNAME'<\/span>,\n    <span class=\"hljs-string\">'PASSWORD'<\/span>\n)-&gt;get(<span class=\"hljs-string\">'https:\/\/example.com\/wp-json\/wp\/v2\/users?context=edit'<\/span>);<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h2 class=\"wp-block-heading\">Planned features for the future<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">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 <code>wp_is_application_passwords_available<\/code> filter.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">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.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Conclusion<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">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.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">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?<\/p>\n","protected":false},"excerpt":{"rendered":"<p>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.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"wpf_show_in_dewp_planet_feed":false,"flobn_post_versions":"","webmentions_disabled_pings":false,"webmentions_disabled":false,"lazy_load_responsive_images_disabled":false,"footnotes":""},"categories":[37],"tags":[],"class_list":["post-6129","post","type-post","status-publish","format-standard","hentry","category-wordpress"],"wp-worthy-pixel":{"ignored":false,"public":"853818a475224d07b329fd87410ba9af","server":"vg07.met.vgwort.de","url":"https:\/\/vg07.met.vgwort.de\/na\/853818a475224d07b329fd87410ba9af"},"wp-worthy-type":"normal","_links":{"self":[{"href":"https:\/\/florianbrinkmann.com\/en\/wp-json\/wp\/v2\/posts\/6129","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/florianbrinkmann.com\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/florianbrinkmann.com\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/florianbrinkmann.com\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/florianbrinkmann.com\/en\/wp-json\/wp\/v2\/comments?post=6129"}],"version-history":[{"count":3,"href":"https:\/\/florianbrinkmann.com\/en\/wp-json\/wp\/v2\/posts\/6129\/revisions"}],"predecessor-version":[{"id":6132,"href":"https:\/\/florianbrinkmann.com\/en\/wp-json\/wp\/v2\/posts\/6129\/revisions\/6132"}],"wp:attachment":[{"href":"https:\/\/florianbrinkmann.com\/en\/wp-json\/wp\/v2\/media?parent=6129"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/florianbrinkmann.com\/en\/wp-json\/wp\/v2\/categories?post=6129"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/florianbrinkmann.com\/en\/wp-json\/wp\/v2\/tags?post=6129"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}