Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[3.0.x] Allow users to upgrade to jjwt 0.12.5 #12474

Merged
merged 1 commit into from
Mar 12, 2024

Conversation

mkurz
Copy link
Member

@mkurz mkurz commented Mar 12, 2024

Users can put in their build.sbt, just like we do:

val jjwtVersion = "0.12.5"
val jjwts = Seq(
  "io.jsonwebtoken" % "jjwt-api",
  "io.jsonwebtoken" % "jjwt-impl"
).map(_ % jjwtVersion) ++ Seq(
  ("io.jsonwebtoken" % "jjwt-jackson" % jjwtVersion).excludeAll(ExclusionRule("com.fasterxml.jackson.core"))
)

libraryDependencies ++= jjwts

/cc @hertg This one is for you, so you can upgrade jjwt in your Play 3.0.x project.

Users can put in their build.sbt:
```
val jjwtVersion = "0.12.5"
val jjwts = Seq(
  "io.jsonwebtoken" % "jjwt-api",
  "io.jsonwebtoken" % "jjwt-impl"
).map(_ % jjwtVersion) ++ Seq(
  ("io.jsonwebtoken" % "jjwt-jackson" % jjwtVersion).excludeAll(ExclusionRule("com.fasterxml.jackson.core"))
)

libraryDependencies ++= jjwts
```
private val jwtParser: JwtParser = Jwts
.parserBuilder()
private val jwtParser: JwtParser = Classes
.newInstance[JwtParserBuilder]("io.jsonwebtoken.impl.DefaultJwtParserBuilder")
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No actual change here, we just call what the method does for us, which is identical in 0.11.5 and 0.12.5:

.getDeclaredMethod("parseClaimsJws", classOf[CharSequence])
.invoke(jwtParser, encodedString.asInstanceOf[CharSequence])
.asInstanceOf[Jws[Claims]]
}
Copy link
Member Author

@mkurz mkurz Mar 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's the only part where we need to fall back to reflection:

This would be source compatible, but not binary compatible, because the bytecode generated refers to the String method in the 0.11.5 jar, but this String method does not exist anymore in the 0.12.5 jars.
Anway we can detect that and use reflection as last resort.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, Matthias, promise that we won't do the same in main branch 🙏 😄

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ihostage Of course not, in main there is #12211 which does everything correctly.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By default in 3.0.x nothing changes, this just gives users the opportunity to upgrade to make use of latest jjwt features.

@mkurz mkurz added this to the 2.9.3 / 3.0.3 milestone Mar 12, 2024
@mkurz
Copy link
Member Author

mkurz commented Mar 12, 2024

@Mergifyio backport 2.9.x

Copy link
Contributor

mergify bot commented Mar 12, 2024

backport 2.9.x

✅ Backports have been created

@mkurz mkurz merged commit 4567ab5 into playframework:3.0.x Mar 12, 2024
25 checks passed
@mkurz mkurz deleted the jjwt-compatibility branch March 12, 2024 14:41
mergify bot added a commit that referenced this pull request Mar 12, 2024
[2.9.x] Allow users to upgrade to jjwt 0.12.5 (backport #12474) by @mkurz
@hertg
Copy link
Contributor

hertg commented May 7, 2024

Hi @mkurz

Just saw the new release today, thank you so much for your efforts. I've tried it out, and put the dependency override in my build.sbt, just as advised. Unfortunately, I don't think it works, unless I messed something else up on my end.

They changed some methods on JwtBuilder (the setXY() methods were renamed to xy()).

0.11.5: https://github.com/jwtk/jjwt/blob/0.11.5/api/src/main/java/io/jsonwebtoken/JwtBuilder.java#L260
0.12.5: https://github.com/jwtk/jjwt/blob/0.12.5/api/src/main/java/io/jsonwebtoken/JwtBuilder.java#L550

I assume you were not aware of those changes while implementing this workaround? Or did I do something wrong?
It's not that big of a deal, I'll continue to work on my fork for now. Just wanted to figure out if the issues is on my end, I don't fully understand how the workaround / dependency override works :)


The play application starts successfuly, and works until Play tries to create the Play session cookie, then the app terminates with the following stacktrace. If the cookie already exists, everything works fine (i.e. the parsing works).

Uncaught error from thread [application-pekko.actor.default-dispatcher-5]: 'io.jsonwebtoken.JwtBuilder io.jsonwebtoken.JwtBuilder.setNotBefore(java.util.Date)', shutting down JVM since 'pekko.jvm-exit-on-fatal-error' is enabled for ActorSystem[application]
java.lang.NoSuchMethodError: 'io.jsonwebtoken.JwtBuilder io.jsonwebtoken.JwtBuilder.setNotBefore(java.util.Date)'
	at play.api.mvc.JWTCookieDataCodec$JWTFormatter.format(Cookie.scala:797)
	at play.api.mvc.JWTCookieDataCodec.encode(Cookie.scala:643)
	at play.api.mvc.JWTCookieDataCodec.encode$(Cookie.scala:641)
	at play.api.mvc.DefaultJWTCookieDataCodec.encode(Cookie.scala:844)
	at play.api.mvc.FallbackCookieDataCodec.encode(Cookie.scala:826)
	at play.api.mvc.FallbackCookieDataCodec.encode$(Cookie.scala:826)
	at play.api.mvc.DefaultSessionCookieBaker.encode(Session.scala:120)
	at play.api.mvc.CookieBaker.encodeAsCookie(Cookie.scala:478)
	at play.api.mvc.CookieBaker.encodeAsCookie$(Cookie.scala:477)
	at play.api.mvc.DefaultSessionCookieBaker.encodeAsCookie(Session.scala:120)
	at play.api.mvc.Result.$anonfun$bakeCookies$2(Results.scala:359)
	at scala.Option.map(Option.scala:242)
	at play.api.mvc.Result.bakeCookies(Results.scala:358)
	at play.core.server.common.ServerResultUtils.prepareCookies(ServerResultUtils.scala:312)
	at play.core.server.PekkoHttpServer.$anonfun$runAction$6(PekkoHttpServer.scala:494)
	at scala.concurrent.impl.Promise$Transformation.run(Promise.scala:470)
	at org.apache.pekko.dispatch.BatchingExecutor$AbstractBatch.processBatch(BatchingExecutor.scala:73)
	at org.apache.pekko.dispatch.BatchingExecutor$BlockableBatch.$anonfun$run$1(BatchingExecutor.scala:110)
	at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.scala:18)
	at scala.concurrent.BlockContext$.withBlockContext(BlockContext.scala:94)
	at org.apache.pekko.dispatch.BatchingExecutor$BlockableBatch.run(BatchingExecutor.scala:110)
	at org.apache.pekko.dispatch.TaskInvocation.run(AbstractDispatcher.scala:59)
	at org.apache.pekko.dispatch.ForkJoinExecutorConfigurator$PekkoForkJoinTask.exec(ForkJoinExecutorConfigurator.scala:57)
	at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387)
	at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1312)
	at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1843)
	at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1808)
	at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:188)
16:45:00 [ERROR] [o.a.p.a.ActorSystemImpl]  Uncaught error from thread [application-pekko.actor.default-dispatcher-5]: 'io.jsonwebtoken.JwtBuilder io.jsonwebtoken.JwtBuilder.setNotBefore(java.util.Date)', shutting down JVM since 'pekko.jvm-exit-on-fatal-error' is enabled for ActorSystem[application]
java.lang.NoSuchMethodError: 'io.jsonwebtoken.JwtBuilder io.jsonwebtoken.JwtBuilder.setNotBefore(java.util.Date)'
	at play.api.mvc.JWTCookieDataCodec$JWTFormatter.format(Cookie.scala:797)
	at play.api.mvc.JWTCookieDataCodec.encode(Cookie.scala:643)
	at play.api.mvc.JWTCookieDataCodec.encode$(Cookie.scala:641)
	at play.api.mvc.DefaultJWTCookieDataCodec.encode(Cookie.scala:844)
	at play.api.mvc.FallbackCookieDataCodec.encode(Cookie.scala:826)
	at play.api.mvc.FallbackCookieDataCodec.encode$(Cookie.scala:826)
	at play.api.mvc.DefaultSessionCookieBaker.encode(Session.scala:120)
	at play.api.mvc.CookieBaker.encodeAsCookie(Cookie.scala:478)
	at play.api.mvc.CookieBaker.encodeAsCookie$(Cookie.scala:477)
	at play.api.mvc.DefaultSessionCookieBaker.encodeAsCookie(Session.scala:120)
16:45:00 [INFO ] [p.c.s.PekkoHttpServer]  Stopping Pekko HTTP server...
16:45:00 [INFO ] [p.c.s.PekkoHttpServer]  Unbinding /[0:0:0:0:0:0:0:0]:9000
16:45:00 [INFO ] [p.c.s.PekkoHttpServer]  Terminating server binding for /[0:0:0:0:0:0:0:0]:9000
16:45:05 [INFO ] [p.a.d.HikariCPConnectionPool]  Shutting down connection pool.
16:45:05 [INFO ] [p.c.s.PekkoHttpServer]  Running provided shutdown stop hooks

@mkurz
Copy link
Member Author

mkurz commented May 8, 2024

@hertg I will take a look

@mkurz
Copy link
Member Author

mkurz commented May 8, 2024

@hertg Thanks for the report. The problem should be fixed with

now. I published Play 3.0.4-M1 (https://github.com/playframework/playframework/releases/tag/3.0.4-M1), can you please upgrade and test?
Thanks!

@hertg
Copy link
Contributor

hertg commented May 8, 2024

@mkurz I tested out 3.0.4-M1, the creation and parsing of the Play session cookie seems to be working now. I'll continue to work with this pre-release version and will report back if I stumble upon any issues. So far, all looks good. As always, great work Matthias, thank you kindly.

@mkurz
Copy link
Member Author

mkurz commented May 8, 2024

@hertg Great! Actually you can use 3.0.4-M1 in production. The only change right now is this fix, see 3.0.3...3.0.4-M1

It would be great if you can keep testing this patch in May and eventually report any other problems. If everything is OK I can cut 3.0.4 in June (including other fixes and dependency upgrades then of courses).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants