• Curious Minds Media Labs
  • Connect to LinkedIn API from AS3 and Adobe AIR

    3-9-11

    As a Flex Developer in this day and age, you’ll undoubtedly be asked to connect an application to some social media API sooner or later. If you’ve undergone this process already, you may know that this can bring a host of difficulties and hassles. I have found that each service presents a unique set of challenges that you’ll need to overcome in order to get your application all wired up and communicating with the services’ publicly exposed methods. That being said, I’ve never had more difficulty consuming a web service from an AIR application than I recently encountered while connecting a client application to LinkedIn’s API.

    Many of the differences you’ll encounter in connecting to popular social media sites has to do with the tooling available to Flex Developers targeting the AIR runtime. Many services (Facebook, Twitter, LinkedIn to name a few) utilize OAuth 2.0 protocol for interacting with users’ data in a secure way. OAuth, in case you’re not familiar (and you will be very shortly), is “an open protocol to allow secure API authorization in a simple and standard method from desktop and web applications”. You can read the specification at http://tools.ietf.org/html/rfc5849 if you want to find out more.

    OAuth basically requires that you do the following steps in order to allow the user the opportunity to grant access to your application to manage data on his or her behalf. This is an overview of the process. We will look at the specifics of how to connect to LinkedIn in a few moments, using the following basic information as a primer (or a review depending on your level of familiarity with this process).

    Step 1: Register your application with the service you wish to connect to and receive a Consumer Key and Consumer Secret.

    Step 2: Your application makes a request to the service to authenticate a given user. Successful completion of this step will yield a Request Token.

    Step 3: The application redirects the user to the service’s sign-in and authorize page, where the user must agree to allow the app to make service calls on his behalf.

    Step 4: The application, upon receiving the consent of the user, exchanges the Request Token for an Access Token. This token consists of two Strings, the token and the token secret, and will be used to “sign” (a secretive process we’ll discuss in a moment) the API requests.

    Step 5: Call the resource you wish to consume (status update, personal info, etc..), using your Access Token (and a few other variables) to sign the request.

    There are a numerous code libraries available for connecting to the various social media. Probably the easiest to use of these is the FacebookDesktop Object, part of the com.facebook.graph package. Right out of the box, this tool makes it quite easy to connect to Facebook’s “Graph” API, abstracting most of the ugly details of the OAuth process we’ve just enumerated. This nice little kit practically does all of the work for you, and is very simple to set up and use in your desktop app. If you need to communicate with Facebook, I highly recommend this tool.

    For the purposes of this post however, I’d like to focus on LinkedIn’s API. Connecting to this service represents the other end of the difficulty spectrum in my opinion, and required quite a lot of tinkering to implement successfully. I have spent many hours refining this code. It is my hope that this tutorial will fill the enormous informational void on this topic, and help out some others where I had quite a lot of difficulty. Please grab the src folder for this sample application here. There are some functions in the source that are not included in the text of this tutorial, and I think it will serve you best to actually run this application while reading along. The tools I used to connect to LinkedIn were from the same code library I used to connect to Twitter, the OAuth lib called “oauth-as3” from iotashan http://www.iotashan.com. You can grab the latest source for this project from the following svn location: http://oauth-as3.googlecode.com/svn/trunk/. There is also a dependency introduced when we include the iotashan packages. Go ahead and grab the as3crypto swc from the following URL http://code.google.com/p/as3crypto/downloads/detail?name=as3crypto.swc and include that in the build path for your project. Alternatively, you can also grab the source from google code if you prefer to see what exactly is going on under the hood, as I have in the sample application.

    So, let’s get into the code now. Admittedly, this isn’t the most impressive application I’ve ever written (from a UI standpoint), but the control flow is what we’re concerned with here, and the functional elements are pretty straightforward and informative. In order to run this application, you’ll need to register an application with LinkedIn to obtain your own Consumer Key and Consumer Secrets. I’ve indicated on lines 45 and 46 of my source where your Strings should be inserted to make your application unique from LinkedIn’s perspective. The entry point for this whole handshake process is found on line 60 in the ‘post()’ function below. This is called after you enter some comment text into the field, and hit the Submit Button on the UI.


    private function post():void{
    _comment = commentArea.text;
    var shOb:SharedObject = SharedObject.getLocal(_sharedObName);
    var tmpTok:Object = shOb.data["accessToken"];
    _oauthConsumer = new OAuthConsumer(_consumerKey, _consumerSecret);
    _signatureMethod = new OAuthSignatureMethod_HMAC_SHA1();
    if(tmpTok){
    var token:OAuthToken = new OAuthToken(tmpTok.key, tmpTok.secret);
    doStatusUpdate(_comment, token);
    } else {
    hiOauth_CareToDance();
    }
    }

    In this function we set up some basic variables (our OAuthConsumer and OAuthSignatureMethod_HMAC_SHA1 Objects) which we’ll need for making our requests, as well as check for the possibility that we’ve already got a valid Access Token stored in a Shared Object called ‘linkedInData.acessToken’. Let’s assume that tmpTok comes back null (line 68), and we need to go through the entire process of getting both the Request and Access Tokens from LinkedIn. We proceed to the function called ‘hiOauth_CareToDance()’ which is the point at which we’ll be requesting the Request Token.


    private function hiOauth_CareToDance():void{
    _requestParams = generateParams(_consumerKey);
    _oauthReq = new OAuthRequest(_reqMeth, _requestTokenURL, _requestParams, _oauthConsumer, _token);
    var postData:Object = _oauthReq.buildRequest(_signatureMethod, OAuthRequest.RESULT_TYPE_POST);
    var urlReq:URLRequest = new URLRequest(_requestTokenURL);
    urlReq.method = _reqMeth;
    urlReq.data = postData;
    var loader:URLLoader = new URLLoader(urlReq);
    loader.addEventListener(Event.COMPLETE, onComplete);
    loader.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
    loader.load(urlReq);

    function onComplete(evt:Event):void{
    loader.removeEventListener(Event.COMPLETE, onComplete); loader.removeEventListener(IOErrorEvent.IO_ERROR, onIOError);
    _token = generateToken(evt.currentTarget.data);
    _requestParams.oauth_token = _token; authenticate(_requestParams, _token);
    }

     

    function onIOError(evt:IOErrorEvent):void{
    trace('FAIL!');
    loader.removeEventListener(Event.COMPLETE, onComplete); loader.removeEventListener(IOErrorEvent.IO_ERROR, onIOError);
    }
    }

    You’ve seen URL Requests similar to this already, but the important thing to note here is that we want to make use of our OAuthRequest Object to create the data we’ll add to the URLRequest’s ‘data’ property to send along in our POST Request. I’ve passed a few variables into the constructor of this Object to set it up, and called the instance’s buildRequest method to get back an Object containing everything LinkedIn wants to see in order to issue us a Request Token. On Event.COMPLETE we move on to the function called ‘authenticate’ after adding our token (_token) to _requestParams.oauth_token. I’ve created a convenient little utility to parse the returned data from this request and turn it into an OAuthToken Object. This function is called ‘generateToken’. In the ‘authenticate’ method we’re navigating to LinkedIn’s authentication URL, which allows the user to grant our application control to access and manipulate personal data on the user’s behalf. We also pop up a little window which gives the user a text input into which he or she will enter the 5 digit PIN number that LinkedIn’s authenticate window will supply us with. This function is called ‘popAuthWindow’.


    private function authenticate(params:Object, token:OAuthToken):void{
    var getReq:URLRequest = new URLRequest(_authenticateURL + '?oauth_token=' + token.key); navigateToURL(getReq, '_blank');
    popAuthWindow();
    }

    PopAuthWindow is really quite basic as well, but the one thing to notice here is that I’ve set a variable called ‘submitCallbackFunc’ to a function called ‘authenticateCallbackFunc’ which will get called when the user clicks the ‘submit’ button in the popup window. I find this to be a little easier and more straightforward than creating a custom event for communicating between the popup and the main application, but you may have your own opinion about the cleanest way to accomplish this. This function will accept the pin number. Let’s look at this function now.


    private function authenticateCallbackFunc(pin:String):void{
    _requestParams.oauth_verifier = pin;
    var authReq:OAuthRequest = new OAuthRequest(_reqMeth, _accessTokenURL, _requestParams, _oauthConsumer, _token);
    var postData:Object = authReq.buildRequest(_signatureMethod, OAuthRequest.RESULT_TYPE_POST);
    var urlReq:URLRequest = new URLRequest(_accessTokenURL);
    urlReq.method = _reqMeth;
    urlReq.data = postData;
    var loader:URLLoader = new URLLoader(urlReq);
    loader.addEventListener(Event.COMPLETE, onComplete);
    loader.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
    loader.load(urlReq);

    function onComplete(evt:Event):void{
    loader.removeEventListener(Event.COMPLETE, onComplete); loader.removeEventListener(IOErrorEvent.IO_ERROR, onIOError);
    _token = generateToken(evt.currentTarget.data);
    _requestParams.oauth_token = _token.key;
    _requestParams.oauth_token_secret = _token.secret;
    var so:SharedObject = SharedObject.getLocal(_sharedObName);
    so.data["accessToken"] = _token;
    so.flush();
    doStatusUpdate(_comment, _token);
    }

     

    function onIOError(evt:IOErrorEvent):void{
    trace('FAIL!');
    loader.removeEventListener(Event.COMPLETE, onComplete); loader.removeEventListener(IOErrorEvent.IO_ERROR, onIOError);
    }
    }

    This looks an awful lot like the ‘hiOauth_CareToDance’ function we’ve already discussed, but were the ‘_token’ variable was null before (we hadn’t yet gotten our Request Token) we are now going to have the data returned from our call to the Request Token resource. In this step, we are ostensibly trading the Request Token for an Access Token. We make use of our handy OAuthRequest Object once again to put together the variables and the signature needed to gain access to LinedIn’s Access Token resource, and populate the URLRequest’s data property with our return from the buildRequest method. On our Complete Event, we will again be given another bit of data from which we can parse our ‘oauth_token’ and ‘oauth_token_secret’. At this point we have been fully authenticated, and have everything we need to call the actual API resources we might wish to utilize in our application. So, knowing this we can finally call the status update resource, and submit our comments we had typed into our comments text input. We do this in the ‘doStatusUpdate’ method.


    private function doStatusUpdate(comment:String, tok:OAuthToken):void{
    if(!_requestParams){
    _requestParams = generateParams(_consumerKey);
    delete _requestParams.oauth_token_secret;
    delete _requestParams.oauth_verifier;
    var oauthGen:OAuthRequest = new OAuthRequest(_reqMeth, _statusUpdateURL, _requestParams, _oauthConsumer, tok);
    var header:URLRequestHeader = oauthGen.buildRequest(_signatureMethod, OAuthRequest.RESULT_TYPE_HEADER);
    var urlReq:URLRequest = new URLRequest(_statusUpdateURL);
    urlReq.method = _reqMeth;
    urlReq.requestHeaders.push(header);
    urlReq.contentType = 'text/xml';
    urlReq.data = generateLinkedInStatusXML(comment);
    var loader:URLLoader = new URLLoader(urlReq);
    loader.addEventListener(Event.COMPLETE, onComplete);
    loader.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
    loader.load(urlReq);

    function onComplete(evt:Event):void{
    Alert.show(“Your status message was successfully sent.”, “Linked In Status Updated!” );
    }

    function onIOError(evt:IOErrorEvent):void{
    // if we hit this.. our token must be expired..
    hiOauth_CareToDance();
    }
    }

    We see some familiar elements in this, such as the OAuthRequest and the URLRequest variables, but you’ll also notice that we have introduced a URLRequestHeader in this function which we did not have previously. The reason for this is that we are now transmitting two necessary pieces of data over the wire, not just one. We have to supply our Access Token so that we can demonstrate to LinkedIn’s servers that we are legit, but we also want to send some data to update our status at the same time. This was a little tricky for me to figure out, so I’m hoping that this demo makes even one developer’s life a little easier (ten would be even better). In order to do this, we will again build the OAuth signature with the OAuthRequest, but since we are going to want to use the data property of our URLRequest for the status update XML, we need to change the mode of our OAuthRequest to OAuthRequest.RESULT_TYPE_HEADER. You’ll notice this on line 89 of the sample application. When we change the request type to header, we get back a URLRequestHeader instead of a generic Object. This we can then push into our requestHeaders array. LinkedIn is able to deal with this accordingly, and will verify the validity of this request based on the header, and not the data of our request as we had done previously. As for the data in this step (the actual status comment), I have created a very basic ‘generateLinkedInStatusXML’ function which creates the most basic status update markup from our comment input text. You can do more with this data if you so desire. Please refer to the LinkedIn API reference pages for more information on this.

    In addition, make sure you set the content type of your URLRequest to ‘text/xml’ or you will not have success in making this call (yet another trap I fell into during this arduous process). There are also 2 lines of code deleting certain items from the _requestParams data before we make this request. These 2 variables were necessary in previous steps of the OAuth authentication process, but which we no longer need now that we have the Access Token. Don’t pay too much attention to these. I believe you can leave those intact, and it will have zero effect on the success of this call.

    OK, with a little bit of luck this request will come back successfully and our application will alert us to the fact that our status has been updated. Hop on over to your LinkedIn profile and verify that the message did make it onto your wall (or whatever they call it in LinkedIn parlance).

    Congratulations! You have just successfully navigated the tedious OAuth handshake, and are now able to build out a more useful and full featured application based on this new knowledge.

    8 Responses to “Connect to LinkedIn API from AS3 and Adobe AIR”

    1. Bill says:

      Thanks for posting – very informative about connecting to Linkedin… I tried to download your src but the zip file is missing – Would you be able to repost?

    2. Dave says:

      Great Work. Thanks a ton!

    3. Peter Chen says:

      Could this be done in Flex AS3 (in a browser) instead of AIR?

      Thanks

    4. Jeff says:

      Thanks so much for posting this; I’m finding it extraordinarily helpful in figuring out how to connect to LinkedIn, and oAuth in general. One issue I have run into; when executing the post() call, I am getting the following error:

      Warning: Failed to load policy file from https://api.linkedin.com/crossdomain.xml

      *** Security Sandbox Violation ***
      Connection to https://api.linkedin.com/uas/oauth/requestToken halted – not permitted from [My swf url]

      Error #2044: Unhandled securityError:. text=Error #2048: Security sandbox violation: [My swf url] cannot load data from https://api.linkedin.com/uas/oauth/requestToken.

      I am running my debug swf from WAMP on my local machine; would this have something to do with it?

    5. Jeff says:

      Actually, never mind… for some reason I completely spaced on the probability that LI doesn’t have an open crossdomain file, and missed the “AIR” in this post’s title. Thanks again!

    6. Rajesh Sharma says:

      Hey,
      It’s really great example. Thanks for posting.

    7. Rajesh Sharma says:

      Hi,
      Thanks for the post.
      Is there any facebook sample.
      Thanks

    Leave a Reply