Hi all. The issue I'm about to raise involves a number of moving parts, but I believe I have definitively isolated it to being a core Grails/Spring issue, and not something due to the third parties in play. The basic problem is that when a user starts a session with my application, the session successfully communicates with an OAuth provider (tested with LinkedIn and Twitter), but expires when the browser redirects to the provider's website for authentication.
First, my environment: Oracle JDK 1.6.0.29, 64-bit, running on Ubuntu 11.10, with testing done in IntelliJ IDEA 11.0. I have tested what follows in both Grails 1.3.8 and 2.0.1, and they produce identical results, starting from clean projects with only the oauth-scribe plugin added (I did that to make sure none of my other code was at issue).
Second, the code from the controller that implements the OAuth interaction. It is exactly as written by the plugin author, with only a few print statements added by me:
Third, the relevant statements from the authenticate and callback controllers shown above:Code:def authenticate = { Token requestToken = EMPTY_TOKEN if (oauthService.getOauthVersion() == SupportedOauthVersion.ONE) { requestToken = oauthService.requestToken } session[OauthService.REQUEST_TOKEN_SESSION_KEY] = requestToken println "session.id is ${session.id} in authenticate" println "attribute names for session in authenticate:" Enumeration<String> names = session.attributeNames names.each { println it Object attribute = session[it] println "attribute is a Token? ${attribute instanceof Token}" } String url = oauthService.getAuthorizationUrl(requestToken) print "url is ${url}" return redirect(url: url) } def callback = { Verifier verifier = extractVerifier(params) if (!verifier) { return redirect(uri: oauthService.failureUri) } println "session.id is ${session.id} in callback" println "attribute names for session in callback:" Enumeration<String> names = session.attributeNames names.each {println it} Token requestToken = (Token) session[OauthService.REQUEST_TOKEN_SESSION_KEY] print "requstToken null? ${requestToken == null} and verifier null? ${verifier == null}" Token accessToken = oauthService.getAccessToken(requestToken, verifier) session[OauthService.ACCESS_TOKEN_SESSION_KEY] = accessToken session.removeAttribute(OauthService.REQUEST_TOKEN_SESSION_KEY) return redirect(uri: oauthService.successUri) }
obtaining request token from https://api.linkedin.com/uas/oauth/requestToken
setting oauth_callback to http://myhost:8080/trunk/oauth/callback
generating signature...
...
session.id is CF2FCF26E84929E986F59AEFCBE926A8 in authenticate
attribute names for session in authenticate:
oasRequestToken
attribute is a Token? true
url is https://api.linkedin.com/uas/oauth/a...7-c3fb8a002688
...
session.id is 94A56B5549ABFEB2A550A1F2FCE9E95D in callback
attribute names for session in callback:
requstToken null? true and verifier null? false
obtaining access token from https://api.linkedin.com/uas/oauth/accessToken
| Error 2012-03-30 10:12:13,242 [http-bio-8080-exec-7] ERROR errors.GrailsExceptionResolver - NullPointerException occurred when processing request: [GET] /trunk/oauth/callback - parameters:
oauth_token: 21ce3626-6323-4145-a707-c3fb8a002688
oauth_verifier: 86389
Stacktrace follows:
Message: null
Line | Method
->> 75 | getAccessToken in org.scribe.oauth.OAuth10aServiceImpl
| 118 | getAccessToken in uk.co.desirableobjects.oauth.scribe.OauthService
| 25 | doCall . . . . in uk.co.desirableobjects.oauth.scribe.OauthControlle r$_closure1
| 886 | runTask in java.util.concurrent.ThreadPoolExecutor$Worker
| 908 | run . . . . . in ''
^ 662 | run in java.lang.Thread
As you can see, the requestToken stored in the session map disappears because the session id in the authenticate action is no longer in use when the callback action is called. Note that I print all of the session attributes in both actions, and the only attributeBy switching the session map call to servletContext, I get successful authentications, but aside from being bad practice, it suggests very strongly that the user's session is expiring, even with the browser open, as soon as a page that the application itself sent the user to is visited. Since the code works exactly as expected when the session is taken out of play, the only remaining question is what is causing the session to expire. I appreciate any help you all can offer.


Reply With Quote