PHP OAuth Provider: Request Tokens

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.

The consumer requests a request token (see my earlier post about consuming OAuth), and as a provider, we need to handle that request. In my example, I chose to pass the variables as GET parameters, but you could adapt this to handle POST variables or information contained in HTTP headers.

OAuth Provider Code

We have the same block of code called on every request where we're negotiating OAuth, and it looks like this:

$this->provider = new OAuthProvider();
 
// set names of functions to be called by the extension
$this->provider->consumerHandler(array($this,'lookupConsumer'));
$this->provider->timestampNonceHandler(
array($this,'timestampNonceChecker'));
$this->provider->tokenHandler(array($this,'tokenHandler'));
 
// no access token needed for this URL only
$this->provider->setRequestTokenPath('/v2/oauth/request_token');
 
// now check the request validity
$this->provider->checkOAuthRequest();

The setRequestTokenPath() function is more important than it looks. Set this to the path of your request token endpoint, this ensures that the checkOAuthRequest doesn't expect a valid access token to be supplied - because you haven't got one yet! This confused me at first, because I could request a request token and get one, but the oauth extension always returned the 401 code.

Handler Functions

You also need to flesh out those three function names you gave for the handlers, the consumerHandler(), the timestampNonceHandler() and the tokenHandler(). My functions look something like this:

The consumerHandler ($db is a PDO object set elsewhere):

    public function lookupConsumer($provider) {
        $sql = 'select consumer_secret from oauth_consumers '
            . 'where consumer_key = :key';
        $stmt = $db->prepare($sql);
        $response = $stmt->execute(array(
            ':key' => $provider->token
            ));
        if($response) {
            $results = $stmt->fetchAll(PDO::FETCH_ASSOC);
            // just one row needed
            $consumer = $results[0];
        }
        $provider->consumer_secret = $consumer['consumer_secret'];
 
        return OAUTH_OK;
    }

In this function you should take the consumer key, retrieve the matching secret from your own database, and set it to the consumer_secret property of the $provider object. You should return the OAUTH_OK constant if everything seems OK to proceed; the OAuth extension will then use the secret you set to check the signature of the request.

The tokenHandler() can be empty at this stage as you don't need it:

   public function tokenHandler($provider) {
        return OAUTH_OK;
    }

Beware though, we will revisit this function in the next two posts in the series; when incoming requests have request/access tokens with them, we will check them in this function.

The functionality of the timestampNonceHandler function is entirely up to you and I've seen everything from an empty function returning OAUTH_OK to some quite sophisticated time and nonce checking. The function is here so that you can set restrictions on what values are valid for those two fields. For example if you have rules about how the nonce is generated, you would check here that the nonce used matches those rules. The timestamp is here to prevent replaying of request token requests if they are captured - it is normal to compare the given timestamp is within a few minutes of the local server time. Just like when you generate tokens and secrets, the appropriate level of security depends entirely on your application.

Storing and Returning the Request Token

If everything above goes to plan, then we need to generate, store and return a request token to the consumer. Again I include my table description from my example application:

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 | |
+----------------------+--------------+------+-----+-------------------+----------------+

The actual code which works with this looks like this:

   // bin 2 hex because the binary isn't friendly
    $request_token = bin2hex($this->provider->generateToken(4));
    $request_token_secret = bin2hex($this->provider->generateToken(12));
 
    $sql = 'insert into oauth_request_tokens set '
        . 'consumer_key = :consumer_key, '
        . 'request_token = :request_token, '
        . 'request_token_secret = :request_token_secret, '
        . 'callback = :callback';
 
    $stmt = $db->prepare($sql);
    $result = $stmt->execute(array(
        "consumer_key" => $this->provider->consumer_key,
        "request_token" => $request_token,
        "request_token_secret" => $request_token_secret,
        "callback" => $callback));
    $tokens = array('request_token' => $request_token,
      'request_token_secret' => $request_token_secret);
 
       echo 'login_url=http://api.local/oauth_login?' .
                 'request_token='.$tokens['request_token'].
                 '&request_token='.$tokens['request_token'].
                 '&request_token_secret='.$tokens['request_token_secret'].
                 '&oauth_callback_confirmed=true';

Note the simple query-string output being printed here. This is how OAuth likes its responses returned, so make sure you avoid any view handlers or anything else which will confuse the issue. Also this example is incomplete as it doesn't handle failures of database or anything, that is in an effort to keep the code samples relevant and short but I hope it isn't confusing to have them removed.

Request Tokens

This step is the simplest really, but when you get this far you have the basics of OAuth working already and although we have more to add as we authenticate our users and give access tokens, I certainly found that after this point I had falled into the most obvious traps and got over them! If there's anything I've missed, please add a comment, always pleased to hear others chiming in and sharing their experience.

edit: You can find the next installment of this series here - covering user verification

6 thoughts on “PHP OAuth Provider: Request Tokens

  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. hi..

    can you explain why
    'request_token='.$tokens['request_token'] is there twice in..

    echo 'login_url=http://api.local/oauth_login?' .
    'request_token='.$tokens['request_token'].
    '&request_token='.$tokens['request_token'].
    '&request_token_secret='.$tokens['request_token_secret'].
    '&oauth_callback_confirmed=true'

    ??

  3. Eirron: Sure! The first one forms part of the login_url, but it is also given separately because you need the request token later on when you sent through the verification to get the access token. So it is returned as a separate variable at this stage in order for it to easily be stored for later. Make sense?

    • sorry.. I must be missing something. I see the request_token is needed in login url and later sent thru verification.. but can't this be achieved by sending the request_token key/value once?

  4. I want to use this oauth provider code for Salesforce.Tel me if there any resourse available on internet to generate client id and client secret for for Salesforce (client).That means In my case Salesforce is a client which request id and secret from phpoauthprovider on linux based server

Leave a Reply

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

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>