Missing state parameter exception when using: play 2.3, pac4j-oauth 1.7.0

1,283 views
Skip to first unread message

Kristian Kime

unread,
May 24, 2015, 3:06:49 PM5/24/15
to pac4j...@googlegroups.com
First thanks for creating pac4j and the supporting libraries. They seem like very useful libraries and I'm looking forward to using them.

I must be doing something simple wrong because I seem to have most of the functionality working properly but I can't get it 100%. Basically I started with a new play app ("authenticator new") and followed the steps on the play site (https://github.com/pac4j/play-pac4j). Ending up with something very similar to your play demo app (https://github.com/pac4j/play-pac4j-scala-demo).

The "protectedIndex" url seems to work fine (https://github.com/kristiankime/play-pac4j-heroku/blob/master/app/controllers/Application.scala line 19). If a user is not logged in they are asked to authenticate, otherwise they arrive at the page. But if the user attempts to use the "authenticate with Google" link *when they are logged out* they get the following error:

 @6m948og1e - Internal server error, for (GET) [/callback?client_name=Google2Client&state=ktvPGBdy8d&code=4/nIBxikPTP9zQlZ82iSCPDH61M0E5hydVQNriuEMnOF8.As0y2HAtpIgfJvIeHux6iLYFlJkOmwI] ->

play.api.Application$$anon$1: Execution exception[[OAuthCredentialsException: Missing state parameter : session expired or possible threat of cross-site request forgery]]
at play.api.Application$class.handleError(Application.scala:296) ~[play_2.11-2.3.9.jar:2.3.9]
at play.api.DefaultApplication.handleError(Application.scala:402) [play_2.11-2.3.9.jar:2.3.9]
at play.core.server.netty.PlayDefaultUpstreamHandler$$anonfun$3$$anonfun$applyOrElse$4.apply(PlayDefaultUpstreamHandler.scala:320) [play_2.11-2.3.9.jar:2.3.9]
at play.core.server.netty.PlayDefaultUpstreamHandler$$anonfun$3$$anonfun$applyOrElse$4.apply(PlayDefaultUpstreamHandler.scala:320) [play_2.11-2.3.9.jar:2.3.9]
at scala.Option.map(Option.scala:146) [scala-library-2.11.6.jar:na]
Caused by: org.pac4j.oauth.client.exception.OAuthCredentialsException: Missing state parameter : session expired or possible threat of cross-site request forgery
at org.pac4j.oauth.client.BaseOAuth20Client.getOAuthCredentials(BaseOAuth20Client.java:86) ~[pac4j-oauth-1.7.0.jar:na]
at org.pac4j.oauth.client.BaseOAuthClient.retrieveCredentials(BaseOAuthClient.java:128) ~[pac4j-oauth-1.7.0.jar:na]
at org.pac4j.oauth.client.BaseOAuthClient.retrieveCredentials(BaseOAuthClient.java:45) ~[pac4j-oauth-1.7.0.jar:na]
at org.pac4j.core.client.BaseClient.getCredentials(BaseClient.java:220) ~[pac4j-core-1.7.0.jar:na]
at org.pac4j.play.java.RequiresAuthenticationAction$6.apply(RequiresAuthenticationAction.java:202) ~[play-pac4j_java-1.4.0.jar:na]

Steps to reproduce: 
1) start system
2) go to localhost:9000
3) click "logout" link
4) click "Authenticate with Google" link

The complete application is a public github repo (https://github.com/kristiankime/play-pac4j-heroku). It is basically ready to run but you need to set the environment variables GOOGLE_KEY & GOOGLE_SECRET to working google Client ID & Client Secret that allow the javascript origin "https://www.localhost.com" and "http://localhost:9000/callback?client_name=Google2Client" as a Redirect URI. I have one of these setup that I can share if needed but I was unsure if there were security risks in doing so. 

Jérôme LELEU

unread,
May 25, 2015, 4:24:47 AM5/25/15
to Kristian Kime, pac4j...@googlegroups.com
Hi,

The state parameter is generated before redirecting to Google, saved to the session, retrieved from Google after authentication and compared to the one saved into session.

I see a state parameter on your /callback url, so it works on Google side. So I guess the one saved into session is not the same as the one received from Google, the log message is a bit misleading because the state parameter might be missing or different (https://github.com/pac4j/pac4j/blob/1.7.x/pac4j-oauth/src/main/java/org/pac4j/oauth/client/BaseOAuth20Client.java#L83).

The default session timeout is 1 minute: https://github.com/pac4j/play-pac4j/blob/master/play-pac4j_java/src/main/java/org/pac4j/play/Config.java#L33, I would increase this value and test again.

Thanks.
Best regards,
Jérôme


--
You received this message because you are subscribed to the Google Groups "pac4j-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to pac4j-users...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Kristian Kime

unread,
May 26, 2015, 8:52:55 AM5/26/15
to pac4j...@googlegroups.com, kristi...@gmail.com
Hi Jérôme,

Thanks for getting back to me so quickly. I've tried setting the time out to 10 min (https://github.com/kristiankime/play-pac4j-heroku/blob/master/app/controllers/Global.scala#L46) which I can't imagine isn't long enough and I'm still getting the same results...

It does indeed look like the state ID is being sent back but that the session is not stored in the Play cache. The thing that's confusing is that the protectedURL logs in correctly and keeps the session cached. Is there something different about the way the protected url works? ie could I have accidentally configured direct authentication and protected links differently?

Thanks in advance,
-Kristian

Jérôme LELEU

unread,
May 26, 2015, 11:02:08 AM5/26/15
to Kristian Kime, pac4j...@googlegroups.com
Hi,

1 minute is generally enough, 10 minutes is for sure.

Indeed, if the protected url is properly restored (and is not the default url), I tend to think that the storage in cache works.

You keep the same session identifier in the Play cookie, don't you?

I'll make a test with your demo this week.

Thanks.
Best regards,
Jérôme

Kristian Kime

unread,
May 26, 2015, 10:14:06 PM5/26/15
to pac4j...@googlegroups.com, kristi...@gmail.com
Hi thanks again for looking into this,

> Indeed, if the protected url is properly restored (and is not the default url), I tend to think that the storage in cache works.
Sounds correct, this route (https://github.com/kristiankime/play-pac4j-heroku/blob/master/conf/routes#L7) seems to be restored properly

> You keep the same session identifier in the Play cookie, don't you?
I haven't done anything with Play cookie handling. Did I miss something in your tutorial?

> I'll make a test with your demo this week.
Again thanks :)

As I mentioned above you will need to set environment variables for GOOGLE_KEY & GOOGLE_SECRET (you can see them being used here https://github.com/kristiankime/play-pac4j-heroku/blob/master/conf/pac4j.conf). If not you'll get errors here (https://github.com/kristiankime/play-pac4j-heroku/blob/master/app/controllers/Global.scala#L41-42). After that everything should run. Please let me know if you encounter any issues.

Jérôme LELEU

unread,
May 28, 2015, 2:24:12 AM5/28/15
to Kristian Kime, pac4j...@googlegroups.com
Hi,

I just cloned  your application, used my own Google key and secret, changed the port to 8080 (as my Google credentials are defined for this one) and made a test: it works for me:

The only issue I had was that I needed to enable the "Google + API" in the Google developer console for my credentials.

Can you turn on your logs on org.pac4j?

Thanks.
Best regards,
Jérôme

Kristian Kime

unread,
May 28, 2015, 9:41:11 AM5/28/15
to pac4j...@googlegroups.com, kristi...@gmail.com

> I just cloned  your application, used my own Google key and secret, changed the port to 8080 (as my Google credentials are defined for this one) and made a test: it works for me:
Bugger looks like I've created a real Heisenbug...

> The only issue I had was that I needed to enable the "Google + API" in the Google developer console for my credentials.
Yeah they just added that pretty recently. Should have mentioned that.

> Can you turn on your logs on org.pac4j?


Here is a complete run of the logs for the following:

(1) "run" system
(2) hit localhost:9000
(3) use "Authenticate with Google" link
(4) enter name/pass on google's site
(5) auto redirects back to site and see the error

I have inserted comments with "---------" on either side to indicate when my actions took place in relation to the logging


[play-pac4j-heroku] $ run

--- (Running the application, auto-reloading is enabled) ---

[info] play - Listening for HTTP on /0:0:0:0:0:0:0:0:9000

(Server started, use Ctrl+D to stop and go back to the console...)

---------------- Initial Site Hit -----------------

[info] play - Application started (Dev)
[debug] o.p.p.s.ScalaController - getOrCreateSessionId : None
[debug] o.p.p.s.ScalaController - sessionId for getRedirectionUrl() : 614c8830-b62b-438e-b8f8-6b8cdb7edf34
[debug] o.p.p.CallbackController - defaultUrl : /
[debug] o.p.p.s.ScalaController - requestedUrlToSave : /
[debug] o.p.o.c.BaseOAuth20Client - Random state parameter: yikne1kP9T
[debug] o.p.p.s.ScalaController - redirectAction to : org.pac4j.core.client.RedirectAction@6196d5a8
[debug] o.p.p.s.ScalaController - redirectAction to : org.pac4j.core.client.RedirectAction@6196d5a8
[debug] o.p.p.s.ScalaController - sessionId for profile : None

---------------- Authenticate Link -----------------

[debug] o.p.p.j.ActionContext - clientName : Google2Client
[debug] o.p.p.StorageHelper - retrieved sessionId : null
[debug] o.p.p.StorageHelper - generated sessionId : 2b1949dd-1556-4f8a-8f3b-15ab9afd6573
[debug] o.p.p.j.RequiresAuthenticationAction - client : <Google2Client> | callbackUrl: http://localhost:9000/callback?client_name=Google2Client | name: null | isDirectRedirection: true | enableContextualRedirects: false |
[debug] o.p.o.c.BaseOAuth20Client - sessionState : null / stateParameter : yikne1kP9T
[error] o.p.o.c.BaseOAuth20Client - Missing state parameter : session expired or possible threat of cross-site request forgery
[error] o.p.p.j.RequiresAuthenticationAction - Unexpected error
org.pac4j.oauth.client.exception.OAuthCredentialsException: Missing state parameter : session expired or possible threat of cross-site request forgery
at org.pac4j.oauth.client.BaseOAuth20Client.getOAuthCredentials(BaseOAuth20Client.java:86) ~[pac4j-oauth-1.7.0.jar:na]
at org.pac4j.oauth.client.BaseOAuthClient.retrieveCredentials(BaseOAuthClient.java:128) ~[pac4j-oauth-1.7.0.jar:na]
at org.pac4j.oauth.client.BaseOAuthClient.retrieveCredentials(BaseOAuthClient.java:45) ~[pac4j-oauth-1.7.0.jar:na]
at org.pac4j.core.client.BaseClient.getCredentials(BaseClient.java:220) ~[pac4j-core-1.7.0.jar:na]
at org.pac4j.play.java.RequiresAuthenticationAction$6.apply(RequiresAuthenticationAction.java:202) ~[play-pac4j_java-1.4.0.jar:na]
[error] play - Cannot invoke the action, eventually got an error: org.pac4j.oauth.client.exception.OAuthCredentialsException: Missing state parameter : session expired or possible threat of cross-site request forgery
[error] application -

! @6ma5kd984 - Internal server error, for (GET) [/callback?client_name=Google2Client&state=yikne1kP9T&code=4/4yDPyazII6KPsYQ327joF_766-D6OIT5aC-2IBIhJWc.InBMd_LXLNEaJvIeHux6iLb73ng1mwI] ->

play.api.Application$$anon$1: Execution exception[[OAuthCredentialsException: Missing state parameter : session expired or possible threat of cross-site request forgery]]
at play.api.Application$class.handleError(Application.scala:296) ~[play_2.11-2.3.9.jar:2.3.9]
at play.api.DefaultApplication.handleError(Application.scala:402) [play_2.11-2.3.9.jar:2.3.9]
at play.core.server.netty.PlayDefaultUpstreamHandler$$anonfun$3$$anonfun$applyOrElse$4.apply(PlayDefaultUpstreamHandler.scala:320) [play_2.11-2.3.9.jar:2.3.9]
at play.core.server.netty.PlayDefaultUpstreamHandler$$anonfun$3$$anonfun$applyOrElse$4.apply(PlayDefaultUpstreamHandler.scala:320) [play_2.11-2.3.9.jar:2.3.9]
at scala.Option.map(Option.scala:146) [scala-library-2.11.6.jar:na]
Caused by: org.pac4j.oauth.client.exception.OAuthCredentialsException: Missing state parameter : session expired or possible threat of cross-site request forgery
at org.pac4j.oauth.client.BaseOAuth20Client.getOAuthCredentials(BaseOAuth20Client.java:86) ~[pac4j-oauth-1.7.0.jar:na]
at org.pac4j.oauth.client.BaseOAuthClient.retrieveCredentials(BaseOAuthClient.java:128) ~[pac4j-oauth-1.7.0.jar:na]
at org.pac4j.oauth.client.BaseOAuthClient.retrieveCredentials(BaseOAuthClient.java:45) ~[pac4j-oauth-1.7.0.jar:na]
at org.pac4j.core.client.BaseClient.getCredentials(BaseClient.java:220) ~[pac4j-core-1.7.0.jar:na]
at org.pac4j.play.java.RequiresAuthenticationAction$6.apply(RequiresAuthenticationAction.java:202) ~[play-pac4j_java-1.4.0.jar:na]

Thanks again and please let me know if this is clear enough / helpful.

Jérôme LELEU

unread,
May 29, 2015, 2:16:54 AM5/29/15
to Kristian Kime, pac4j...@googlegroups.com
Hi,

I just made the test with logs enabled and I have a fairly different output. Compared to your logs, the differences are in red:

[debug] o.p.p.s.ScalaController - redirectAction to : org.pac4j.core.client.RedirectAction@589580b3
[debug] o.p.p.s.ScalaController - redirectAction to : org.pac4j.core.client.RedirectAction@589580b3
[debug] o.p.p.s.ScalaController - sessionId for profile : None

---------------- Authenticate Link -----------------

[debug] o.p.p.s.ScalaController - Entering RequiresAuthentication
[debug] o.p.p.s.ScalaController - getOrCreateSessionId : None
[debug] o.p.p.s.ScalaController - sessionId : a493016a-a1a6-4c03-8d4c-9aae9b817df9
[debug] o.p.p.s.ScalaController - sessionId for profile : None
[debug] o.p.p.s.ScalaController - profile : null
[debug] o.p.p.s.ScalaController - sessionId for getRedirectionUrl() : a493016a-a1a6-4c03-8d4c-9aae9b817df9
[debug] o.p.p.CallbackController - defaultUrl : /protected
[debug] o.p.p.s.ScalaController - requestedUrlToSave : /protected
[debug] o.p.o.c.BaseOAuth20Client - Random state parameter: nJvpPHi6YI
code&state=nJvpPHi6YI
[debug] o.p.p.s.ScalaController - redirectAction to : org.pac4j.core.client.RedirectAction@34de2990
[debug] o.p.p.s.ScalaController - redirectAction : org.pac4j.core.client.RedirectAction@34de2990

[debug] o.p.p.j.ActionContext - clientName : Google2Client
[debug] o.p.p.StorageHelper - retrieved sessionId : a493016a-a1a6-4c03-8d4c-9aae9b817df9
[debug] o.p.p.j.RequiresAuthenticationAction - client : <Google2Client> | callbackUrl: http://localhost:8080/callback?client_name=Google2Client | name
: null | isDirectRedirection: true | enableContextualRedirects: false |

As soon as I click on the /protected url, I enter into the RequiresAuthentication function to compute the redirection url and be redirected to Google, something I don't see in your logs, although you are actually redirected to Google, aren't you?

Very strange... The authentication process does not even seem to start for you...

Can you tell more about your environment? JDK? OS? Play version?

I think the best now is to do some debugging inside the application. First, can you put a break point inside the action just to get the stack trace and the caller (a class from Play)? Then, can you put a break point in this caller and try to figure out where it goes and why it doesn't seem to go inside the RequiresAuthentication function?

Thanks.
Best regards,
Jérôme



Kristian Kime

unread,
May 30, 2015, 8:35:55 PM5/30/15
to pac4j...@googlegroups.com, kristi...@gmail.com
Quick rundown on the environment:

I'm on a mac os version 10.10.3
using activator version 1.3.2 (that should be checked in)
Oracle's JDK 1.8.0_45 (for mac obviously)
I've run this both from the command line and intellij (not that it should really matter..)
and tried both Chrome and Firefox in case the browser was somehow the issue

One thing I'm nor sure if I communicated clearly. The "protected" link works fine me. It's only when I click on "Authenticate with Google" that I get an error. In fact it even works when I click on the "protected" link and then click on "Authenticate with Google".

The only way it fails is if I haven't used the protected link (or used it and then logged out) and click "Authenticate with Google".

Going to do the breakpoints when I get a free moment.

Be well,
-Kristian

Kristian Kime

unread,
May 30, 2015, 9:10:33 PM5/30/15
to pac4j...@googlegroups.com
Hmm... my apologies I must not be understanding how your library works. I just noticed that the "Authenticate with Google" link is a direct link to google that includes a state parameter. But presumably I need to generate a session for this before I hit google and it redirects back? Do I need to do something before I let them use that link?

Jérôme LELEU

unread,
May 31, 2015, 3:30:40 AM5/31/15
to Kristian Kime, pac4j...@googlegroups.com
Hi,

I see now: I was testing the protected link (which indeed works), but the direct link to Google didn't work.

As you generate a new session, you must pass it to the view to make everything work (https://github.com/kristiankime/play-pac4j-heroku/blob/master/app/controllers/Application.scala#L14):

Ok(views.html.index(profile, urlGoogle))

must be replaced by:

Ok(views.html.index(profile, urlGoogle)).withSession(newSession)

Thanks.
Best regards,
Jérôme


Kristian Kime

unread,
May 31, 2015, 7:53:04 AM5/31/15
to pac4j...@googlegroups.com, kristi...@gmail.com
Yeah that seems to have fixed it. All that for a copy / paste error. Sorry about that. Thanks for taking the time to help.
Reply all
Reply to author
Forward
0 new messages