Skip to content

Commit a18955b

Browse files
authored
Add support for passing json values for header and payload (#643)
1 parent b610b66 commit a18955b

File tree

3 files changed

+124
-4
lines changed

3 files changed

+124
-4
lines changed

lib/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ javadoc {
7272
}
7373

7474
dependencies {
75-
implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.4.2'
75+
implementation 'com.fasterxml.jackson.core:jackson-databind:2.14.0'
7676

7777
testImplementation 'org.bouncycastle:bcprov-jdk15on:1.70'
7878
testImplementation 'junit:junit:4.13.2'

lib/src/main/java/com/auth0/jwt/JWTCreator.java

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,27 @@ public Builder withHeader(Map<String, Object> headerClaims) {
9898
return this;
9999
}
100100

101+
/**
102+
* Add specific Claims to set as the Header.
103+
* If provided json is null then nothing is changed
104+
*
105+
* @param headerClaimsJson the values to use as Claims in the token's Header.
106+
* @return this same Builder instance.
107+
* @throws IllegalArgumentException if json value has invalid structure
108+
*/
109+
public Builder withHeader(String headerClaimsJson) throws IllegalArgumentException {
110+
if (headerClaimsJson == null) {
111+
return this;
112+
}
113+
114+
try {
115+
Map<String, Object> headerClaims = mapper.readValue(headerClaimsJson, HashMap.class);
116+
return withHeader(headerClaims);
117+
} catch (JsonProcessingException e) {
118+
throw new IllegalArgumentException("Invalid header JSON", e);
119+
}
120+
}
121+
101122
/**
102123
* Add a specific Key Id ("kid") claim to the Header.
103124
* If the {@link Algorithm} used to sign this token was instantiated with a KeyProvider,
@@ -467,6 +488,33 @@ public Builder withPayload(Map<String, ?> payloadClaims) throws IllegalArgumentE
467488
return this;
468489
}
469490

491+
/**
492+
* Add specific Claims to set as the Payload. If the provided json is null then
493+
* nothing is changed.
494+
*
495+
* <p>
496+
* If any of the claims are invalid, none will be added.
497+
* </p>
498+
*
499+
* @param payloadClaimsJson the values to use as Claims in the token's payload.
500+
* @return this same Builder instance.
501+
* @throws IllegalArgumentException if any of the claim keys or null,
502+
* or if the values are not of a supported type,
503+
* or if json value has invalid structure.
504+
*/
505+
public Builder withPayload(String payloadClaimsJson) throws IllegalArgumentException {
506+
if (payloadClaimsJson == null) {
507+
return this;
508+
}
509+
510+
try {
511+
Map<String, Object> payloadClaims = mapper.readValue(payloadClaimsJson, HashMap.class);
512+
return withPayload(payloadClaims);
513+
} catch (JsonProcessingException e) {
514+
throw new IllegalArgumentException("Invalid payload JSON", e);
515+
}
516+
}
517+
470518
private boolean validatePayload(Map<String, ?> payload) {
471519
for (Map.Entry<String, ?> entry : payload.entrySet()) {
472520
String key = entry.getKey();

lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java

Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.auth0.jwt.algorithms.Algorithm;
44
import com.auth0.jwt.interfaces.ECDSAKeyProvider;
55
import com.auth0.jwt.interfaces.RSAKeyProvider;
6+
import com.fasterxml.jackson.core.JsonProcessingException;
67
import com.fasterxml.jackson.databind.ObjectMapper;
78
import org.junit.Rule;
89
import org.junit.Test;
@@ -82,13 +83,48 @@ public void shouldAddHeaderClaim() {
8283

8384
@Test
8485
public void shouldReturnBuilderIfNullMapIsProvided() {
86+
Map<String, Object> nullMap = null;
87+
String nullString = null;
8588
String signed = JWTCreator.init()
86-
.withHeader(null)
89+
.withHeader(nullMap)
90+
.withHeader(nullString)
8791
.sign(Algorithm.HMAC256("secret"));
8892

8993
assertThat(signed, is(notNullValue()));
9094
}
9195

96+
@Test
97+
public void shouldSupportJsonValueHeaderWithNestedDataStructure() {
98+
String stringClaim = "someClaim";
99+
Integer intClaim = 1;
100+
List<String> nestedListClaims = Arrays.asList("1", "2");
101+
String claimsJson = "{\"stringClaim\": \"someClaim\", \"intClaim\": 1, \"nestedClaim\": { \"listClaim\": [ \"1\", \"2\" ]}}";
102+
103+
String jwt = JWTCreator.init()
104+
.withHeader(claimsJson)
105+
.sign(Algorithm.HMAC256("secret"));
106+
107+
assertThat(jwt, is(notNullValue()));
108+
String[] parts = jwt.split("\\.");
109+
String headerJson = new String(Base64.getUrlDecoder().decode(parts[0]), StandardCharsets.UTF_8);
110+
111+
assertThat(headerJson, JsonMatcher.hasEntry("stringClaim", stringClaim));
112+
assertThat(headerJson, JsonMatcher.hasEntry("intClaim", intClaim));
113+
assertThat(headerJson, JsonMatcher.hasEntry("listClaim", nestedListClaims));
114+
}
115+
116+
@Test
117+
public void shouldFailWithIllegalArgumentExceptionForInvalidJsonForHeaderClaims() {
118+
String invalidJson = "{ invalidJson }";
119+
120+
exception.expect(IllegalArgumentException.class);
121+
exception.expectMessage("Invalid header JSON");
122+
123+
JWTCreator.init()
124+
.withHeader(invalidJson)
125+
.sign(Algorithm.HMAC256("secret"));
126+
}
127+
92128
@Test
93129
public void shouldOverwriteExistingHeaderIfHeaderMapContainsTheSameKey() {
94130
Map<String, Object> header = new HashMap<>();
@@ -105,6 +141,7 @@ public void shouldOverwriteExistingHeaderIfHeaderMapContainsTheSameKey() {
105141
assertThat(headerJson, JsonMatcher.hasEntry(HeaderParams.KEY_ID, "xyz"));
106142
}
107143

144+
108145
@Test
109146
public void shouldOverwriteExistingHeadersWhenSettingSameHeaderKey() {
110147
Map<String, Object> header = new HashMap<>();
@@ -715,8 +752,11 @@ public void withPayloadShouldAddBasicClaim() {
715752

716753
@Test
717754
public void withPayloadShouldCreateJwtWithEmptyBodyIfPayloadNull() {
755+
Map<String, Object> nullMap = null;
756+
String nullString = null;
718757
String jwt = JWTCreator.init()
719-
.withPayload(null)
758+
.withPayload(nullMap)
759+
.withPayload(nullString)
720760
.sign(Algorithm.HMAC256("secret"));
721761

722762
assertThat(jwt, is(notNullValue()));
@@ -921,10 +961,42 @@ public void withPayloadShouldSupportNullValuesEverywhere() {
921961
assertThat(headerJson, JsonMatcher.hasEntry("objClaim", objClaim));
922962
}
923963

964+
@Test
965+
public void withPayloadShouldSupportJsonValueWithNestedDataStructure() {
966+
String stringClaim = "someClaim";
967+
Integer intClaim = 1;
968+
List<String> nestedListClaims = Arrays.asList("1", "2");
969+
String claimsJson = "{\"stringClaim\": \"someClaim\", \"intClaim\": 1, \"nestedClaim\": { \"listClaim\": [ \"1\", \"2\" ]}}";
970+
971+
String jwt = JWTCreator.init()
972+
.withPayload(claimsJson)
973+
.sign(Algorithm.HMAC256("secret"));
974+
975+
assertThat(jwt, is(notNullValue()));
976+
String[] parts = jwt.split("\\.");
977+
String payloadJson = new String(Base64.getUrlDecoder().decode(parts[1]), StandardCharsets.UTF_8);
978+
979+
assertThat(payloadJson, JsonMatcher.hasEntry("stringClaim", stringClaim));
980+
assertThat(payloadJson, JsonMatcher.hasEntry("intClaim", intClaim));
981+
assertThat(payloadJson, JsonMatcher.hasEntry("listClaim", nestedListClaims));
982+
}
983+
984+
@Test
985+
public void shouldFailWithIllegalArgumentExceptionForInvalidJsonForPayloadClaims() {
986+
String invalidJson = "{ invalidJson }";
987+
988+
exception.expect(IllegalArgumentException.class);
989+
exception.expectMessage("Invalid payload JSON");
990+
991+
JWTCreator.init()
992+
.withPayload(invalidJson)
993+
.sign(Algorithm.HMAC256("secret"));
994+
}
995+
924996
@Test
925997
public void shouldCreatePayloadWithNullForMap() {
926998
String jwt = JWTCreator.init()
927-
.withClaim("name", (Map<String,?>) null)
999+
.withClaim("name", (Map<String, ?>) null)
9281000
.sign(Algorithm.HMAC256("secret"));
9291001
assertThat(jwt, is(notNullValue()));
9301002
assertTrue(JWT.decode(jwt).getClaim("name").isNull());

0 commit comments

Comments
 (0)