On Core REST API Authentication

Having an API is well and good, but if there are no ways for third-party apps to actually authenticate and use the API, it’s not very useful.


While the framework for the REST API was merged into WordPress Core with the 4.4 release, the only means of using any endpoints that currently require authentication are what is known as ‘cookie authentication’ — that is, piggybacking off of the authentication cookies (plus a nonce) that WordPress Core sets in the browser when you log in traditionally to your WordPress site.  Unfortunately, that leaves the REST API as little more useful than the legacy `admin-ajax.php` file.

Fortunately, there are several authentication methods being worked on at the moment, in plugin form, for consideration of merging in to Core.

I’m heading up one of them, called Application Passwords.  In short, it lets a user generate as many single-application passwords as desired — one for each phone, desktop app, or other integration desired — and later revoke any application password whenever desired without affecting other applications or the user’s normal authentication.  The passwords are then passed with each request as Basic Authentication, encoded in the header of each request, as per RFC2617.

The other plugin is OAuth 1.0a authentication (spec).  Most OAuth usage across the internet is actually OAuth 2.0 — however, OAuth 2.0 requires HTTPS on the server side.  Ordinarily for most hosted services, this is not a problem.  However, for a distributed platform like WordPress, this is untenable due to the majority of sites not supporting HTTPS.  So an older, more complex specification — designed to not require HTTPS — had to be used.

For the record, I’m fully expecting to see an OAuth 2.0 plugin be built in the near future for use on sites that have a SSL certificate and support HTTPS.  However, that may not be very useful for app developers that want a ‘build once, run everywhere’ authentication method that will always be available.

Limiting Permissions

One of the discussions that came up with regard to Application Passwords is whether a REST API request that uses Application Password authentication should be able to modify Application Password endpoints.

Now, this is a very interesting question, and it can lead to many more questions — such as if an Application Password shouldn’t be usable to create or delete other Application Passwords, whether they should be allowed to do other user-administrative tasks (providing the relevant user has those permissions).  After all, if we’re preventing them from making a new Application Password, but they can just go and change the user’s actual password or email address, it’s a rather silly restriction.

So, there are several possiblilities.

First, you can just say “Any ways in to your account give full access to everything your account can do.  Be careful what applications and websites you give access to.” — the most basic, relatively easy to understand way.  Honestly, this is my preference.

Secondly, when creating a new Application Password or connecting a new client via oAuth, you could do something like … selecting what ‘role’ you’d like to give that connection.  For example, if your normal user account as an Administrator, but you’re connecting an app that’s intended just for writing blog posts, you may want to downscale your role for that authentication key to either an Author or perhaps an Editor.  An advantage to this is that it would be more cross-API — that is, it would work just as well with the legacy XML-RPC API, as with the new REST API.

This ‘role restriction’ method may be somewhat fragile, as it would need to only filter `current_user_can` and `user_can` — but only when checking the current user’s ID.  However, that may goof up some cron tasks that may run on the same request as the REST API request or other incendtal things.

Thirdly, we could do something REST API specific — either whitelist or blacklist REST API endpoints based on authentication key.  So, when either creating a new Application Password or authorizing a new OAuth connection, you would set rules as to what endpoints it can be used to hit.  Perhaps you’d want to allow all `wp/v2` namespaced endpoints, but no endpoints added by plugins to custom namespaces.  Or you want to only allow it to access core `posts` and `taxonomies` endpoints.  Or even something like allowing it to access anything but `plugins`, `themes`, `users`, or `options` endpoints.

The downside of this is that it won’t work with the legacy XML-RPC API and the user interface for it would likely be far more confusing for users.  It also could get problematic as permissions may vary for who can read `options` endpoints and who can write to them — or the like.  So then it may further complicate to allowing GET requests but not POST, PUT, DELETE requests to certain endpoints.

Your Thoughts?

In the end, I’m not sure what the best path forward is.  Maybe I’ve missed something.  But I am confident that we need to start paying more attention to authentication and permissions for the forthcoming REST API.  If you have any thoughts or remarks, please leave them in the comments.



10 thoughts on “On Core REST API Authentication

  1. George –

    This is neat. I’ll be playing with it this week. Can you comment on the relative security versus the existing basic authentication? I’m so far from being an expert in security matters, but it seems like this would have the same issue — transmitting an easily decodable password over potentially insecure HTTP. I get that the goal of having a separate API password, would in some cases limit the impact of a breach.

    Not sure there is a better solution for auth over HTTP. Genuinely hopping your going to explain why my concern is incorrect.


    1. Yes, if someone sniffs your packets and swipes your application password, they can use that application password to make requests using it. However, there are several important advantages of Application Passwords over Basic Authorization:

      1) Tokens are easily revoked, unlike normal passwords which need to be changed and then updated.
      2) Tokens can not be used to log into /wp-admin/ — they are good exclusively for API requests.
      3) It’s the same risk you’d be otherwise exposed to when logging in via wp-admin — the risk of someone sniffing the requests and swiping your cookies, in that instance.

      I believe that OAuth 1.0a handles this to an extent, as it uses the private keys to ‘sign’ requests, so that even if a request is sniffed, the most that t.hey would be able to accomplish would be to run the same request again, as they wouldn’t have the private key to sign a new request — and I believe it’s limited even further by timestamps being factored into the signing, but I’m not positive on that.

      In short, OAuth 1.0a is more or less designed to operate securely over unsecured connections.

      1. Good answer. I change my feedback to the following then:

        0) Excited about this. Need to dig I’m a see how I can help out.

        1) I think you need to make the benefits of this method, as well as the weakness more clear in your proposal.

        2) I wonder if there is a simple.way to make tokens time limited. Obviously there is, but that requires a request for token, which I’d imagine is just as susceptible the request being sniffed. I have to look at how that works in oAuth1.a and JWT.

  2. Perhaps you’d want to allow all wp/v2 namespaced endpoints, but no endpoints added by plugins to custom namespaces

    I like the notion of restricting tokens to certain namespaces – though it could be a false sense of security as a plugin that creates the namespace could obviously do whatever it pleases behind the scenes on your behalf.

    Overall though I think application passwords would be a huge win for WP-API adoption.

  3. I agree that authentication is the next big challenge for the REST API. I spent a couple months studying RFCs, reading about various attack vectors and building prototypes for different protocols — it’s enough to make anyone paranoid.

    Based on my research, I think Application Passwords offer some usability improvements, but little in the way of actual protection and may provide a false sense of security.

    Compared to Basic Authentication, the ability to revoke tokens without invalidating clients is very nice. However, like Basic Authentication, the password is sent over the wire in plaintext for every request. If clients are also able to change the authenticated user’s password, they can easily gain access to /wp-admin.

    Aside from passwords being leaked/captured, this also leaves it open to various other attack vectors that are covered by OAuth 1.0a and OAuth 2.0 over TLS, including:

    It’s not possible to prevent replay attacks.
    No way to verify message integrity, which is provided by the signature in OAuth 1.0a and TLS in OAuth 2.0.

    As a concept, Application Passwords are pretty similar to the Client Credentials flow in OAuth 2.0… without TLS.

    If Application Passwords aren’t going to distinguish between authentication and authorization, I think something like a one-legged OAuth 1.0a flow or HTTP Signatures would be more secure. Then the Application Password would be treated as a shared secret rather than being sent with every request.

    Considering the distributed and open source nature of WordPress, it has some unique challenges. Imagine a Super Cool SaaS requires users to generate an Application Password to connect their WordPress site to the service. The server then stores the Application Password in their database in plaintext and over time collects thousands of passwords. If the service’s database is compromised, the attacker suddenly has plaintext passwords for thousands of sites with an easily scriptable API.

    I haven’t really thought through scenarios like this much since I’ve mainly been concerned with implementing authentication for a single server with TLS, but I imagine this could be a problem for any API that uses long-lived access tokens.

    Ultimately, despite its complexity, an OAuth 1.0a provider is probably the best bet for core inclusion from a security standpoint. Another nice thing about OAuth 2.0 is that it has various standardized extensions, so it could also be interesting to have an OAuth 2.0 provider with extensions for for MAC and nonce/timestamp checks when TLS isn’t available.

  4. If an Application Password allows changing your user password, and changing other authentication related options, I’m not sure I see what the point is. As Brady suggests, isn’t this offering a false sense of security?

    I also think that suggesting that you select a “user role” for an Application Password is kind of missing the point. If you’re an Administrator and you restrict the Application Password to be an “Author,” so what? An Author can still change his/her own password. The app changes your password, logs into WP-Admin, and boom, its an Administrator.

    I agree that selecting specific namespaces to provide access to is way too complicated for the average user. If there can be hooks or filters that would allow developers to do this, awesome, but users shouldn’t have to worry about it.

    I would suggest that each Application Password should be given one of two very simple access levels: Standard Access, which lets it do everything your account can do EXCEPT messing with passwords or authentication settings in any way; this should be the default. And Full Control, which lets it do absolutely everything your account can do INCLUDING changing all your passwords; this should display a prominent warning when selected. The access level should be the user’s choice when creating an Application Password, but Standard Access should always be the default setting.

    Alternately, make Standard Access as described above the ONLY option out of the box, but provide hooks to allow developers to add other access levels; then if a user REALLY NEEDS an Application Password that gives full control of their account, they can always install a plugin for that.

    1. So, in this suggestion, Standard Access would be for administrating things like Posts, Taxonomies, Comments — basically everything that legacy XML-RPC supports, but Full Access would provide the whole kit and kaboodle?

      Where do you think third-party endpoints would fit in? Should the endpoints somehow have to ‘opt in’ to be allowed in the more limited ‘Standard Access’ set of endpoints, otherwise they’d only be available with ‘Full Access’ ?

      1. My assumption here is that, by default, we want to ensure an Application Password for a given user account can be disabled. Otherwise, what’s the point of using an Application Password at all? In order to do that, it should be impossible for the application using that password to give itself another access point to that user account, or for it to prevent the user from logging in. So it should not be able to change the user login password, or create additional Application Passwords. It seems to me those are the only things that need to be restricted.

        I’m certainly no security expert, so I’ll admit there could be issues here I’m not considering, but restricting only those specific features seems to me to be sufficient to ensure the user retains control of their site. Even if the application does something malicious, there’s nothing it can do to prevent the user from logging in and deleting its Application Password.

        Personally, I wouldn’t be opposed to a more advanced capability-based solution (which most users should be accustomed to by now from mobile devices anyway), but I realize that’s far more complicated. Simply blocking applications from changing passwords by default feels like a reasonable bare minimum level of security to me.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s