Skip to content

Commit

Permalink
feat(javasdk): support multiple values in a static claim (#1925)
Browse files Browse the repository at this point in the history
* feat(java-sdk): support multiple values in a static claim

* add note in docs

* bump runtime to 1.1.29
  • Loading branch information
efgpinto authored Jan 9, 2024
1 parent c620814 commit 92feccf
Show file tree
Hide file tree
Showing 10 changed files with 45 additions and 32 deletions.
6 changes: 4 additions & 2 deletions docs/src/modules/java/pages/using-jwts.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,11 @@ When the values of specific claims are known in advance, Kalix can be configured
@JWT(validate = JWT.JwtMethodMode.BEARER_TOKEN,
bearerTokenIssuer = "my-issuer",
staticClaims = {
@JWT.StaticClaim(claim = "role", value = "admin"),
@JWT.StaticClaim(claim = "aud", value = "${ENV}.kalix.io")})
@JWT.StaticClaim(claim = "role", value = {"admin", "editor"}), // <1>
@JWT.StaticClaim(claim = "aud", value = "${ENV}.kalix.io")}) // <2>
----
<1> When declaring multiple values for the same claim, **all** of them will be required when validating the request.
<2> The value of the claim can be dependent on an environment variable, which will be resolved at runtime.

NOTE: For specifying an issuer claim (i.e. "iss"), you should still use the provided issuer-specific fields and not static claims.

Expand Down
2 changes: 1 addition & 1 deletion project/Dependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ object Dependencies {
val RuntimeVersion = System.getProperty(
"kalix-runtime.version",
// temporarily accept the old system property name
System.getProperty("kalix-proxy.version", "1.1.27"))
System.getProperty("kalix-proxy.version", "1.1.29"))
}

// changing the Scala version of the Java SDK affects end users
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,10 @@ enum JwtMethodMode {

/**
* The value can be set as: a hardcoded literal (e.g. "admin"), an ENV variable (e.g "${ENV_VAR}")
* or a combination of both (e.g. "${ENV_VAR}-admin")
* or a combination of both (e.g. "${ENV_VAR}-admin").
* When declaring multiple values, ALL of those will be required when validating the claim.
*/
String value();
String[] value();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,15 @@
package kalix.javasdk.impl

import java.lang.reflect.Method

import kalix.JwtMethodOptions
import kalix.JwtServiceOptions
import kalix.MethodOptions
import kalix.javasdk.annotations.JWT
import Reflect.Syntax._
import kalix.JwtStaticClaim

import scala.jdk.CollectionConverters.IterableHasAsJava

object JwtDescriptorFactory {

private def jwtMethodOptions(javaMethod: Method): JwtMethodOptions = {
Expand All @@ -34,9 +35,11 @@ object JwtDescriptorFactory {
.validate()
.map(springValidate => jwt.addValidate(JwtMethodOptions.JwtMethodMode.forNumber(springValidate.ordinal())))
ann.bearerTokenIssuer().map(jwt.addBearerTokenIssuer)

ann
.staticClaims()
.foreach(sc => jwt.addStaticClaim(JwtStaticClaim.newBuilder().setClaim(sc.claim()).setValue(sc.value())))
.foreach(sc =>
jwt.addStaticClaim(JwtStaticClaim.newBuilder().setClaim(sc.claim()).addAllValue(sc.value().toList.asJava)))
jwt.build()
}

Expand Down Expand Up @@ -66,7 +69,8 @@ object JwtDescriptorFactory {
ann.bearerTokenIssuer().map(jwt.addBearerTokenIssuer)
ann
.staticClaims()
.foreach(sc => jwt.addStaticClaim(JwtStaticClaim.newBuilder().setClaim(sc.claim()).setValue(sc.value())))
.foreach(sc =>
jwt.addStaticClaim(JwtStaticClaim.newBuilder().setClaim(sc.claim()).addAllValue(sc.value().toList.asJava)))

val kalixServiceOptions = kalix.ServiceOptions.newBuilder()
kalixServiceOptions.setJwt(jwt.build())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,8 @@ public static class ActionWithMethodLevelJWT extends Action {
bearerTokenIssuer = {"a", "b"},
staticClaims = {
@JWT.StaticClaim(claim = "role", value = "admin"),
@JWT.StaticClaim(claim = "aud", value = "${ENV}.kalix.io")
@JWT.StaticClaim(claim = "aud", value = "${ENV}.kalix.io"),
@JWT.StaticClaim(claim = "more-roles", value = {"viewer", "editor"})
})
public Action.Effect<Message> message(@RequestBody Message msg) {
return effects().reply(msg);
Expand All @@ -120,7 +121,8 @@ public Action.Effect<Message> message(@RequestBody Message msg) {
bearerTokenIssuer = {"a", "b"},
staticClaims = {
@JWT.StaticClaim(claim = "role", value = "admin"),
@JWT.StaticClaim(claim = "aud", value = "${ENV}.kalix.io")
@JWT.StaticClaim(claim = "aud", value = "${ENV}.kalix.io"),
@JWT.StaticClaim(claim = "more-roles", value = {"editor", "viewer"})
})
public static class ActionWithServiceLevelJWT extends Action {
@PostMapping("/message")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,11 +224,13 @@ class ActionDescriptorFactorySpec extends AnyWordSpec with ComponentDescriptorSu
jwtOption.getValidate(0) shouldBe JwtMethodMode.BEARER_TOKEN
assertRequestFieldJavaType(method, "json_body", JavaType.MESSAGE)

val Seq(claim1, claim2) = jwtOption.getStaticClaimList.asScala.toSeq
val Seq(claim1, claim2, claim3) = jwtOption.getStaticClaimList.asScala.toSeq
claim1.getClaim shouldBe "role"
claim1.getValue shouldBe "admin"
claim1.getValue(0) shouldBe "admin"
claim2.getClaim shouldBe "aud"
claim2.getValue shouldBe "${ENV}.kalix.io"
claim2.getValue(0) shouldBe "${ENV}.kalix.io"
claim3.getClaim shouldBe "more-roles"
claim3.getValueList.asScala.toSeq shouldBe Seq("viewer", "editor")
}
}

Expand All @@ -240,11 +242,13 @@ class ActionDescriptorFactorySpec extends AnyWordSpec with ComponentDescriptorSu
jwtOption.getBearerTokenIssuer(1) shouldBe "b"
jwtOption.getValidate shouldBe JwtServiceMode.BEARER_TOKEN

val Seq(claim1, claim2) = jwtOption.getStaticClaimList.asScala.toSeq
val Seq(claim1, claim2, claim3) = jwtOption.getStaticClaimList.asScala.toSeq
claim1.getClaim shouldBe "role"
claim1.getValue shouldBe "admin"
claim1.getValue(0) shouldBe "admin"
claim2.getClaim shouldBe "aud"
claim2.getValue shouldBe "${ENV}.kalix.io"
claim2.getValue(0) shouldBe "${ENV}.kalix.io"
claim3.getClaim shouldBe "more-roles"
claim3.getValueList.asScala.toSeq shouldBe Seq("editor", "viewer")
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,9 @@ class EventSourcedEntityDescriptorFactorySpec extends AnyWordSpec with Component

val Seq(claim1, claim2) = jwtOption2.getStaticClaimList.asScala.toSeq
claim1.getClaim shouldBe "role"
claim1.getValue shouldBe "method-admin"
claim1.getValue(0) shouldBe "method-admin"
claim2.getClaim shouldBe "aud"
claim2.getValue shouldBe "${ENV}"
claim2.getValue(0) shouldBe "${ENV}"
}
}

Expand All @@ -141,9 +141,9 @@ class EventSourcedEntityDescriptorFactorySpec extends AnyWordSpec with Component

val Seq(claim1, claim2) = jwtOption.getStaticClaimList.asScala.toSeq
claim1.getClaim shouldBe "role"
claim1.getValue shouldBe "admin"
claim1.getValue(0) shouldBe "admin"
claim2.getClaim shouldBe "aud"
claim2.getValue shouldBe "${ENV}.kalix.io"
claim2.getValue(0) shouldBe "${ENV}.kalix.io"
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,9 @@ class ValueEntityDescriptorFactorySpec extends AnyWordSpec with ComponentDescrip
jwtOption.getValidate shouldBe JwtServiceMode.BEARER_TOKEN
val Seq(claim1, claim2) = jwtOption.getStaticClaimList.asScala.toSeq
claim1.getClaim shouldBe "role"
claim1.getValue shouldBe "admin"
claim1.getValue(0) shouldBe "admin"
claim2.getClaim shouldBe "aud"
claim2.getValue shouldBe "${ENV}.kalix.io"
claim2.getValue(0) shouldBe "${ENV}.kalix.io"
}
}

Expand All @@ -126,9 +126,9 @@ class ValueEntityDescriptorFactorySpec extends AnyWordSpec with ComponentDescrip
jwtOption.getValidate(0) shouldBe JwtMethodMode.BEARER_TOKEN
val Seq(claim1, claim2) = jwtOption.getStaticClaimList.asScala.toSeq
claim1.getClaim shouldBe "role"
claim1.getValue shouldBe "method-admin"
claim1.getValue(0) shouldBe "method-admin"
claim2.getClaim shouldBe "aud"
claim2.getValue shouldBe "${ENV}"
claim2.getValue(0) shouldBe "${ENV}"
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -285,9 +285,9 @@ class ViewDescriptorFactorySpec extends AnyWordSpec with ComponentDescriptorSuit

val Seq(claim1, claim2) = jwtOption.getStaticClaimList.asScala.toSeq
claim1.getClaim shouldBe "role"
claim1.getValue shouldBe "admin"
claim1.getValue(0) shouldBe "admin"
claim2.getClaim shouldBe "aud"
claim2.getValue shouldBe "${ENV}.kalix.io"
claim2.getValue(0) shouldBe "${ENV}.kalix.io"
}
}

Expand All @@ -301,9 +301,9 @@ class ViewDescriptorFactorySpec extends AnyWordSpec with ComponentDescriptorSuit

val Seq(claim1, claim2) = jwtOption.getStaticClaimList.asScala.toSeq
claim1.getClaim shouldBe "role"
claim1.getValue shouldBe "admin"
claim1.getValue(0) shouldBe "admin"
claim2.getClaim shouldBe "aud"
claim2.getValue shouldBe "${ENV}.kalix.io"
claim2.getValue(0) shouldBe "${ENV}.kalix.io"
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,9 @@ class WorkflowEntityDescriptorFactorySpec extends AnyWordSpec with ComponentDesc

val Seq(claim1, claim2) = jwtOption.getStaticClaimList.asScala.toSeq
claim1.getClaim shouldBe "role"
claim1.getValue shouldBe "method-admin"
claim1.getValue(0) shouldBe "method-admin"
claim2.getClaim shouldBe "aud"
claim2.getValue shouldBe "${ENV}.kalix.io"
claim2.getValue(0) shouldBe "${ENV}.kalix.io"
}
}

Expand All @@ -127,9 +127,9 @@ class WorkflowEntityDescriptorFactorySpec extends AnyWordSpec with ComponentDesc

val Seq(claim1, claim2) = jwtOption.getStaticClaimList.asScala.toSeq
claim1.getClaim shouldBe "role"
claim1.getValue shouldBe "admin"
claim1.getValue(0) shouldBe "admin"
claim2.getClaim shouldBe "aud"
claim2.getValue shouldBe "${ENV}"
claim2.getValue(0) shouldBe "${ENV}"
}
}

Expand Down

0 comments on commit 92feccf

Please sign in to comment.