From 6524741fd1a4611021eedba5ecc685b0163d48be Mon Sep 17 00:00:00 2001 From: Enno <458526+ennru@users.noreply.github.com> Date: Tue, 24 Oct 2023 10:54:23 +0200 Subject: [PATCH 1/3] Replace Spray JWT dependency --- .../alpakka/google/auth/GoogleOAuth2.scala | 2 +- .../google/auth/JwtSprayJsonParser.scala | 77 +++++++++++++++++++ project/Dependencies.scala | 7 +- 3 files changed, 82 insertions(+), 4 deletions(-) create mode 100644 google-common/src/main/scala/akka/stream/alpakka/google/auth/JwtSprayJsonParser.scala diff --git a/google-common/src/main/scala/akka/stream/alpakka/google/auth/GoogleOAuth2.scala b/google-common/src/main/scala/akka/stream/alpakka/google/auth/GoogleOAuth2.scala index 61bebde794..e09b8c9351 100644 --- a/google-common/src/main/scala/akka/stream/alpakka/google/auth/GoogleOAuth2.scala +++ b/google-common/src/main/scala/akka/stream/alpakka/google/auth/GoogleOAuth2.scala @@ -13,7 +13,7 @@ import akka.stream.Materializer import akka.stream.alpakka.google.http.GoogleHttp import akka.stream.alpakka.google.{implicits, RequestSettings} import pdi.jwt.JwtAlgorithm.RS256 -import pdi.jwt.{JwtClaim, JwtSprayJson} +import pdi.jwt.JwtClaim import spray.json.DefaultJsonProtocol._ import spray.json.JsonFormat diff --git a/google-common/src/main/scala/akka/stream/alpakka/google/auth/JwtSprayJsonParser.scala b/google-common/src/main/scala/akka/stream/alpakka/google/auth/JwtSprayJsonParser.scala new file mode 100644 index 0000000000..bb5ab0806f --- /dev/null +++ b/google-common/src/main/scala/akka/stream/alpakka/google/auth/JwtSprayJsonParser.scala @@ -0,0 +1,77 @@ +/* + * Copyright (C) since 2016 Lightbend Inc. + */ + +/* + * This software is licensed under the Apache 2 license. + * Copyright 2021 JWT-Scala Contributors. + */ + +package akka.stream.alpakka.google.auth + +import pdi.jwt.{JwtAlgorithm, JwtClaim, JwtHeader, JwtJsonCommon} + +import java.time.Clock +import pdi.jwt.exceptions.JwtNonStringException +import spray.json._ + +/** + * Implementation of `JwtCore` using `JsObject` from spray-json. + * + * This class originally came from jwt-spray-json, + * but was removed in https://github.com/jwt-scala/jwt-scala/commit/bf1131ce02480103c0b953b97da001105a3ee038 + */ +trait JwtSprayJsonParser[H, C] extends JwtJsonCommon[JsObject, H, C] { + protected def parse(value: String): JsObject = value.parseJson.asJsObject + + protected def stringify(value: JsObject): String = value.compactPrint + + protected def getAlgorithm(header: JsObject): Option[JwtAlgorithm] = + header.fields.get("alg").flatMap { + case JsString("none") => None + case JsString(algo) => Option(JwtAlgorithm.fromString(algo)) + case JsNull => None + case _ => throw new JwtNonStringException("alg") + } + +} + +object JwtSprayJson extends JwtSprayJson(Clock.systemUTC) { + def apply(clock: Clock): JwtSprayJson = new JwtSprayJson(clock) +} + +class JwtSprayJson(override val clock: Clock) extends JwtSprayJsonParser[JwtHeader, JwtClaim] { + + import DefaultJsonProtocol._ + override def parseHeader(header: String): JwtHeader = { + val jsObj = parse(header) + JwtHeader( + algorithm = getAlgorithm(jsObj), + typ = safeGetField[String](jsObj, "typ"), + contentType = safeGetField[String](jsObj, "cty"), + keyId = safeGetField[String](jsObj, "kid") + ) + } + + override def parseClaim(claim: String): JwtClaim = { + val jsObj = parse(claim) + val content = JsObject(jsObj.fields - "iss" - "sub" - "aud" - "exp" - "nbf" - "iat" - "jti") + JwtClaim( + content = stringify(content), + issuer = safeGetField[String](jsObj, "iss"), + subject = safeGetField[String](jsObj, "sub"), + audience = safeGetField[Set[String]](jsObj, "aud") + .orElse(safeGetField[String](jsObj, "aud").map(s => Set(s))), + expiration = safeGetField[Long](jsObj, "exp"), + notBefore = safeGetField[Long](jsObj, "nbf"), + issuedAt = safeGetField[Long](jsObj, "iat"), + jwtId = safeGetField[String](jsObj, "jti") + ) + } + + private[this] def safeRead[A: JsonReader](js: JsValue) = + safeReader[A].read(js).fold(_ => None, a => Option(a)) + + private[this] def safeGetField[A: JsonReader](js: JsObject, name: String) = + js.fields.get(name).flatMap(safeRead[A]) +} diff --git a/project/Dependencies.scala b/project/Dependencies.scala index ecec888645..4074d28904 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -31,7 +31,8 @@ object Dependencies { val CouchbaseVersion = "2.7.16" val CouchbaseVersionForDocs = "2.7" - val JwtCoreVersion = "3.0.1" + // https://github.com/jwt-scala/jwt-scala/releases + val JwtScalaVersion = "9.4.4" val log4jOverSlf4jVersion = "1.7.36" val jclOverSlf4jVersion = "1.7.36" @@ -204,7 +205,7 @@ object Dependencies { libraryDependencies ++= Seq( "com.typesafe.akka" %% "akka-http" % AkkaHttpVersion, "com.typesafe.akka" %% "akka-http-spray-json" % AkkaHttpVersion, - "com.github.jwt-scala" %% "jwt-spray-json" % "9.0.2", // ApacheV2 + "com.github.jwt-scala" %% "jwt-json-common" % JwtScalaVersion, // https://github.com/googleapis/google-auth-library-java "com.google.auth" % "google-auth-library-credentials" % GoogleAuthVersion, "io.specto" % "hoverfly-java" % hoverflyVersion % Test // ApacheV2 @@ -304,7 +305,7 @@ object Dependencies { libraryDependencies ++= Seq( "com.typesafe.akka" %% "akka-http" % AkkaHttpVersion, "com.typesafe.akka" %% "akka-http-spray-json" % AkkaHttpVersion, - "com.github.jwt-scala" %% "jwt-spray-json" % "7.1.4" // ApacheV2 + "com.github.jwt-scala" %% "jwt-json-common" % JwtScalaVersion ) ++ Mockito ) From 28cd81bd280121a34aa9b12142fa505ef43d5006 Mon Sep 17 00:00:00 2001 From: Enno <458526+ennru@users.noreply.github.com> Date: Tue, 24 Oct 2023 11:58:38 +0200 Subject: [PATCH 2/3] Mark private and internal --- .../alpakka/google/auth/JwtSprayJsonParser.scala | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/google-common/src/main/scala/akka/stream/alpakka/google/auth/JwtSprayJsonParser.scala b/google-common/src/main/scala/akka/stream/alpakka/google/auth/JwtSprayJsonParser.scala index bb5ab0806f..debfe2a3f7 100644 --- a/google-common/src/main/scala/akka/stream/alpakka/google/auth/JwtSprayJsonParser.scala +++ b/google-common/src/main/scala/akka/stream/alpakka/google/auth/JwtSprayJsonParser.scala @@ -9,6 +9,7 @@ package akka.stream.alpakka.google.auth +import akka.annotation.InternalApi import pdi.jwt.{JwtAlgorithm, JwtClaim, JwtHeader, JwtJsonCommon} import java.time.Clock @@ -21,7 +22,8 @@ import spray.json._ * This class originally came from jwt-spray-json, * but was removed in https://github.com/jwt-scala/jwt-scala/commit/bf1131ce02480103c0b953b97da001105a3ee038 */ -trait JwtSprayJsonParser[H, C] extends JwtJsonCommon[JsObject, H, C] { +@InternalApi +private[auth] trait JwtSprayJsonParser[H, C] extends JwtJsonCommon[JsObject, H, C] { protected def parse(value: String): JsObject = value.parseJson.asJsObject protected def stringify(value: JsObject): String = value.compactPrint @@ -36,11 +38,13 @@ trait JwtSprayJsonParser[H, C] extends JwtJsonCommon[JsObject, H, C] { } -object JwtSprayJson extends JwtSprayJson(Clock.systemUTC) { +@InternalApi +private[auth] object JwtSprayJson extends JwtSprayJson(Clock.systemUTC) { def apply(clock: Clock): JwtSprayJson = new JwtSprayJson(clock) } -class JwtSprayJson(override val clock: Clock) extends JwtSprayJsonParser[JwtHeader, JwtClaim] { +@InternalApi +private[auth] class JwtSprayJson(override val clock: Clock) extends JwtSprayJsonParser[JwtHeader, JwtClaim] { import DefaultJsonProtocol._ override def parseHeader(header: String): JwtHeader = { From 28ced512cd0f7a68ff8d2f08a232de7be2132ffc Mon Sep 17 00:00:00 2001 From: Enno <458526+ennru@users.noreply.github.com> Date: Tue, 24 Oct 2023 12:05:59 +0200 Subject: [PATCH 3/3] Special copyright header for the imported file --- build.sbt | 3 ++- .../stream/alpakka/google/auth/JwtSprayJsonParser.scala | 6 ++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/build.sbt b/build.sbt index 0449d24d48..4f0fb94267 100644 --- a/build.sbt +++ b/build.sbt @@ -186,7 +186,8 @@ lazy val googleCommon = alpakkaProject( "google-common", "google.common", Dependencies.GoogleCommon, - Test / fork := true + Test / fork := true, + headerSources / excludeFilter := HiddenFileFilter || "JwtSprayJsonParser.scala" ) lazy val googleCloudBigQuery = alpakkaProject( diff --git a/google-common/src/main/scala/akka/stream/alpakka/google/auth/JwtSprayJsonParser.scala b/google-common/src/main/scala/akka/stream/alpakka/google/auth/JwtSprayJsonParser.scala index debfe2a3f7..ef4121b44e 100644 --- a/google-common/src/main/scala/akka/stream/alpakka/google/auth/JwtSprayJsonParser.scala +++ b/google-common/src/main/scala/akka/stream/alpakka/google/auth/JwtSprayJsonParser.scala @@ -1,10 +1,8 @@ -/* - * Copyright (C) since 2016 Lightbend Inc. - */ - /* * This software is licensed under the Apache 2 license. * Copyright 2021 JWT-Scala Contributors. + * + * Copyright (C) since 2023 Lightbend Inc. */ package akka.stream.alpakka.google.auth