Archive for October, 2008|Monthly archive page
The Netflix API Part 1 – Making Your Initial Request
Please note that all keys, tokens and signatures in this series are made up and not usable.
I was excited when I found that the Netflix API was released. I also found that a lot of what was out there less than clear. What’s new? So I thought I’d share and explain what I find as I take my steps to understand it. I plan to start with Actionscript and Flex and then to move on to C# with WPF with the goal of taking it to the Microsoft Surface that we have at EffectiveUI.
To start with, go ahead and go to the developers section at Netflix and sign up for a developer account. You don’t need to have a Netflix account to get started, you can create up to three test accounts once you sign up, but you won’t be able to use some features that a full fledged subscriber would be able to. You should be fine to start out with though. FYI I’ll be using the Netflix REST API.
Netflix uses a newish standard for account access called OAuth. It allows a subscriber of a site such as Netflix to allow third parties sites and applications to access to their account assets without having to give their Netflix username and password to that site or app. A common example is if I have a Flickr account and I want make prints with a third party photo printer, the photo printer rerouts me to Flickr, where I log in and tell Flickr that I want to allow the printer to have read only access to my photos. Flickr then passes back what’s called a token that the third party can use to gain access to my Flickr photos so that I can select and print them.
Yes this can get a little convoluted, but before I get started with code, I’m going to send you to Hueniverse where there is an excellent four part overview of how OAuth works (http://www.hueniverse.com/hueniverse/2007/10/beginners-guide.html). It is vital that you do this because I’m going to assume you’ve read it as I continue. It’ll only take about 10 minutes to read over it.
Since I’m sure you didn’t just that guide I’m going to define three terms:
- Consumer – is the application that is requesting access to protected data at the Service Provider on behalf of the User.
- User – is the person using the Consumer to access their data on the Service Provider.
- Service Provider – is the application that the Consumer is requesting access to on behalf of the User.
These are much more clearly defined in the overview at Hueniverse mentioned above. You should really read it.
A Basic Request
A basic OAuth request consists of the following items:
- oauth_consumer_key – the Netflix consumer key you received when signing up as a developer
- oauth_nonce – a unique item that can only be used once
- oauth_signature_method – In the case of Netflix it is HMAC-SHA1
- oauth_timestamp – time in milliseconds since 1/1/70
- oauth_signature – The key and message (described below) encrypted using the oauth_signature_method
Signatures
For OAuth to work all requests by the consumer to the service provider need to be accompanied by a signature. For Netflix this is a HMAC-SHA1 encoded key/message string that is tacked onto the end of the request. HUH? If you check out the pseudocode on the wikipedia link above, you’ll see that you’re gonna spend your entire week trying to figure out how to code that one. Luckily for us there is an AS3 OAuth library from iotashan.com at Google Code that handles all that stuff for us. It’s easiest just to build a swc file out of the code and include it in your project.
Okay back to the HMAC-SHA1 encoded signature. There is a key and a message.
The key is your Netflix consumer secret key, sometimes referred to as a shared secret concatenated with an ampersand ‘&’ with a secret token provided by the service provider. If you don’t have a secret token yet then you just let your key end with the ampersand character. A key without a secret token might look like this:
Kzb6wjrhgR&
The message is the url and query string using the properties and values listed above that makes up your request WITHOUT the oauth_signature. As a URL with query string it might look like this:
http://api.netflix.com/oauth/request_token?oauth_consumer_key=5cgh5q5b27g27dji4wyt3h27&oauth_nonce=420E8742-E341-6F5C-8E60-4973D0A85CAF&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1225298006
You pass the key and message as parameters to the encryption method and get back your signature which might look like this:
cg5jE6EIFXU4th63HD3HFSHY4LL
It will become the value of the oauth_signature value.
In the end a url and query string to make an initial request for a token might look like this:
http://api.netflix.com/oauth/request_token?oauth_consumer_key=5cgh5q5b27g27dji4wyt3h27&oauth_nonce=420E8742-E341-6F5C-8E60-4973D0A85CAF&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1225298006&oauth_signature=cg5jE6EIFXU4th63HD3HFSHY4LL
On the service provider side, they will take the exact same steps on the url and name value pairs to create a signature that should be the same as yours. Plus the nonce value and the timestamp are combined to create a unique value that can never be used again. This guarantees that the request can only be used once and not intercepted in anyway and used again.
Thankfully the AS3 OAuth library will take care of much of this for us.
Making an Initial Netflix Token Request using AS3 OAuth
I’ll be using Flex to create this example I’ll use a class and utilize HTTPService (Flex Framework) to show you how this can be done. This can also be done in straight AS3 with URLLoader.
First create a new project in Flex. I’m calling mine NetFlix Tutorial and under Application type select “Desktop Application (Runs in Adobe AIR).” Select the Next button twice and select the Library path tab. Select “Add SWC” or “Add SWC Folder” and select the location of the AS3 OAuth swc that you built. Select Finish.
In the src folder create a new folder called netflix. In netflix create a folder called tutorial. Now create an actionscript class called tokenRequest.
At the top of your class declaration import the oauth classes:
import org.iotashan.oauth.*;
Create several static constants to hold some unchanging stuff you’ll need:
private static const CONSUMER_KEY:String = "INSERT YOUR CONSUMER KEY HERE"; private static const:CONSUMER_SECRET:String = "INSERT YOUR CONSUMER SECRET HERE" private static NETFLIX_BASE_URL:String = "http://api.netflix.com/"; private static const SIG_METHOD:IOAuthSignatureMethod = new OAuthSignatureMethod_HMAC_SHA1();
Before people start howling, yes, storing your secret key in an easily uncompilable swf is dangerous. I would advise you to store your secret key externally and read it in when the app starts.
Now create a consumer instance variable:
private var _consumer:OAuthConsumer;
The constructor which will instantiate our _consumer variable by passing the consumer key and consumer secret to it:
public function TokenRequest()
{
_consumer = new OAuthConsumer(TokenRequest.CONSUMER_KEY, TokenRequest.CONSUMER_SECRET);
}
Just for testing lets create a function that will return the request url and query string:
public function getRequestURL():String
{
var reqUrl:String = NETFLIX_BASE_URL + "oauth/request_token";
var tokenRequest:OAuthRequest = new OAuthRequest("GET", reqUrl, null, _consumer);
return tokenRequest.buildRequest(TokenRequest.SIG_METHOD);
}
The reqUrl is build by using th base url plus the path to the request_token method. The reqest object is created by passing the method, in our case “GET”, the request url, any additional properties – there are none since this is our initial token request, and the consumer object. The call to build request generates our full request with signature and returns it.
Now back in our Main.mxml let’s add some tags:
- Script
- TextArea – id=”txtDebug”, width=”100%”, height=”100%”
- Button – id=”btnRequestToken”, label=”Request Token”, click=”btnRequestToken_Click(event)”
In the application tag set layout=”vertical”.
In the script tag, import the Token Request tag, create the btnRequestToken_Click and requestToken methods.
import netflix.tutorial.TokenRequest;
private function btnRequestToken_Click(event:MouseEvent):void
{
requestToken();
}
private function requestToken():void
{
var tokenReq:TokenRequest = new TokenRequest();
txtDebug.text += tokenReq.getRequestUrl() + "\n";
}
Your output should look something like this:
http://api.netflix.com/oauth/request_token?oauth_consumer_key=5cgh5q5b27g27dji4wyt3h27&oauth_nonce=8ED39931-8CD6-9ADE-D8BE-49E177B2FD12&oauth_signature=cg5jE6EIFXU4th63HD3HFSHY4LL%3D&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1225305192
Now lets wire up our class to make the actual request to Netflix.
Back in the TokenRequest class, lets start with the Flex-centric HTTPService class.
Import the http service class:
import mx.rpc.http:HTTPService;
Below the _consumer instance variable declaration add:
private var _service:HTTPService;
In the constructor add:
_service = new HTTPService(); _service.addEventListener(ResultEvent.RESULT, onServiceResult); _service.addEventListener(FaultEvent.FAULT, onServiceFault);
Above the class declaration add event metadata so we can relay the events:
[Event(name="result", type="mx.rpc.events.ResultEvent")] [Event(name="fault", type="mx.rpc.events.FaultEvent")]
Add the onServiceResult and onServiceFault methods:
private function onServiceResult(e:ResultEvent):void
{
dispatchEvent(e);
}
private function onServiceFault(e:FaultEvent):void
{
dispatchEvent(e);
}
Add a requestToken method to initiate the request:
public function requestToken():void
{
_service.url = getRequestUrl();
_service.send();
}
Now in the main.mxml file add listeners and methods to the tokenReq object and add the call to tokenReq.requestToken():
private function requestToken():void
{
var tokenReq:TokenRequest = new TokenRequest();
//txtDebug.text += tokenReq.getRequestUrl() + "\n";
tokenReq.addEventListener(ResultEvent.RESULT, onRequestTokenResult);
tokenReq.addEventListener(FaultEvent.FAULT, onRequestTokenFault);
tokenReq.requestToken();
}
private function onRequestTokenResult(e:ResultEvent):void
{
txtDebug.text = e.result.toString();
}
private function onRequestTokenFault(e:FaultEvent):void
{
txtDebug.text = e.fault.message;
}
Run the app and your output should look similar to this:
oauth_token=wapq45jpvnaldkuezcdm4qj7&oauth_token_secret=JbBM7avBarg5&application_name=My+Cool+Netflix+Viewer&login_url=https%3A%2F%2Fapi-user.netflix.com%2Foauth%2Flogin%3Foauth_token%3Hwapq43jpvnaghjueycdm4nk5
This result has all the stuff you need to get going with the Netflix API. What can you do with this? Stay tuned.
Here is all the source:
Main.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical">
<mx:Script>
<![CDATA[
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import netflix.tutorial.TokenRequest;
private function btnRequestToken_Click(event:MouseEvent):void
{
requestToken();
}
private function requestToken():void
{
var tokenReq:TokenRequest = new TokenRequest();
//txtDebug.text += tokenReq.getRequestUrl() + "\n";
tokenReq.addEventListener(ResultEvent.RESULT, onRequestTokenResult);
tokenReq.addEventListener(FaultEvent.FAULT, onRequestTokenFault);
tokenReq.requestToken();
}
private function onRequestTokenResult(e:ResultEvent):void
{
txtDebug.text = e.result.toString();
}
private function onRequestTokenFault(e:FaultEvent):void
{
txtDebug.text = e.fault.message;
}
]]>
</mx:Script>
<mx:TextArea id="txtDebug" width="100%" height="100%" />
<mx:Button id="btnRequestToken" label="Request Token" click="btnRequestToken_Click(event)" />
</mx:WindowedApplication>
netflix.tutorial.TokenRequest
package netflix.tutorial
{
import mx.core.UIComponent;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.rpc.http.HTTPService;
import org.iotashan.oauth.*;
[Event(name="result", type="mx.rpc.events.ResultEvent")]
[Event(name="fault", type="mx.rpc.events.FaultEvent")]
public class TokenRequest extends UIComponent
{
private static const CONSUMER_KEY:String = "INSERT YOUR CONSUMER KEY HERE";
private static const CONSUMER_SECRET:String = "INSERT YOUR CONSUMER SECRET HERE"
private static const NETFLIX_BASE_URL:String = "http://api.netflix.com/";
private static const SIG_METHOD:IOAuthSignatureMethod = new OAuthSignatureMethod_HMAC_SHA1();
private var _consumer:OAuthConsumer;
private var _service:HTTPService
public function TokenRequest()
{
_consumer = new OAuthConsumer(TokenRequest.CONSUMER_KEY, TokenRequest.CONSUMER_SECRET);
_service = new HTTPService();
_service.addEventListener(ResultEvent.RESULT, onServiceResult);
_service.addEventListener(FaultEvent.FAULT, onServiceFault);
}
private function onServiceResult(e:ResultEvent):void
{
dispatchEvent(e);
}
private function onServiceFault(e:FaultEvent):void
{
dispatchEvent(e);
}
public function requestToken():void
{
_service.url = getRequestUrl();
_service.send();
}
public function getRequestUrl():String
{
var reqUrl:String = NETFLIX_BASE_URL + "oauth/request_token";
var tokenRequest:OAuthRequest = new OAuthRequest("GET", reqUrl, null, _consumer);
return tokenRequest.buildRequest(TokenRequest.SIG_METHOD);
}
}
}
Comments (11)


