diff --git a/README.md b/README.md index 4be50dcc..aae66eb6 100644 --- a/README.md +++ b/README.md @@ -158,10 +158,10 @@ new ResourceConfig() This example shows - First method - An unprotected endpoint. No token is required to use this endpoint. -- Second method - A protected endpoint. This endpoint will require a valid token from the "employee" issuer. -- Third method - A protected endpoint. This endpoint will require a valid token from one of the configured issuers. -- Fourth method - A non-annotated endpoint. This endpoint will not be accessible from outside the server (will return a 501 NOT_IMPLEMENTED). - +- Second method - A protected endpoint. This endpoint will require a valid token from one of the configured issuers. +- Third method - A protected endpoint. This endpoint will require a valid token from the "employee" or "manager" issuer. +- Fourth method - A protected endpoint. This endpoint will require a valid token from the "manager" issuer and a claim where key is "acr" and value is "Level4". +- Fifth method - A non-annotated endpoint. This endpoint will not be accessible from outside the server (will return a 501 NOT_IMPLEMENTED). ```java @Path("/rest") public class ProductResource { @@ -181,6 +181,16 @@ public class ProductResource { public Product add(Product product) { return service.create(product); } + + @PUT + @PATH("/product") + @RequiredIssuers(value = { + ProtectedWithClaims(issuer = "employee"), + ProtectedWithClaims(issuer = "manager") + }) + public Product add(Product product) { + return service.update(product); + } @DELETE @PATH("/product/{id}") @@ -189,8 +199,17 @@ public class ProductResource { return service.delete(id); } + @GET + @PATH("/product/{id}") + public void add(String id) { + return service.get(id); + } } ``` + +The claimMap in **`@ProtectedWithClaims`** can contain entries where the expected value is an asterisk, e.g.: **`"acr=*"`**. This will require that the claim is present in the token, without regards to its value. + + ### token-validation-ktor See demo application in **`token-validation-ktor-demo`** for example configurations and setups. diff --git a/token-validation-core/src/main/java/no/nav/security/token/support/core/api/ProtectedWithClaims.java b/token-validation-core/src/main/java/no/nav/security/token/support/core/api/ProtectedWithClaims.java index cf66b11f..71ccf985 100644 --- a/token-validation-core/src/main/java/no/nav/security/token/support/core/api/ProtectedWithClaims.java +++ b/token-validation-core/src/main/java/no/nav/security/token/support/core/api/ProtectedWithClaims.java @@ -11,10 +11,11 @@ @Target({ TYPE, METHOD }) @Protected public @interface ProtectedWithClaims { - + String issuer(); /** - * Required claims in token in key=value format + * Required claims in token in key=value format. + * If the value is an asterisk (*), it checks that the required key is present. * @return array containing claims as key=value */ String[] claimMap() default {}; diff --git a/token-validation-core/src/main/java/no/nav/security/token/support/core/jwt/JwtTokenClaims.java b/token-validation-core/src/main/java/no/nav/security/token/support/core/jwt/JwtTokenClaims.java index 4c234b58..5134de7f 100644 --- a/token-validation-core/src/main/java/no/nav/security/token/support/core/jwt/JwtTokenClaims.java +++ b/token-validation-core/src/main/java/no/nav/security/token/support/core/jwt/JwtTokenClaims.java @@ -53,6 +53,9 @@ public boolean containsClaim(String name, String value) { if (claim == null) { return false; } + if (value.equals("*")) { + return true; + } if (claim instanceof String) { String claimAsString = (String) claim; return claimAsString.equals(value); diff --git a/token-validation-core/src/test/java/no/nav/security/token/support/core/jwt/JwtTokenClaimsTest.java b/token-validation-core/src/test/java/no/nav/security/token/support/core/jwt/JwtTokenClaimsTest.java index cab846ec..e6299154 100644 --- a/token-validation-core/src/test/java/no/nav/security/token/support/core/jwt/JwtTokenClaimsTest.java +++ b/token-validation-core/src/test/java/no/nav/security/token/support/core/jwt/JwtTokenClaimsTest.java @@ -14,13 +14,32 @@ class JwtTokenClaimsTest { @Test void containsClaimShouldHandleBothStringAndListClaim() { assertThat( - withClaim("arrayClaim", List.of("1","2")).containsClaim("arrayClaim", "1") + withClaim("arrayClaim", List.of("1", "2")).containsClaim("arrayClaim", "1") ).isTrue(); assertThat( withClaim("stringClaim", "1").containsClaim("stringClaim", "1") ).isTrue(); } + @Test + void containsClaimShouldHandleAsterisk() { + assertThat( + withClaim("stringClaim", "1").containsClaim("stringClaim", "*") + ).isTrue(); + assertThat( + withClaim("emptyStringClaim", "").containsClaim("emptyStringClaim", "*") + ).isTrue(); + assertThat( + withClaim("nullStringClaim", null).containsClaim("nullStringClaim", "*") + ).isFalse(); + assertThat( + withClaim("arrayClaim", List.of("1", "2")).containsClaim("arrayClaim", "*") + ).isTrue(); + assertThat( + withClaim("emptyArrayClaim", List.of()).containsClaim("emptyArrayClaim", "*") + ).isTrue(); + } + private JwtTokenClaims withClaim(String name, Object value) { var claims = new JWTClaimsSet.Builder().claim(name, value).build(); //do json parsing to simulate usage when creating from token