PHP OAuth Provider: Authenticate User

I’ve been working with OAuth, as a provider and consumer, and there isn’t a lot of documentation around it for PHP at the moment so I thought I’d share my experience in this series of articles. This relates to the stable OAuth 1.0a spec, however OAuth2 has already started to be adopted (and differs greatly). This article uses the pecl_oauth extension and builds on Rasmus’ OAuth Provider post. This post is the third in the series, following on from the ones about the initial requirements and how to how to handle request tokens.

This phase is probably the most familiar to us as developers, as it’s simply a login form. The consumer will send the user to us at the URL we provided in the request token, and the user will have the request token key as a parameter. The access control on this page will look the same as on the rest of the website; if the user has a session already then the page is displayed, otherwise they must be logged in to see it.

Request Token Verify

First of all we need to be sure that the request token that has been supplied is valid. For me, the code looks something like this:

        $sql = 'SELECT request_token FROM oauth_request_tokens
            WHERE request_token = ' . $this->db->escape($token) . '
            AND authorised_user_id IS NULL';
        $query = $this->db->query($sql);

        $result = $query->result();
        if(count($result) > 0) { 
            return true; 
        }
        return false; 

Request tokens can only be used once, so if there is already a user associated with this one, something is wrong and we should not accept it. If the token exists and is awaiting user information, then that’s all good and we can go ahead.

Grant or Deny

Once we know who the user is and we’ve checked the token, we present them with the option to grant or deny the access that is being requested. In my system, I put the request token in the user’s session, rather than putting it to the form and then accepting it back again, just so I can be sure that nothing unexpected is happening. Depending on whether the user accepts or not, we will take different action.

Deny

This is the simplest case, so let’s look at it first. If the user denies access, we delete this request token. It can only be used once, and if the user has denied it, then the token becomes invalid. You can either literally delete it or flag it as invalid – mine are deleted so that I don’t have to remember to come back and clean up the tables later.

Grant

If the user grants access then we need to store some information at this point. First we generate a verifier code. My application supports the “oob” parameter which means that users will sometimes be re-typing the code from one application to another, so I’m keeping it short to make that easier! In fact, it’s trivial (read the comments to find better ways to do this, I feel confident people will suggest some!)

substr(md5(rand()), 0, 6);

We then take the verifier code and user_id, and add them to our table against the appropriate request token. As a reminder, this is our table structure:


desc oauth_request_tokens;
+----------------------+--------------+------+-----+-------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------------------+--------------+------+-----+-------------------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| consumer_key | varchar(30) | NO | | NULL | |
| request_token | varchar(8) | NO | | NULL | |
| request_token_secret | varchar(32) | NO | | NULL | |
| callback | varchar(400) | YES | | NULL | |
| verification | varchar(20) | YES | | NULL | |
| authorised_user_id | int(11) | YES | | NULL | |
| created_date | timestamp | NO | | CURRENT_TIMESTAMP | |
+----------------------+--------------+------+-----+-------------------+----------------+

We simply update the verification and authorised_user_id columns appropriately, then grab the callback so we can forward the user back to the consumer; my code is as follows:

        $verification_code = substr(md5(rand()), 0, 6);
        $sql = 'UPDATE oauth_request_tokens SET authorised_user_id = ' 
            . $this->db->escape($user_id) . ',
            verification = "' . $verification_code . '"
            WHERE request_token = ' . $this->db->escape($token);
        $query = $this->db->query($sql);

        if ($this->db->affected_rows() == 1) {
            $fetch_sql = 'SELECT callback, verification FROM oauth_request_tokens
                WHERE request_token = ' . $this->db->escape($token); 
            $fetch_query = $this->db->query($fetch_sql); 

            $result = $fetch_query->result();
            return $result[0];
        }

Verifier Codes

If the consumer had a callback URL, we simply return the user to them with the verifier code attached as a parameter, otherwise we output the verifier for the user to take and use as instructed by the consumer application.

               if($oauth_info->callback == "oob") {
                    // special case, we can't forward the user on so just display verification code
                    $view_data['verification'] = $oauth_info->verification;
                } else {
                    // add our parameter onto the URL
                    if(strpos($oauth_info->callback, '?' !== false)) {
                        $url = $oauth_info->callback . '&';
                    } else {
                        $url = $oauth_info->callback . '?';
                    }
                    $url .= 'oauth_token=' . $oauth_info->verification;
                    redirect($url);
                    exit; // we shouldn't be here 
                }

Verified

At this point, the user has authorised (or not) the request token, and the consumer has the verifier code. The final step in the dance is to exchange the known information for a valid access token, which we will cover in the next post.

6 thoughts on “PHP OAuth Provider: Authenticate User

  1. I’ve been working with OAuth, as a provider and consumer, and there isn’t a lot of documentation around it for PHP at the moment so I thought I’d share my experience in this series of articles. This relates to the stable OAuth 1.0a spec, however OAuth2 h

  2. Pingback: Helpful Links Every Developer Should Have | The PHP Coder's Judy Chop

Leave a Reply

Please use [code] and [/code] around any source code you wish to share.

This site uses Akismet to reduce spam. Learn how your comment data is processed.