Apr 3, 2012

LinkedIn API in a Grails Application

Before getting started, I want to mention that the LinkedIn API has a major limitation that may change your design. My client’s website has a public facing form where a user can apply for job openings and upload their resume. We wanted to add an option for them to log in with LinkedIn to speed up the application process. Originally, I was simply going to capture and store the user’s public profile url. Then, I was planning to call the LinkedIn API to get some of their public profile data and display it on a screen in our application in real time.

My plans had to change when I discovered that a LinkedIn user would have to log in each time we wanted to pull the data from their public profile. In other words, every time you hit the LinkedIn API to access the public profile data (yes, the data you can see by going to a person’s public url, or simply Googling for their LinkedIn profile), the LinkedIn user must log in (Ref: http://developer.linkedin.com/thread/1418). My alternative solution was to capture their public profile data in our database. It is useful for our system to store the profile information for searching purposes, even though the data could be outdated. Each time a user logs in through the LinkedIn API, it will re-upload their profile data and refresh our database.

Getting the LinkedIn API working in a Grails application isn’t difficult. First you’ll need to create a LinkedIn Developer account (http://developer.linkedin.com), and for that, you’ll need a LinkedIn account. Once you’re signed into the LinkedIn DeveloperNetwork, follow the instruction to add a new application. This will generate the unique API key and secret key you will need.

LinkedIn uses Oauth for authentication, so the next step is to set this up. The good news is, there’s a Grails plugin for that… but it requires some tweaking (I’ll get to that soon). Install the oauth-plugin in your Grails app… http://grails.org/plugin/oauth

To configure oauth, add this code to the Config.groovy file…

oauth {
linkedin {
requestTokenUrl="https://api.linkedin.com/uas/oauth/requestToken"
accessTokenUrl="https://api.linkedin.com/uas/oauth/accessToken"
authUrl="https://api.linkedin.com/uas/oauth/authorize"
consumer.key="###"
consumer.secret="###"
}
}

(Note: The consumer.key and consumer.secret are unique to each site. Replace the ‘###’ value with the unique values generated by LinkedIn for your application. consumer.key = API key; consumer.secret = Secret Key)

Now for the tweaking. As of this writing the version of the oauth-plugin is 0.12, which contains a bug. The workaround for this bug is described in the plugin documentation. Here’s the code you’ll need to add in the Config.groovy file for the workaround…

//necessary to fix a bug in the Oauth plugin 0.12
httpClient {
timeout {
socket = 5000
connection = 5000
}
}

Next, you’ll want to add a link on your page so your user can log in to LinkedIn…

<g:oauthLink consumer='linkedin' returnTo="[controller:'jobPosting', action: 'linkedin', project:search?.id ]">
Login with LinkedIn
</g:oauthLink>

Here I added an action in my existing JobPostingController rather than using the controller that came with the plugin…

class JobPostingController {
def oauthService
def apiUrl = "http://api.linkedin.com/v1/people/~:(id,first-name,last-name,public-profile-url,headline,summary)"
def linkedin = {
if (session.oauthToken == null) {
redirect(uri:"/")
}
if (params?.apiUrl) apiUrl = params.apiUrl
def response = oauthService.accessResource(
apiUrl, 'linkedin', [key:session.oauthToken.key, secret:session.oauthToken.secret], 'GET')
//LinkedInProfile is a domain class created to store the data in our application
LinkedInProfile linkedInProfile = new LinkedInProfile()
if(response) {
def person = new XmlParser().parseText(response.body)
linkedInProfile.firstName = person.get("first-name")[0]?.text()
linkedInProfile.lastName = person.get("last-name")[0]?.text()
//...continue parsing XML
}
render(view: 'thankYou')
}
}

This code will handle the LinkedIn authentication, and then parse the XML in the response. The apiUrl value can be changed to retrieve specific data, or LinkedIn provides a default (https://developer.linkedin.com/documents/profile-fields).

One other thing I wanted to touch on. When the user chooses to login through LinkedIn, they will be redirected to a LinkedIn authorization page. This means you’ll lose your params data. In my case, I needed to pass the job id back to my page after the LinkedIn authentication was completed. This was easy once I determined how it was working. All I needed to do was override the default oauthUrl tag in the OauthTagLib with a new TagLib in our application. Now I can pass any params back to my page that I need to.

References:
http://java.dzone.com/articles/grails-oauth-and-linkedin-apis (a bit outdated, but a good tutorial using the basic plugin code)

About the Author

Object Partners profile.

One thought on “LinkedIn API in a Grails Application

  1. Bobby Warner says:

    Did you investigate Spring Social LinkedIn at all? And if so, why wasn’t it chosen for your project? I’ve used the Spring Social Facebook and Twitter ones, but haven’t played with the LinkedIn module yet. Just curious. Good post, thanks!

  2. Dimitris Zavaliadis says:

    Hi Amy, nice post!

    According to the LinkedIn APIs Terms of Use (https://developer.linkedin.com/documents/linkedin-apis-terms-use Section 3.4) you are not allowed to store profile data unless you get the user’s explicit consent. How do you deal with that?

    Generally speaking, I think the LinkedIn API is quite restrictive especially when it comes to throttle limits (https://developer.linkedin.com/documents/throttle-limits). I had an idea for an application that would heavily use this API, but after reading the documentation I’m not still sure whether it’s feasilbe with all these limitations 🙁

    Regards,
    Dimitris

  3. Hello, thanks for your comments!

    @Bobby – No, I didn’t look at using Spring Social LinkedIn. I looked for a Grails plugin, but one doesn’t exist for LinkedIn (yet!) (http://grails.org/plugin/spring-social-core). I’ll definitely consider this if a future project requires it.

    @Dimitris – Thanks for bringing that up, it’s a great point. As I mentioned above, job applicants are required to fill out an application form on our website to apply for an open position. The applicant can fill out the form manually, or choose to log into their LinkedIn account to pre-fill the fields on our form and speed up the application process. They must review and make any necessary edits of the data pulled in from LinkedIn before submitting the form. By submitting the form, they are giving us permission to store the data from the form in our database (see section 3.4.3).

    I agree and was very disappointed when I started reading the documentation too. Good luck with your project, I hope you figure out a way to make the API work for you.

    Amy

  4. Sagar says:

    when i implemented your code in my application it showing me an error in controller

    LinkedInProfile linkedInProfile = new LinkedInProfile()

    .. can u help me. how to resolve this issue

  5. Jon DeJong says:

    Sagar,

    I have not run this code, but it would appear from the comment above the line that LinkedInProfile is just a basic Grails Domain class created by Amy, no different than any other Domain class.

    1. Hi Sagar,
      The LinkedInProfile class is a domain class that I created in our application to store the LinkedIn profile data. You have to create your own domain class in your application. You can name it whatever you want, or add the attributes you want to store to a domain class you already have in your application.

      Amy

Leave a Reply to Bobby Warner Cancel reply

Your email address will not be published.

Related Blog Posts
Natively Compiled Java on Google App Engine
Google App Engine is a platform-as-a-service product that is marketed as a way to get your applications into the cloud without necessarily knowing all of the infrastructure bits and pieces to do so. Google App […]
Building Better Data Visualization Experiences: Part 2 of 2
If you don't have a Ph.D. in data science, the raw data might be difficult to comprehend. This is where data visualization comes in.
Unleashing Feature Flags onto Kafka Consumers
Feature flags are a tool to strategically enable or disable functionality at runtime. They are often used to drive different user experiences but can also be useful in real-time data systems. In this post, we’ll […]
A security model for developers
Software security is more important than ever, but developing secure applications is more confusing than ever. TLS, mTLS, RBAC, SAML, OAUTH, OWASP, GDPR, SASL, RSA, JWT, cookie, attack vector, DDoS, firewall, VPN, security groups, exploit, […]