From 2050c030bd49991e399e227e528d9eae6ac00316 Mon Sep 17 00:00:00 2001 From: Robin Karlsson Date: Sat, 25 Mar 2023 10:51:40 +0100 Subject: [PATCH 1/3] Preserve insertion order for claims --- .../main/java/com/auth0/jwt/JWTCreator.java | 4 +- .../java/com/auth0/jwt/JWTCreatorTest.java | 43 +++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/lib/src/main/java/com/auth0/jwt/JWTCreator.java b/lib/src/main/java/com/auth0/jwt/JWTCreator.java index 7ed83940..ed46f78f 100644 --- a/lib/src/main/java/com/auth0/jwt/JWTCreator.java +++ b/lib/src/main/java/com/auth0/jwt/JWTCreator.java @@ -71,8 +71,8 @@ public static class Builder { private final Map headerClaims; Builder() { - this.payloadClaims = new HashMap<>(); - this.headerClaims = new HashMap<>(); + this.payloadClaims = new LinkedHashMap<>(); + this.headerClaims = new LinkedHashMap<>(); } /** diff --git a/lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java b/lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java index 8fc83b44..8b28724e 100644 --- a/lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java +++ b/lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java @@ -4,6 +4,7 @@ import com.auth0.jwt.interfaces.ECDSAKeyProvider; import com.auth0.jwt.interfaces.RSAKeyProvider; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -938,4 +939,46 @@ public void shouldCreatePayloadWithNullForList() { assertThat(jwt, is(notNullValue())); assertTrue(JWT.decode(jwt).getClaim("name").isNull()); } + + @Test + public void shouldPreserveInsertionOrder() throws Exception { + List headerInsertionOrder = new ArrayList<>(); + Map header = new LinkedHashMap<>(); + for (int i = 0; i < 10; i++) { + String key = "h" + i; + header.put(key, "v" + 1); + headerInsertionOrder.add(key); + } + + List payloadInsertionOrder = new ArrayList<>(); + JWTCreator.Builder builder = JWTCreator.init().withHeader(header); + for (int i = 0; i < 10; i++) { + String name = "c" + i; + builder = builder.withClaim(name, "v" + i); + payloadInsertionOrder.add(name); + } + String signed = builder.sign(Algorithm.HMAC256("secret")); + + assertThat(signed, is(notNullValue())); + String[] parts = signed.split("\\."); + Base64.Decoder urlDecoder = Base64.getUrlDecoder(); + String headerJson = new String(urlDecoder.decode(parts[0]), StandardCharsets.UTF_8); + String payloadJson = new String(urlDecoder.decode(parts[1]), StandardCharsets.UTF_8); + + ObjectMapper objectMapper = new ObjectMapper(); + + List headerFields = new ArrayList<>(); + objectMapper.readValue(headerJson, ObjectNode.class) + .fieldNames().forEachRemaining(headerFields::add); + headerFields.retainAll(headerInsertionOrder); + assertThat("Header insertion order should be preserved", + headerFields, is(equalTo(headerInsertionOrder))); + + List payloadFields = new ArrayList<>(); + objectMapper.readValue(payloadJson, ObjectNode.class) + .fieldNames().forEachRemaining(payloadFields::add); + payloadFields.retainAll(payloadInsertionOrder); + assertThat("Claim insertion order should be preserved", + payloadFields, is(equalTo(payloadInsertionOrder))); + } } From b8c91b7d0b3ef4c54e09c6a4db273bf208b7956a Mon Sep 17 00:00:00 2001 From: Robin Karlsson Date: Mon, 27 Mar 2023 17:40:17 +0200 Subject: [PATCH 2/3] Preserve insertion order for JSON claims --- lib/src/main/java/com/auth0/jwt/JWTCreator.java | 4 ++-- .../test/java/com/auth0/jwt/JWTCreatorTest.java | 14 +++++++++----- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/lib/src/main/java/com/auth0/jwt/JWTCreator.java b/lib/src/main/java/com/auth0/jwt/JWTCreator.java index ece589b1..0b0d21e4 100644 --- a/lib/src/main/java/com/auth0/jwt/JWTCreator.java +++ b/lib/src/main/java/com/auth0/jwt/JWTCreator.java @@ -112,7 +112,7 @@ public Builder withHeader(String headerClaimsJson) throws IllegalArgumentExcepti } try { - Map headerClaims = mapper.readValue(headerClaimsJson, HashMap.class); + Map headerClaims = mapper.readValue(headerClaimsJson, LinkedHashMap.class); return withHeader(headerClaims); } catch (JsonProcessingException e) { throw new IllegalArgumentException("Invalid header JSON", e); @@ -508,7 +508,7 @@ public Builder withPayload(String payloadClaimsJson) throws IllegalArgumentExcep } try { - Map payloadClaims = mapper.readValue(payloadClaimsJson, HashMap.class); + Map payloadClaims = mapper.readValue(payloadClaimsJson, LinkedHashMap.class); return withPayload(payloadClaims); } catch (JsonProcessingException e) { throw new IllegalArgumentException("Invalid payload JSON", e); diff --git a/lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java b/lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java index 1dd1c335..cd26e3ee 100644 --- a/lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java +++ b/lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java @@ -3,7 +3,6 @@ import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.interfaces.ECDSAKeyProvider; import com.auth0.jwt.interfaces.RSAKeyProvider; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import org.junit.Rule; @@ -974,7 +973,7 @@ public void withPayloadShouldSupportJsonValueWithNestedDataStructure() { .sign(Algorithm.HMAC256("secret")); assertThat(jwt, is(notNullValue())); - String[] parts = jwt.split("\\."); + String[] parts = jwt.split("\\.") ; String payloadJson = new String(Base64.getUrlDecoder().decode(parts[1]), StandardCharsets.UTF_8); assertThat(payloadJson, JsonMatcher.hasEntry("stringClaim", stringClaim)); @@ -1014,7 +1013,9 @@ public void shouldCreatePayloadWithNullForList() { @Test public void shouldPreserveInsertionOrder() throws Exception { - List headerInsertionOrder = new ArrayList<>(); + String taxonomyJson = "{\"class\": \"mammalia\", \"order\": \"carnivora\", \"family\": \"canidae\", \"genus\": \"vulpes\"}"; + List taxonomyClaims = Arrays.asList("class", "order", "family", "genus"); + List headerInsertionOrder = new ArrayList<>(taxonomyClaims); Map header = new LinkedHashMap<>(); for (int i = 0; i < 10; i++) { String key = "h" + i; @@ -1022,8 +1023,11 @@ public void shouldPreserveInsertionOrder() throws Exception { headerInsertionOrder.add(key); } - List payloadInsertionOrder = new ArrayList<>(); - JWTCreator.Builder builder = JWTCreator.init().withHeader(header); + List payloadInsertionOrder = new ArrayList<>(taxonomyClaims); + JWTCreator.Builder builder = JWTCreator.init() + .withHeader(taxonomyJson) + .withHeader(header) + .withPayload(taxonomyJson); for (int i = 0; i < 10; i++) { String name = "c" + i; builder = builder.withClaim(name, "v" + i); From c43a24178d4811c547d31520cd4bd5b1d8fb38cd Mon Sep 17 00:00:00 2001 From: Robin Karlsson Date: Mon, 27 Mar 2023 17:44:39 +0200 Subject: [PATCH 3/3] Oops, remove unintended whitespace --- lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java b/lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java index cd26e3ee..53cd267b 100644 --- a/lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java +++ b/lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java @@ -973,7 +973,7 @@ public void withPayloadShouldSupportJsonValueWithNestedDataStructure() { .sign(Algorithm.HMAC256("secret")); assertThat(jwt, is(notNullValue())); - String[] parts = jwt.split("\\.") ; + String[] parts = jwt.split("\\."); String payloadJson = new String(Base64.getUrlDecoder().decode(parts[1]), StandardCharsets.UTF_8); assertThat(payloadJson, JsonMatcher.hasEntry("stringClaim", stringClaim));