Providing a “Sign-in with LinkedIn” functionality using Scala


To integrate a “Sign in with LinkedIn ” functionality in your social project that is being built with Play 2.2.1, follow these steps ( this post summarizes the work done step by step).

1) Create a LinkedIn app (if you do not have one already)

Follow the link https://developer.linkedin.com/documents/quick-start-guide‎ and create an app. Enter all the details including Site URL . The Site URL could be something like http://www..com (but it should be a valid site’s URL)

Once you have saved this app , you would get a
API Key and secret .

Take a note of this
API Key and secret .
We would use them in our code.

2) If you are using Play, then add the
API Key, secret key & contextURL(callback URL) in application.conf file.

linkedin.key=<your_key>
linkedin.secret=<your_secret_key>
contextUrl="localhost:9000/user"

3) Download a LinkedIn login image from linkedin.png, & save it in “public/images” folder of app

4) Add following dependency in build.sbt (or Build.scala for Play 2.1.x or older versions)

“org.scribe”         % “scribe”         % “1.3.5”

5) Add following to routes file in conf folder

GET             /user/linkedin/login              controllers.LinkedInController.linkedinLogin
GET             /user/linkedin/callback        controllers.LinkedInController.linkedinCallback



6) Let us now create LinkedInController.scala to configure API Key , secret key and callbackURL . We would also use this scala file to request access token from LinkedIn .

package controllers

import play.api.mvc.Controller
import org.scribe.builder.ServiceBuilder
import org.scribe.oauth.OAuthService
import org.scribe.builder.api.LinkedInApi
import org.scribe.model.Token
import play.api.mvc.Action
import org.scribe.model.Verifier
import org.scribe.model.OAuthRequest
import org.scribe.model.Verb
import org.scribe.model.Response
import scala.xml.XML
import play.api.Logger
import play.api.Play
import play.api.i18n.Messages

object LinkedInController extends Controller {
 val apiKey: String = Play.current.configuration.getString("linkedin.key").get
 val apiSecret: String = Play.current.configuration.getString("linkedin.secret").get
 var requestToken: Token = null
 val currentUserId = "userId"
 val protectedResourceUrl: String = "http://api.linkedin.com/v1/people/~:(id,first-name,last-name,email-address,headline,picture-url,industry,positions:(id,title,summary,start-date,end-date,is-current,company:(id,name,type,size,industry,ticker)))";

 /**
 * Get OAuthService Request
 */
 def getOAuthService: OAuthService = {
  val service: OAuthService = new ServiceBuilder()
   .provider(classOf[LinkedInApi])
   .apiKey(apiKey)
   .apiSecret(apiSecret)
   .scope("r_fullprofile")
   .scope("r_emailaddress")
   .callback("http://" + getContextUrl + "/linkedin/callback")
   .build();
  service
 }

 /**
 * To get The root context from application.config
 */
 def getContextUrl: String = {
  Play.current.configuration.getString("contextUrl").get
 }

 def linkedinLogin: Action[play.api.mvc.AnyContent] = Action {
 try {
  requestToken = getOAuthService.getRequestToken
  val authUrl: String = getOAuthService.getAuthorizationUrl(requestToken)
  Redirect(authUrl)
 }  catch {
   case ex : Throwable=> {
   Logger.error("Error During Login Through LinkedIn - " + ex)
   Ok(views.html.RedirectMain("", "failure"))
  }
 }
}

 def linkedinCallback: Action[play.api.mvc.AnyContent] = Action { implicit request =>
  try {
  getVerifier(request.queryString) match {
   case None => Ok(views.html.RedirectMain("", "failure"))
   case Some(oauth_verifier) =>
    val verifier: Verifier = new Verifier(oauth_verifier)
    val accessToken: Token = getOAuthService.getAccessToken(requestToken, verifier);
    val oAuthRequest: OAuthRequest = new OAuthRequest(Verb.GET, protectedResourceUrl)
    getOAuthService.signRequest(accessToken, oAuthRequest)
    val response: Response = oAuthRequest.send
    response.getCode match {
    case 200 =>
     val linkedinXML = scala.xml.XML.loadString(response.getBody)
     val userEmailId = (linkedinXML \\ "email-address").text.trim
     val userNetwokId = (linkedinXML \\ "id").text.trim
     val userName = (linkedinXML \\ "first-name").text.trim +" " +(linkedinXML \\ "last-name").text.trim
     val userSession = request.session + ("userId" -> userEmailId)
     Ok(views.html.RedirectMain(userEmailId, "success")).withSession(userSession)
    case _ =>
     Logger.error("Error " + response.getCode + " : During Login Through LinkedIn - " + response.getBody)
     Ok(views.html.RedirectMain("", "failure"))
   }
  }
 } catch {
    case ex: Throwable => {
    Logger.error("Error During Login Through LinkedIn - " + ex)
    Ok(views.html.RedirectMain("", "failure"))
   }
 }
}
 def getVerifier(queryString: Map[String, Seq[String]]): Option[String] = {
  val seq = queryString.get("oauth_verifier").getOrElse(Seq())
  seq.isEmpty match {
   case true => None
   case false => seq.headOption
 }
}
}

7) Now let us create a template RedirectMain.scala.html which would show LinkedIn Login Page on clicking LinkedIn login link on login page .

@(userId:String ,status:String) (implicit flash: play.api.mvc.Flash , session: play.api.mvc.Session , lang: Lang)
@main("Drops of Hope",Option(userId)) {
 <body onload="redirect('@status');"></body>
<script type="text/javascript">
  window.setTimeout(function() { $(".alert").alert('close'); }, 10000);
  function close_win(redirectUrl) {
   window.opener.location=redirectUrl
   window.opener.focus();
   window.close();
  }
 function redirect(status) {
 if (status == "success")
 {
  close_win('http://localhost:9000/');
 }
 else
 {
  close_win('http://localhost:9000/login');
 }
}
 </script>
}

8) Add LinkedIn login link in the login.scala.html.

<script type="text/javascript">
  window.setTimeout(function() { $(".alert").alert('close'); }, 10000);
  function open_win_login(url,w,h)
  {
   wleft = (screen.width - w) / 2;
   wtop = (screen.height - h) / 2;
   window.open(url, name,
    'width=' + w + ', height=' + h + ', ' +
    'left=' + wleft + ', top=' + wtop + ', ' +
    'location=no, menubar=no, ' +
    'status=no, toolbar=no, scrollbars=no, resizable=no');\
   window.resizeTo(w, h);
   window.moveTo(wleft, wtop);
   window.focus();
  }
</script>
<div id = "loginWithSocialMedia">
  <a onclick ="open_win_login('/user/linkedin/login',600,400)" href="javascript:void(0);"><img src="@routes.Assets.at("images/linkedin.png")" alt="LinkedIn" title="LinkedIn" /></a>
</div>
Advertisements
This entry was posted in JavaScript, Scala and tagged , , , , , , , . Bookmark the permalink.

2 Responses to Providing a “Sign-in with LinkedIn” functionality using Scala

  1. Hurrah! Finally I got a website from where I be capable of actually obtain valuable data
    regarding my study and knowledge.

  2. samy says:

    How can we do without Play ? (just aka http)

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