Authentication in WordPress 2.8

Use Case

I’ve spent a lot of time working with the WordPress authentication system. I took over the OpenID plugin for WordPress two years ago, and was hired by Vidoop last May to work on the DiSo Project full time. Last summer, Matt Mullenweg invited me to talk at WordCamp SF 2008 about OAuth. As you can see in my slidedeck, it was a lot of smoke and mirrors at that point… we didn’t have OAuth in WordPress, although it was on the roadmap for 2.7.

We’ve had an OAuth plugin for a little while that Stephen Paul Weber wrote, but it wasn’t until a couple of months ago that I finally sat down to polish it up. The first use-case we wanted to tackled was XML-RPC, so I got to work with Joseph Scott. Having OAuth authentication with XML-RPC would allow for blog clients like MarsEdit or the WordPress iPhone app to communicate with your blog without having to share your WordPress password.

Problem

It wasn’t very long before we butted up against my biggest complaint about the WordPress authentication system – it is very “username/password” centric. There are places in the authentication code where it bails out prematurely if the username or password are missing. This isn’t a problem if your plugin simply wants to authenticate the user against a different password store like LDAP; in fact that works quite well.

The problem is that there are a number of authentication systems widely deployed in the wild (SAML, OpenID, OAuth, etc) that do not fit the standard model of username and password. You can look at the OpenID plugin to see some of the more interesting things that need to be done in order to make it work on the various versions of WordPress. However, when it came to hooking OAuth into the WordPress XML-RPC endpoint, there simply was no way to hack around it… we had to change some of the underlying assumptions.

It’s worth noting one additional requirement we had. Because the wp_authenticate() function, which does most of the heavy lifting for WordPress authentication, resides in pluggable.php it is possible for a plugin to replace the function entirely and authenticate the user however they want. The problem with this solution is that many authentication mechanisms don’t know if they should be invoked without examining the request. If wp_authenticate is replaced, and then the plugin determines it shouldn’t intervene, then it’s already too late. There is no way to pass the function call back to the standard version of wp_authenticate(). This is actually the case for all functions in pluggable.php. One possible solution is to create wrapper functions for everything, which I initially advocated. Instead, Peter Westwood came up with a better solution using a well-established model, which we ended up using for the new authentication system.

Solution

It took far more planning than actual coding, but we finally developed a solution that breaks the dependence on a username and password, but maintains backward compatibility with existing plugins that hook into the authentication code. WordPress 2.8 includes a new filter called authenticate which is passed three parameters: a mixed value (either a WP_User object, a WP_Error object, or null), along with the username and password (either or both of which may be null). All of the standard WordPress authentication logic has been moved into two functions that implement this filter, both with relatively low priority.

  • wp_authenticate_username_password() (priority 20) includes the standard logic for authenticating using a standard username and password. It still calls the wp_authenticate_user filter, so plugins that rely on that should be fine. This function also performs the check for an empty username or password.

  • wp_authenticate_cookie() (priority 30) is only added into the filter chain when the user is authenticating via wp-login.php and does the normal checking for the WordPress authentication cookie.

Both of these functions first check to see if the first parameter passed in is a valid WP_User object, and immediately stop if it is. This allows plugins to add their own functions into the filter chain which populate the WP_User object using whatever means they see fit. WordPress still takes care of setting the authentication cookie when appropriate, so plugins need only worry with authenticating the user and returning a valid WP_User object.

So what will this look like for plugins? Well, the OAuth plugin for WordPress isn’t finished yet, but the function below should be pretty close to the final version. While there is of course a lot more code for actually implementing OAuth, this is the only hook into the WordPress authentication system needed to make it work. Note that this function doesn’t care about the username and password parameters that are available from the authenticate hook… other plugins may use them.

add_filter('authenticate', 'oauth_authenticate');

/**
  * If the current request was signed using a valid OAuth access token, verify 
  * the request and return the associated user.
  *
  * @param WP_User|WP_Error|null $user authenticated user
  * @return WP_User|WP_Error|null OAuth authenticated user, if request was signed
  */
function oauth_authenticate($user) {
    if (Auth_OAuth_Signer::requestIsSigned()) {
        $oauth_server = oauth_server();
        $user_id = $oauth_server->verify();
        if ($user_id !== false) {
            $user = new WP_User($user_id);
        }
    }

    return $user;
}

For Plugin Authors

If you’re currently hooking into the WordPress authentication system, especially if you’re providing a custom implementation of wp_authenticate(), take a look at the new authenticate hook. If you are relying on the wp_authenticate action hook, you should also look closely to see if the new hook will do what you need. We left the wp_authenticate hook in place for now, but I’m pretty sure it’s no longer necessary and will likely be removed in future releases. If you are using the wp_authenticate_user hook exclusively, then you’re probably fine, but it’s probably still a good idea to take a look at the new stuff.

So, OAuth in WordPress?

We made additional changes to the WordPress XML-RPC code to make it use the new authentication system appropriately, so it is now possible to hook OAuth into WordPress without any core modifications. We do in fact have a basic OAuth plugin that works with the trunk version of WordPress. However, I don’t think I’m going to push to have the OAuth code included in WordPress 2.8 for two reasons:

  • the OAuth libraries are in flux right now. There have been two main PHP libraries that people have used for OAuth, both with their own strengths and weaknesses. I’m currently working with the oauth-php community to combine these libraries using the best parts from each, and a new clean architecture. This effort can be found on github. (This library also requires PHP5 which is a deal breaker for WordPress… not sure how we’ll manage that.)

  • Because OAuth has the potential to be such an important part of how third party clients interact with a WordPress blog, I want to make sure we get this right. Personally, I’d feel much more comfortable getting some real world experience with this code in a slightly more constrained environment by releasing it as a plugin first. Once we’ve done that and are comfortable with how it integrates into WordPress (plugin API, admin interface, database schema, etc), I’m all for making it a core part of WordPress.

Comments and responses

I certainly agree that WordPress is pretty hefty as it is. However, as more and more services are using OAuth for API access, it begins to make a lot of sense that it should be a core capability of the platform. PollDaddy, an Automattic property, launched a new API today which does not use OAuth. Perhaps they would have used OAuth if it were available in WP core? Who knows.

I definitely think OAuth in WP core makes a lot more sense than OpenID in WP core… whether they realize it or not, there are far more users that would make use of OAuth if it were available to plugin authors.

Very interesting. I’d just love to have OpenID in the core, not by default but as an option or alternative one can select when setting up & be able to change later too. I hate blogs making me get a whole new login account all the time. Which makes me hate forcing that on others.

(This comment turned into a bit of a brain dump, so I apologise in advance.)

When I attacked this problem in a different system (which is unfortunately not open source) I solved it by identifying three separate pluggable components:

  • Authentication frontend. This is responsible for showing some UI, which is usually a HTML form. This is nothing more than a standard action, controller, or whatever such things are called in your framework that happens to call into the “create session” and “destroy session” methods on a particular configured session manager.

  • Session manager. On each request, this component is asked “who is the remote user?” and responds with a user object. It also can optionally provide “start session” and “end session” methods if it supports session state, but since HTTP auth is stateless anything HTTP auth like does not provide these methods. This was implemented as a plugin class with a specific interface.

  • Username/password backend. Used by frontends that deal in usernames and passwords (which, in this case, were either a username/password web form or a basic authentication frontend) to actually check a username/password pair, since username/password is a common enough case for it to be worth having an extra layer for. This was again implemented as a plugin class with a specific interface.

The default case is a HTML forms username/password-based frontend for logging in, a standard “ask the user and then close the session” frontend for logging out, a cookie-based session manager (which supports session state) and a user/password backend that looks at the local DB. You can swap out the user/password backend to support LDAP or whatever.

To add OpenID support for end-users, you use an OpenID frontend, which can either replace or supplement the username/password frontend. The rest of the stack remains the same as username/password.

For an API that uses OAuth, you use a stateless session manager that can validate OAuth arguments and some other external, OAuth-specific gunk to do the request token endpoint and so on. You don’t need any frontend in the traditional sense, but you do need to provide some “frontend” to do the OAuth request token endpoint and so forth. Since it’s not username/password based, you don’t need a username/password backend.

It looks like this Wordpress approach conflates the frontend with the session manager, unless I’m missing something. This presumably means that the thing that does the username/password authentication is creating the session cookie and is therefore tightly coupled with the cookie-based authenticator. It’s also not obvious to me how I’d implement Basic auth support if the username/password thing can only deal with username/password submitted via an HTML form. Maybe I’m misunderstanding, though.

One thing missing from my approach is that in the OAuth case you generally have two authenticated parties: the application that’s making the request and the user the request is on behalf of. I didn’t ever solve this problem, and I don’t think it’s really addressed by your solution either unless I’m missing it.

It seems like the solution in both cases is to split the concept of the remote user in two; there’s the user that’s making the request and the user they’re acting as. In all except the OAuth case they’re the same. It does seem kinda lame to bleed OAuth-specific concepts into the main API, but this would in theory be useful if you wanted to implement some other delegated auth scheme.

There is definitely a little bit I left out because it wasn’t necessarily relevant to the post… wp_authenticate is sort of a manager for your backend authentication methods. Rather than switching out one for another as you describe, you basically add your authentication method somewhere in the chain and the first one to authenticate the user wins.

During a typical user login flow, wp_authenticate is not called directly by the front-end handler. Instead, wp_signon is responsible for taking the HTML form post from wp-login.php, calling wp_authenticate to validate the credentials, and then setting the authentication cookie for the user. When using OAuth with XML-RPC, wp_authenticate is called directly (with an empty username and password), so there is no state management.

There are probably still a few unnecessary layers in the WordPress authentication code as a result of multiple redesigns in the past. I’m sure those will get cleaned out at some point after they’ve been formally deprecated for a time, but it’s really not too bad now. For basic auth, you’d just have a custom frontend handler that calls wp_authenticate, but doesn’t bother with the session cookie.

As for the problem of users versus applications authenticating… I have definitely spent a lot of time thinking about this. There are notes within the WordPress auth code that make reference to a conceptually distinct “logged in user” and “current user”, though I can’t actually find anywhere in the code where these are separated… perhaps just a remnant of an old design. Right now the OAuth plugin simply authenticates the application as the user with full privileges… it’s not ideal, but it’s absolutely no worse than doing XML-RPC with the user’s password. However, because the WordPress permissions system is tied to the WP_User class, my plan is to create a custom WP_User subclass that has the ID of the actual user, but with limited permissions which match the scope of the OAuth token. Having wp_authenticate simply return a WP_User object makes this very clean.

“I hate blogs making me get a whole new login account all the time. Which makes me hate forcing that on others.” is precisely the reason why OAuth in WordPress makes more sense than OpenID. I already have accounts with Facebook and Google etc. I also have OpenID but I’d rather click one button than enter a URL.

Will’s been doing great work here.

I’m excited at the prospect of having OAuth support in core WordPress. I see this as an important foundation piece that will allow WordPress to expand it’s abilities as an application platform.

@Ramon: the plugin isn’t in the WordPress directory yet… that’s intentional. It’s not not quite ready for users.

@Khürt: I think you’re misunderstanding what OAuth is for… it’s not for authenticating to a website yourself, it’s for API authentication. What you’re describing is still OpenID, whether you initiate with a big button or by typing a URL.

@Stephen: actually no, we haven’t gotten AtomPub working yet. I guess I just forgot :). I’ll definitely try and get those changes in before 2.8 releases.

@Will: similar to @Khurt, does OAuth’s chaining of authentication (which sounds a lot like Apache’s logic) make it easier to swap in different autentications (such as LDAP, which actually isn’t as clean as you imply) or the possibility of chaining an OpenID authentication-check there?

To be more brief: does it ismplify the rest of the auth stuff?

Allan: just to clarify, the new chaining of authentication (which is a lot like Apache) is a feature of WordPress 2.8, it has nothing to do with OAuth directly. These changes to the WordPress authentication system are not specifically about enabling OAuth, although that was the use case that prompted them. The idea behind these changes is to make WordPress authentication much more pluggable and clean regardless of what authentication method you’re using. Looking back at the old auth code, you’re right, even something like LDAP wasn’t terribly clean integration. The new hook will make this much, much easier for all authentication plugins.
@Will Hey - just wanted to thank you for pointing out the issues - I will have to try to sit down and sort this one out. If you have any suggestions, I’m all ears.
Information about plugin localization can be found on this ticket. Short answer – I finally am getting around to supporting translations and including them in the plugin. Translations should be attached to that ticket on the Google Code project.
[...] WPEngineer explains how to build a Wordpress 2.8 widget, using object-oriented php; Joseph Scott how to manage the new features of the XML-RPC and AtomPub with Baker; Weblog Tools Collection collected a list of articles dealing with Wordpress 2.8; Justin Tadlock explains the management and customization of taxonomies in the new version; Will Norris wrote an article about new features in authentication. [...]
[...] WordPress 2.8?????????????????????????????????WordPress 2.8??????????????????WordPress????????????????????????Keith Dsouza?????WordPress 2.8???WordPress Codex – Version 2.8WordPress?????2.8??????????????Custom Taxonomies in WordPress 2.8 – Justin TadlokWordPress 2.3 ???Tag??????2.8????????????????WordPress??CMS??????????Build A WordPress 2.8 Widget – WP Engineer??WordPress 2.8??Widget API, ????Widget????????WordPress 2.8 and the body_class() Function – Nathan RiceWordPress 2.8?????body_class()?????????????????class??????????????????????????Authentication in WordPress 2.8 – Will Norris????OAuth???????????????????????????WordPress????????????????Loading JavaScript in Footer in WordPress 2.8 – Lester Chan??????????JavaScript???????????WordPress 2.8????????????http://parandroid.com/wordpress-28-tips-for-developers/ [...]

Hi WIll, Thank you for this write up. I have an LDAP authentication plugin that currently relies on replacing wp_authenticate(). I did not notice the new filter until this morning as my plugin continued to work after the transition from 2.7.x to 2.8.x.

I do have one question for you that I am having troubles answering. It seems that the filters are applied in order of priority. For example, authenticating with username/password in WP is set as a priority 20 while using a cookie is set at 30. I assume my integration should use 10 as that seems to be the default for added functionality.

I also grasp the idea of deferring to higher priority plugins: if ( is_a($user, 'WP_User') ) { return $user; }

However, when it comes to authentication a chain of multiple authentication methods which can say yea/nay to a username/password combination is inherently insecure. For instance, if my plugin attempts to log the user in with LDAP and fails, it should fail permanently, not give the same credentials a shot at the local database.

This widens the effective attack target and essentially creates two passwords (or more) that can access one username's account.

In my current architecture I have handled this by rewriting wp_authenticate as I see fit. I allow users to specify a login mode that either permits failed logins to hit the wp system for another try or fails permanently.

However, using filters it seems that it will simply keep moving down the chain regardless of what I do. Is this true? Am I missing something obvious?

Thanks in advance, Clif

I think Oauth is not necessary anymore, because Facebook Connect is coming more and more. So do i use Facebook connect on my blog at: http://www.androidspiele.net

What do you think?

I know I am too late on this, but do you have a working version of the oAuth authentication for WP as posted in this http://www.slideshare.net/willnorris/wordpress-oauth-presentation

I am working on something related and wanted oAuth, any help will be appreciated. Since you do not have a subscribe to comments here I would appreciate a reply by email.

Is there way to do this yet that anyone knows of? I would like to allow users to authenticate to my wordpress site using an oauth compliant service. Is there a plugin based on the thinking in this article? Has it been added to the Wordpress core?

I have seen some plugins us a filter on init() to force authentication. I am trying to force users to have to had authenticate as well as the authentication coming from my app.

Seem to be running into issues between filter on init and authenticate mostly I think because I don't understand what the functions should do wordpress wise with setting cookies for rest to work.

Any ideas?