Connect your Windows Store App to LinkedIn (via OAuth 2.0) using WebAuthenticationBroker and PasswordVault

I have written a sample using OAuth 1.0a to connect to LinkedIn service. However, for many developers (me too), OAuth 1.0a can be tricky and very complicate to get right with all the details of the protocol.

Like OAuth 1.0a, the OAuth 2.0 provides ways for applications to share their private resources with other applications, but it has been designed with focus on client developer simplicity. You don’t need to worry about implementing request signing mechanism as OAuth 2.0 utilizes and requires SSL for that task. If you want to know more about the differences between OAuth 2.0 and OAuth 1.0a, you can go to this link, or this one on StackOverflow.

Last month, LinkedIn starts supporting OAuth 2.0, so I want to write a small sample showing how easy we can create a Windows Store App (aka Metro or Moderm) to connect to LinkedIn now.

In this post, I will use WebAuthenticationBroker which makes everything much easier as well. In the OAuth 1.0a LinkedIn post, I didn’t worry about storing and reusing an access token, but this time, I will show you how we can securely keep the token in PasswordVault.

Because LinkedIn already provide great documentation about how to use their service with OAuth 2.0, I will focus on my code only.

1. We will start by getting an authorize code by going to LinkedIn, and call WebAuthenticationBroker.AuthenticateAsync and get the WebAuthenticationResult back. During this process, a user has to sign into LinkedIn. If everything works as expected, we will get the authorization code from the WebAuthenticationResult and use it in the next step.

private async Task getAuthorizeCode()
{
    var url = "https://www.linkedin.com/uas/oauth2/authorization?response_type=code"
                                    + "&client_id=" + _consumerKey
                                    + "&scope=" + Uri.EscapeDataString(_scope)
                                    + "&state=STATE"
                                    + "&redirect_uri=" + Uri.EscapeDataString(_callbackUrl);
    log(url);
    var startUri = new Uri(url);
    var endUri = new Uri(_callbackUrl);

    WebAuthenticationResult war = await WebAuthenticationBroker.AuthenticateAsync(
                                                WebAuthenticationOptions.None,
                                                startUri,
                                                endUri);
    switch (war.ResponseStatus)
    {
        case WebAuthenticationStatus.Success:
            {
                // grab access_token and oauth_verifier
                var response = war.ResponseData;
                IDictionary<string, string> keyDictionary = new Dictionary<string, string>();
                var qSplit = response.Split('?');
                foreach (var kvp in qSplit[qSplit.Length - 1].Split('&'))
                {
                    var kvpSplit = kvp.Split('=');
                    if (kvpSplit.Length == 2)
                    {
                        keyDictionary.Add(kvpSplit[0], kvpSplit[1]);
                    }
                }

                _authorizationCode = keyDictionary["code"];
                break;
            }
        case WebAuthenticationStatus.UserCancel:
            {
                log("HTTP Error returned by AuthenticateAsync() : " + war.ResponseErrorDetail.ToString());
                break;
            }
        default:
        case WebAuthenticationStatus.ErrorHttp:
            log("Error returned by AuthenticateAsync() : " + war.ResponseStatus.ToString());
            break;
    }
}

Please notice that we also pass scope which will determine what our app can access. Our user will also warns our user in the login page as well. Please go to LinkedIn document for more information.

/// <summary>
/// In this sample, we want to see the full profile and connections
/// http://developer.linkedin.com/documents/authentication#granting
/// </summary>
private string _scope = "r_fullprofile r_network";

image

2. You would notice that with the magic of WebAuthenticationBroker, our user doesn’t need to worry about anything. After we get the authorization code, we will try to get an access token by sending the authorization code with our secret key as well.

private async Task getAccessToken()
{
    var url = "https://www.linkedin.com/uas/oauth2/accessToken?grant_type=authorization_code"
        + "&code=" + _authorizationCode
        + "&redirect_uri=" + Uri.EscapeDataString(_callbackUrl)
        + "&client_id=" + _consumerKey
        + "&client_secret=" + _consumerSecretKey;

    using (var httpClient = new HttpClient())
    {
        httpClient.MaxResponseContentBufferSize = int.MaxValue;
        httpClient.DefaultRequestHeaders.ExpectContinue = false;

        var httpRequestMessage = new HttpRequestMessage
        {
            Method = HttpMethod.Post,
            RequestUri = new Uri(url)
        };

        var response = await httpClient.SendAsync(httpRequestMessage);
        var jsonString = await response.Content.ReadAsStringAsync();

        var json = JsonObject.Parse(jsonString);
        _accessToken = json.GetNamedString("access_token");
        log("Getting New Access Token");
    }
}

3. That’s it. We can now access LinkedIn API. By default, LinkedIn returns XML, we can get JSON instead by adding the header, “x-li-format”, with “json” value.

async void sendHttpRequestButton_Click(object sender, RoutedEventArgs e)
{
    var apiUrl = linkedInApiUrl.Text;
    var url = apiUrl + "?oauth2_access_token=" + _accessToken;

    if (!string.IsNullOrEmpty(apiQuery.Text))
    {
        url += "&" + apiQuery.Text;
    }

    using (var httpClient = new HttpClient())
    {
        httpClient.MaxResponseContentBufferSize = int.MaxValue;
        httpClient.DefaultRequestHeaders.ExpectContinue = false;
        // By default, LinkedIn returns XML, we can get JSON instead by adding the header below
        httpClient.DefaultRequestHeaders.Add("x-li-format", "json");

        var httpRequestMessage = new HttpRequestMessage
        {
            Method = HttpMethod.Get,
            RequestUri = new Uri(url)
        };

        var response = await httpClient.SendAsync(httpRequestMessage);

        if (response.IsSuccessStatusCode)
        {
            var jsonString = await response.Content.ReadAsStringAsync();
            log(jsonString);
        }
        else
        {
            log(response.ToString());
        }
    }
}

In step #2, once we get the access token, we will keep it in PasswordVault. Now we can close our app and re-open it. The access token should be retrieved from PasswordVault, and our user don’t need to sign into LinkedIn again.

/// <summary>
/// A credential locker to securely store our Access Token 
/// http://msdn.microsoft.com/en-us/library/windows/apps/br227089.aspx
/// </summary>
private PasswordVault _vault;
private const string RESOURCE_NAME = "LinkedInAccessToken";
private const string USER = "user";

// we use a property instead of normal field because of how PasswordVault works
private string _accessToken
{
    get
    {
        try
        {
            var creds = _vault.FindAllByResource(RESOURCE_NAME).FirstOrDefault();
            if (creds != null)
            {
                return _vault.Retrieve(RESOURCE_NAME, USER).Password;
            }
            else
            {
                return null;
            }
        }
        catch (Exception)
        {
            // if no access token found, the FindAllByResource method throws System.Exception: Element not found
            return null;
        }
    }
    set
    {
        _vault.Add(new PasswordCredential(RESOURCE_NAME, USER, value));
    }
}

Let’s run the app and see some nice looking screenshots 😉

After our user signs into LinkedIn and we get the access token, we are now ready to send some request to LinkedIn API.image

Here we access Profile API with some field selectors.

image

Here we access Connections API with some field selectors as well as count parameters.

image

I hope this helps someone. You can download the full source code from here.

Enjoy coding!

Advertisements

2 thoughts on “Connect your Windows Store App to LinkedIn (via OAuth 2.0) using WebAuthenticationBroker and PasswordVault

  1. Pingback: Connect your Metro(??) style app to LinkedIn (via OAuth 1.0a) | Cyan By Fuchsia

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s