• WORK
  • PEOPLE
  • ABOUT
  • BLOG
  • Blog
  • News
  • Events
  • Presentations
  • LinkedIn API in a Grails Application

    By Amy Andrychowicz on Apr. 3 2012

    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)

    4 Responses to LinkedIn API in a Grails Application

    1. Bobby Warner says:
      April 3, 2012 at 10:54 am

      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!

      Reply
    2. Dimitris Zavaliadis says:
      April 3, 2012 at 3:18 pm

      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

      Reply
    3. Amy Andrychowicz says:
      April 6, 2012 at 3:12 pm

      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

      Reply
    4. Pingback: An Army of Solipsists » Blog Archive » This Week in Grails (2012-14)

    Leave a Reply Cancel reply

    Your email address will not be published. Required fields are marked *

    You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

  • Case Studies
  • Java
  • Grails
  • Mobile
  • Approach
  • People
  • Blog
  • Community
  • About
  • Careers
  • Leadership Team
  • Contact
  • Corporate Headquarters
    Object Partners, Inc.
    Butler Square, Suite 302A
    100 North Sixth Street
    Minneapolis, MN 55403
    (612) 746-1580