Dec
8
2009

Log into your Grails app using your Twitter credentials

I’ve got a new little hobby grails project that relies on a user’s tweets to power its data collection, so I’ve been exploring using Twitter’s oauth features to do away user accounts altogether. My little project is called “Twitter Until You’re Fitter” and the idea is that I can tweet my weight “@tuyf 93kg” each week, and the app will pick up the value and graph it over time toward my goal weight (85kg). Given my current gym workload, that shouldn’t be too far away :-)

For those of you who haven’t played with oauth, it’s one of those emerging “federated SSO” type protocols. You generate a signed request from your application and sent it off to twitter. Twitter asks the logged on user whether they wish to allow your application access their account. If they agree, twitter will send your application an oauth_verifier which you can use to logon as that user. Generating and processing all those tokens is easily handled by twitter4j

In order to get started, you first have to register your application with twitter, after which you’ll be issued with a “consumer key” and a “consumer secret”. You’ll want to squirrel those away in your /grails-app/conf/Config.groovy to get access to them later:

twitter {
	oauth.consumer_key = 'd8sfsv9s0afs89v0sdesvs0'
	oauth.consumer_secret = 'lfsdDKSFjepfskvespfseruDFESilsfesfiseLFS=='
}

Next up we’ll taked advantage of Grails 1.2′s new dependency management gear, by adding twitter4j to our /grails-app/conf/BuildConfig.groovy

dependencies {
	build 'net.homeip.yusuke:twitter4j:2.0.10'
}

With our dependency in place, and our keys all set aside in config, it’s time to generate a request to send the user off to twitter. I’ve setup a little TwitterService class to abstract some of the details:

class TwitterService {

    def generateRequestToken(twitter, callbackUrl) {
		def consumerKey = ConfigurationHolder.config.twitter.oauth.consumer_key
		def consumerSecret = ConfigurationHolder.config.twitter.oauth.consumer_secret
		twitter.setOAuthConsumer(consumerKey, consumerSecret)
		def requestToken = twitter.getOAuthRequestToken(callbackUrl)
		return requestToken
    }

}

I’m passing in the twitter4j object itself, since I’m planning on binding that object to the user’s session, and using it to access their timeline. Now we just need a controller to generate the “redirect to login” feature of my application. Something like this should do it:

def requestLogin = {
	def twitterClient = new twitter4j.Twitter()
	def returnUrl = g.createLink(controller: 'oauth', action: 'processLogin', absolute:true).toString()
	log.debug "Generating request with return url of [${returnUrl}]"
	def requestToken = twitterService.generateRequestToken(twitterClient, returnUrl)
	session.twitter = twitterClient
	session.requestToken = requestToken
	redirect(url:requestToken.getAuthorizationURL())
}

First we generate a link for our return url after authentication, then I call the TwitterService to generate my request token. The RequestToken class exposes a getAuthorizationURL() which returns the URL you need to send them off to. The user will be shown an authorisation screen like this:

Twitter OAuth login screen

Assuming they click “Allow”, twitter will redirect back to your requested URL with a URL containing a oauth_verifier parameter. You can use that parameter to login to twitter as that user. Here’s the action that processes the return URL from twitter:

def processLogin = {

	log.debug "Processing Login Return from Twitter"
	if (!session.requestToken) {
		redirect(action: 'requestLogin')
	} else {
		def accessToken = session.twitter.getOAuthAccessToken(session.requestToken, params.oauth_verifier)
		log.debug "Attempting validate..."
		def twitterUser = session.twitter.verifyCredentials()
		log.debug "Validate successful for ${twitterUser.screenName}"
		session.user = twitterUser
		redirect(action: 'displayDetails')
	}
}

That accessToken object turns out to be really important. If you squirrel away it’s token and tokenSecret properties, you can login as the user any time you want with the redirect step (eg. a future session, cookie value, etc).

Phew! We’ve come a long way! We’re now actually logged on as the user, now we just need a gsp to display some timeline details. Perhaps something like this:

<html>
	<head>
		<meta http-equiv="Content-type" content="text/html; charset=utf-8">
		<title>Welcome ${session.user.name}</title>
	</head>
	<body>
		<h1>Successful Login for ${session.user.name}</h1>
		<p>Your user handle is <a href="${session.user.getURL()}">@${session.user.screenName}</a></p>
		<p>Your icon is: <img src="${session.user.profileImageURL}"></p>
		<p>Your timeline:</p>
		<ul>
			<g:each in="${session.twitter.homeTimeline}" var="status" status='i'>
				<li class="${ (i % 2) ? 'odd' : 'even'}">
					<img src="${status.user.profileImageURL}"/>
					${status.user.screenName} : ${status.text} </li>
			</g:each>
		</ul>
	</body>
</html>

Which should give us an output like this:

The user's details

And our application is fully linked up! If the user has a look in their Twitter settings, they’ll now see that our app has read-only access to their account. They can revoke that at any time (so we’ll need some error checking to handle that down the track).

The user's twitter connections tab

Once I’ve got a bit more of the app running, I’ll get some source onto a public repo. Till then, I need to get ready for my Spin class..

About the Author: Glen Smith

10 Comments + Add Comment

  • Hi, Glen. Thanks for article.
    I have a problem.
    During registration i write callback url.
    In my application for Development Environment I try to rewrite callback_url but twitter redirect me to authorisation screen

    Anton

  • Anton,

    If you just need straight login, I think there’s an alternative to requestToken.getAuthorizationURL() which is something like requestToken.getAuthenticationURL() or something like that. Maybe that’s the one you’re after?

  • Bingo!! I got I was looking for!!.. Thanks a lot Glen.

  • Hi Glen! Greate Article!
    Im new at grails but im lovin it!
    But because im new, i didnt understand that much how to implement the code. Could you copy the controller? or paste a screenshot? Thanks a lot!

    hope you’ re fitter now! :D

  • Glen:

    In these lines

    def consumerKey = ConfigurationHolder.config.twitter.oauth.consumer_key
    def consumerSecret = ConfigurationHolder.config.twitter.oauth.consumer_secret
    twitter.setOAuthConsumer(consumerKey, consumerSecret)

    I needed to run consumerKey and consumerSecret with .toString(). I was getting errors that setOAuthConsumer() didn’t know how to handle ConfigurationObjects.

    Grails 1.2.0

    Annoying little thing, and cost me a bit of time, then I tried the toString()s on a lark and it worked.

    Hope this helps someone else :)

  • thanks Glenn for this article, it works fine ;)

    Nabil

  • Hi,

    It works just like that, i really appreciate your efforts.

    I have another requirement which is logout.

    When ever i logout from my application after logged in by using twitter credentials, It should be able to logout from the twitter also.

    Is anyone having idea to do the same?

    Thanks
    Venkata

  • Hi Glen,

    Thanks for the article, it helped. I have a question now, how to persist AccessToken? I am able to do what is mentioned above, I would like to tweet on User’s behalf at later stage but I am unable to reincarnate AccessToken using “token” and “token secret”, can you guide?

    Thanks

  • Hi,

    I have integrated Twitter4j login functionality for my project successfully with your article but on deny of logging giving me exception. Below are the steps i did

    Step.1) if i click on twitter button takes me to twitter login pop up
    box with “Allow” and “Deny” buttons

    Step.2) if i enter proper twitter username and password, it is
    redirecting me to the twitter call back url which is to my application
    url.

    Upto this everything is working fine.

    Step.3) After (Step.1), if i click on “Deny” button it is redirecting
    to the twitter popup box with some information(OK, you’ve denied
    localvc access to interact with your account! Feel free to find other
    applications and find out what people are talking about right now at
    Twitter.).

    Then closed the twitter popup box, if i comes back to my app login
    page it is giving me exception “Stack Overflow”.

    If i close the browser, reopen the new browser and then i could able
    to access the my app login page without any problem.

    How can i resolve the ‘Stack overflow exception’ issue after clicking
    “Deny” button?
    Is there any functionality to take care on “Deny” button?

  • Thanks Glen, it’s what I was looking for! :D

Leave a comment

Glen Smith

About Glen

Co-author Grails in Action