Intro
Quite a few projects I worked on recently required some form of
LinkedIn integration. There were different requirements, from posting status updates to importing users profiles to an
extended MonoX profile. As any developer would do, I headed over to
LinkedIn's Libraries and Tools page. Since we write our applications in C#, I had several libraries to choose from. After extensive research I decided to go with
LinkedIn Developer Toolkit.
LinkedIn's API uses
OAuth protocol for authentication. This allows the application to act (either permanently or temporarily) on behalf of the user without the need for the user to give out his username and password. Information can be retrieved using REST HTTP requests; returned data is in the XML format.
Why choose LinkedIn Developer Toolkit?
Most of the available libraries only allow you to perform basic authentication and to retrieve access tokens, but do not have the inbuilt functionality to post status updates or fetch user data. So instead of having to reinvent the wheel I decided immediately to select the LinkedIn Developer Toolkit. Currently the toolkit does
not support
Company Status Updates.
Please note that the toolkit uses
DotNetOpenAuth project for authentication.
Registering new LinkedIn application
From the APIs menu select API Overview menu option.
Click on the Register button.
On the List of Applications screen click on Add new application button.
On the Add New Application screen enter details about your application. Once you are done click on the Add Application button and copy the API key and the Secret Key. Make sure not to give these keys to anyone.
Implementing IConsumerTokenManager
We will need to make our own implementation of
IConsumerTokenManager interface. For the demonstration purposes I am storing the access tokens in the session. If you are developing a feature which requires temporary access to users' profile, storing the access token in the session is the way to go. However, if you are planning to develop a feature which requires permanent access you will need to store the access token in the database.
You should of course
notify your users that you are storing their access tokens and enable them to remove these access tokens from your system at any time.
The TokenManager class:
public
class
TokenManager : IConsumerTokenManager
{
#region Constructor
public
TokenManager()
:
base
()
{
}
public
TokenManager(
string
appKey,
string
appSecret)
{
ConsumerKey = appKey;
ConsumerSecret = appSecret;
}
#endregion
#region Properties
private
string
_consumerKey = ApplicationSettings.LinkedInKey;
public
string
ConsumerKey
{
get
{
return
_consumerKey; }
set
{ _consumerKey = value; }
}
private
string
_consumerSecret = ApplicationSettings.LinkedInSecretKey;
public
string
ConsumerSecret
{
get
{
return
_consumerSecret; }
set
{ _consumerSecret = value; }
}
#endregion
#region Methods
public
void
ExpireRequestTokenAndStoreNewAccessToken(
string
consumerKey,
string
requestToken,
string
accessToken,
string
accessTokenSecret)
{
HttpContext.Current.Session[
"AccessToken"
] = accessToken;
HttpContext.Current.Session[
"AccessTokenSecret"
] = accessTokenSecret;
}
public
string
GetTokenSecret(
string
token)
{
return
HttpContext.Current.Session[
"AccessTokenSecret"
] !=
null
? HttpContext.Current.Session[
"AccessTokenSecret"
].ToString() :
string
.Empty;
}
public
TokenType GetTokenType(
string
token)
{
throw
new
NotImplementedException();
}
public
void
StoreNewRequestToken(DotNetOpenAuth.OAuth.Messages.UnauthorizedTokenRequest request, DotNetOpenAuth.OAuth.Messages.ITokenSecretContainingMessage response)
{
HttpContext.Current.Session[
"AccessToken"
] = response.Token;
HttpContext.Current.Session[
"AccessTokenSecret"
] = response.TokenSecret;
}
#endregion
}
Retrieving Access Token
Before we can do anything, we need to ask the user for consent to access their account. To do that we need to build an authentication URL and redirect the user to the LinkedIn login page. To handle LinkedIn authentication and callbacks we use a separate page. We also need to have in mind that we need to differentiate the LinkedIn callbacks from authorization requests, so we need to append a query parameter to the LinkedIn callback URL. We will use that query parameter to differentiate betwen callback and authorization request.
Code required to build the authorization URL and to complete the authorization:
#region Properties
private
TokenManager _tokenManager =
null
;
public
TokenManager TokenManager
{
get
{
if
(_tokenManager ==
null
)
{
_tokenManager =
new
TokenManager();
}
return
_tokenManager;
}
}
public
WebOAuthAuthorization Authorization {
get
;
set
; }
public
bool
IsLinkedInCallback
{
get
{
bool
isCallback =
false
;
if
(Request.QueryString[
"isCallback"
] !=
null
)
bool
.TryParse(Request.QueryString[
"isCallback"
],
out
isCallback);
return
isCallback;
}
}
#endregion
#region Page events
protected
void
Page_Load(
object
sender, EventArgs e)
{
try
{
if
(!
this
.IsLinkedInCallback)
{
// reset access tokens
HttpContext.Current.Session[
"AccessToken"
] =
string
.Empty;
HttpContext.Current.Session[
"AccessTokenSecret"
] =
string
.Empty;
}
string
accessToken = IsLinkedInCallback && HttpContext.Current.Session[
"AccessToken"
] !=
null
? HttpContext.Current.Session[
"AccessToken"
].ToString() :
string
.Empty;
this
.Authorization =
new
WebOAuthAuthorization(
this
.TokenManager, accessToken);
if
(!IsLinkedInCallback)
{
// append the query parameter
this
.Authorization.BeginAuthorize(
new
Uri(Request.Url.AbsoluteUri.Append(
"isCallback"
,
true
)));
//this.Authorization.BeginAuthorize(new Uri(Request.Url.AbsoluteUri + "?isCallback=true"));
}
else
{
// complete the authorization and fetch the access tokens
this
.Authorization.CompleteAuthorize();
// redirect back to source page
Response.Redirect(
"/Default.aspx"
);
}
}
catch
(Exception ex)
{
log.Error(ex);
}
}
#endregion
Posting a Status Update
Once we have access tokens we can begin doing something with the user account. Things are pretty much straightforward from here, you only need to pass on a few parameters to the LinkedIn Developer Toolkit and it will take care of the rest.
The code needed to post a status update:
if
(HttpContext.Current.Session[
"AccessToken"
] !=
null
)
{
var authorization =
new
WebOAuthAuthorization(
new
TokenManager(), HttpContext.Current.Session[
"AccessToken"
].ToString());
LinkedInService service =
new
LinkedInService(authorization);
service.CreateShare(
"My test comment"
, LinkedIn.ServiceEntities.VisibilityCode.ConnectionsOnly);
}
Fetching User Profile
Let's see how you can fetch some of your own profile fields and your connections.
Sample code:
if
(HttpContext.Current.Session[
"AccessToken"
] !=
null
)
{
var authorization =
new
WebOAuthAuthorization(
new
TokenManager(), HttpContext.Current.Session[
"AccessToken"
].ToString());
LinkedInService service =
new
LinkedInService(authorization);
// fetch standard profile
List<ProfileField> fields =
new
List<ProfileField>();
fields.Add(LinkedIn.ServiceEntities.ProfileField.FirstName);
fields.Add(LinkedIn.ServiceEntities.ProfileField.LastName);
fields.Add(LinkedIn.ServiceEntities.ProfileField.Connections);
fields.Add(LinkedIn.ServiceEntities.ProfileField.Headline);
var profile = service.GetCurrentUser(ProfileType.Standard, fields);
if
(profile !=
null
)
{
string
fullName = profile.Name;
if
(profile.Connections.Items !=
null
&& profile.Connections.Items.Count > 0)
{
foreach
(var item
in
profile.Connections.Items)
{
}
}
}
}
Conclusion
LinkedIn Developer Toolkit will save you a lot of time, as it allows easy and quick integration of LinkedIn functionality into your site. I highly recommend looking into an additional post on how to
extend a MonoX profile if you're looking to import a lot of additional fields not available in the standard
MonoX profile.