This tutorial is for programmers who are struggling with code that needs to interface directly with an OAuth flow. This is not a general introduction to OAuth.
Yahoo, Google, Twitter and LinkedIn are just some of the services that use OAuth for secure API calls.
The OAuth flow can seem byzantine with multiple steps that must be completed before you can make a signed API call.
I have broken down the OAuth flow into individual, self-contained PHP scripts that can be run from the command line, from requesting a token to making a signed API call.
This tutorial focuses on interfacing with that Yahoo Social APIs using OAuth. Yahoo publishes SDKs that make your life easier, but if you need to debug OAuth problems, then you need to understand the OAuth requests and responses in detail.
My scripts are deliberately devoid of any web session interactions, database interactions, or even command line option handling, just to keep them as simple as possible. It’s straight, standalone procedural code, no objects. If you want to change something, just edit the code and run it.
The goal of the code is to be as transparent as possible, to show you exactly what is sent and what is received. If you look at the code in getreqtok.php, getacctok.php, and refacctok.php, you can see that they can be combined easily into one function, but they are separated for clarity.
For the rest of this tutorial, I will refer to Step numbers which are documented in the YDN Oauth Quick Start Guide’s, Chapter 4, OAuth Authorization Flow.
Input: select the list of apis you want to call.
Output: consumer key and consumer secret.
This is self explanatory and requires no code. At the end of the process, you will have a consumer key and a consumer secret. Please keep the consumer secret secret; it is used to sign all of your requests to Yahoo.
input: consumer key and secret
Getting a “request token” is the first step on the road to making a signed API call. If a user has never granted your application permission to do something on his behalf, this is where you start.
Take a look at the getreqtok.php script. The get_request_token function is straight forward. You’ll find most of the supporting functions in either globals.php or oauth_helper.php.
Remember to set the OAUTH_CONSUMER_KEY and OAUTH_CONSUMER_SECRET in globals.php before running the script.
Since this is a command line script, just run it like this
Change the $debug variable from 0 to 1 in globals.php. Rerun the script, and observe the raw request and header and response.
Change the $usePost variable and see how things differ when using HTTP POST instead of GET.
Note that the $callback parameter must be the string ‘oob’ or a url that points back to the domain that you registered with Yahoo when you obtained the consumer key in step 1. The callback will be invoked at the conclusion of step 3.
The get_request_token call is a good endpoint to test your hmac-sha1 signature code, because this is the simplest call to make. You have no tokens at this point nor token secrets.
Try varying the $useHmacSha1Sig variable. This will exerise the code for generating both types of signatures. In your production code, of course, just pick one signature method for this call and stick to it.
A successful response to this step will contain a [request] token, a [request] token_secret, and in Yahoo’s case, an xoauth_request_auth_url. If you have a web application, you can pop-up a window and open this url. If you have a desktop application, instruct the user to cut-and-paste this auth_url (decoded first) into a browser somewhere.
input: xoauth_request_auth_url (step 2)
There is no code here. For the purposes of this tutorial, cut-and-paste the xoauth_request_auth_url from the conclusion of step 2 into a browser and authenticate yourself.
This step must be done in a web browser of some type, because user authentication must be done on a yahoo server over an SSL connection. This is much safer than having some local application collect a username and password, and sending it to some authentication server.
At the conclusion of this step, you will receive a short string called an “oauth verifier”. If you used ‘oob’ as the callback in step 2, then a web page with the oauth verifier will be shown to the user. Copy this value down and insert it in the script in step 4. If you are writing a web application, your callback url (from step 2) will be invoked with oauth_verifier as one of the parameters.
input: consumer key and secret (step 1),
input: request_token and secret (step 2),
input: oauth_verifier (step 3)
output: access_token, access_token_secret, session_handle
You’re almost there. At the conclusion of step 4, you will receive an access token that allows you to call all the API end points that you initially signed up for in step 1.
The script getacctok.php demonstrates how to perform this step. It is very similar to the getreqtok.php script from step 2. Only the inputs are different. Fill in the request token and request token secret from step 2, and the oauth verifier from step 3, and run the script.
In the script’s output, note the [access] oauth_token, [access] oauth_token_secret, oauth_session_handle, and the xoauth_yahoo_guid. The access token and secret will be used to sign API calls. When this token expires in 1 hour, you will need it and the session handle to obtain a new access token.
The xoauth_yahoo_guid is the GUID of the user, an id that uniquely identifies the user who authorized you to act on his or her behalf. This will be used when you make an API call.
input: consumer key and secret (step 1),
input: old_access_token, old_access_token_secret, session_handle (all from step 4)
output: new_access_token, new_access_token_secret
This is NOT a required step until your access token expires. As soon as you complete step 4, you can go straight to making API calls. Access tokens are good for 1 hour, so you only need to refresh the access token if you receive a token_expired error from any OAuth call, or you’re keeping track of time and want to preemptively refresh before the hour is up.
Look at the script refacctok.php. Again, this script is very similar to both getreqtok.php and getacctok.php with the exception of input variables. Fill in the access token, access token secret, and session handle that you obtained from step 4 and run the script.
Make sure that the access token you enter into this script is decoded, i.e. it should read ‘A=blah’, not ‘A%3Dblah’
The script should output a new access token and accompanying access token secret.
Making an API call
input: api_url and data
input: consumer key and secret (step 1)
input: access_token and secret (step 4)
output: response from api call
This is the moment you have been waiting for. Actually making an API call to get the contacts of a user or add to the user’s contact list. You can keep using the same access token and secret (from step 4) over and over again for all API calls, until that token expires (in 1 hour).
All the steps above have been for oauth handshaking, and the connections have been over SSL (https), therefore the simpler PLAINTEXT signature method could have been used to sign the request. The actual API calls, however, are all over unencrypted port 80, so you must use the HMAC-SHA1 signature now.
This is the step where most people run into trouble, because this will have been the first time they have tested their oauth hmac signature code. If you followed my advice from step 2 above, then you should already have good confidence that your hmac signature code works. It is much simpler to debug your signature code in step 2 than here, when you are actually making an API call.
– Calling Contacts API –
Edit the script callcontact.php and enter the access token, access token secret, and guid, all obtained from step 4. Make sure that the access token you enter into this script is decoded, i.e. it should read ‘A=blah’, not ‘A%3Dblah’
Run the callcontact.php script, and it should fetch the first 5 contacts of a user identified by a GUID (from step 4). This script also shows what the Contacts API end point looks like, and demonstrates how to use both matrix parameters and query parameters in the same url.
Edit the script postcontact.php and run it to add a new contact. Pay particular attention to the POST body format and what is and is not part of the signature base string when formulating the request to the server. An OAuth-signed POST or PUT request is the trickiest type of service call to make.
- Calling YQL -
YQL essentially allows you to “select * from Internet”.
Edit the script yql.php and enter the access token and access token secret that were obtained from step 4. Make sure that the access token you enter into this script is decoded, i.e. it should read ‘A=blah’, not ‘A%3Dblah’
Vary the $querynum value from 1 to 3 and run yql.php after each change. Note how the YQL end point changes when requesting personal data vs public data.
Also note how easy it is to get “stuff” using YQL. Query number 3 from the script gets a listing of sushi restaurants in San Francisco:
select Title,Address,Rating from local.search where query="sushi" and location="san francisco, ca" | sort(field="Rating.TotalRatings",descending="true");
That’s the power of YQL.
The best part is, you can play with YQL for real in the YQL console.
The OAuth HMAC signture generation step that trips most people up is the generation of the Signature Base String, which requires some esoteric “normalization” operation. You can read about it from the spec directly.
A Signature Base String is a RFC 3986 encoded concatenation of:
- An uppercased HTTP method: GET, POST, etc.
- An ‘&’
- A url without any parameters (scheme://fqdn/path)
- An ‘&’
- All the query parameters (combined for both the API call and those for oauth) encoded according to rfc 3986 AND sorted by key. DO NOT include the oauth_signature parameter in this step, because you are generating the oauth_signature.
If you read that last paragraph carefully, you’ll note that the stuff in a signature base string is double rfc 3986 encoded. This means that a Yahoo OAuth access token, which is of the form ‘A=blah’ becomes ‘A%3Dblah’ and finally ‘A%253Dblah’ in the normalized Signature Base String.
A Signature Key is the concatenation of:
- rfc 3986 encoded consumer_secret
- rfc 3986 encoded token_secret
Then you apply the hmac-sha1 function using the signature_key to hash the signature_base_string and finally base64_encode the whole thing:
$sig = base64_encode(hash_hmac('sha1', $signature_base_string, $signature_key))
Odds and ends to keep in mind
- Note that the sorting and encoding of query parameters is done both when constructing the query parameter string, and when constructing the HMAC signature base string.
- When normalizing the signature base string, the ‘?’ separating the url from the parameters (if you’re using GET) is replaced with a ‘&’.
- If you are using POST instead of GET, make sure that the HTTP method string is POST when normalizing the signture base string.
- Make sure that you are using the token secret that goes with the correct token.
- If you get signature invalid error, check your Signature Base String normalization code. Be careful of the deliberate double encoding.
- When debugging, output what you are actually passing to and receiving from the server. Try capturing the actual data that is leaving your machine. If you are testing a web application in Firefox, the Live HTTP Header plugin is great for this.