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