Skip to content

Commit

Permalink
Add support for passing json values for header and payload (#643)
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewrigas committed Mar 27, 2023
1 parent b610b66 commit a18955b
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 4 deletions.
2 changes: 1 addition & 1 deletion lib/build.gradle
Expand Up @@ -72,7 +72,7 @@ javadoc {
}

dependencies {
implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.4.2'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.14.0'

testImplementation 'org.bouncycastle:bcprov-jdk15on:1.70'
testImplementation 'junit:junit:4.13.2'
Expand Down
48 changes: 48 additions & 0 deletions lib/src/main/java/com/auth0/jwt/JWTCreator.java
Expand Up @@ -98,6 +98,27 @@ public Builder withHeader(Map<String, Object> headerClaims) {
return this;
}

/**
* Add specific Claims to set as the Header.
* If provided json is null then nothing is changed
*
* @param headerClaimsJson the values to use as Claims in the token's Header.
* @return this same Builder instance.
* @throws IllegalArgumentException if json value has invalid structure
*/
public Builder withHeader(String headerClaimsJson) throws IllegalArgumentException {
if (headerClaimsJson == null) {
return this;
}

try {
Map<String, Object> headerClaims = mapper.readValue(headerClaimsJson, HashMap.class);
return withHeader(headerClaims);
} catch (JsonProcessingException e) {
throw new IllegalArgumentException("Invalid header JSON", e);
}
}

/**
* Add a specific Key Id ("kid") claim to the Header.
* If the {@link Algorithm} used to sign this token was instantiated with a KeyProvider,
Expand Down Expand Up @@ -467,6 +488,33 @@ public Builder withPayload(Map<String, ?> payloadClaims) throws IllegalArgumentE
return this;
}

/**
* Add specific Claims to set as the Payload. If the provided json is null then
* nothing is changed.
*
* <p>
* If any of the claims are invalid, none will be added.
* </p>
*
* @param payloadClaimsJson the values to use as Claims in the token's payload.
* @return this same Builder instance.
* @throws IllegalArgumentException if any of the claim keys or null,
* or if the values are not of a supported type,
* or if json value has invalid structure.
*/
public Builder withPayload(String payloadClaimsJson) throws IllegalArgumentException {
if (payloadClaimsJson == null) {
return this;
}

try {
Map<String, Object> payloadClaims = mapper.readValue(payloadClaimsJson, HashMap.class);
return withPayload(payloadClaims);
} catch (JsonProcessingException e) {
throw new IllegalArgumentException("Invalid payload JSON", e);
}
}

private boolean validatePayload(Map<String, ?> payload) {
for (Map.Entry<String, ?> entry : payload.entrySet()) {
String key = entry.getKey();
Expand Down
78 changes: 75 additions & 3 deletions lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java
Expand Up @@ -3,6 +3,7 @@
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 org.junit.Rule;
import org.junit.Test;
Expand Down Expand Up @@ -82,13 +83,48 @@ public void shouldAddHeaderClaim() {

@Test
public void shouldReturnBuilderIfNullMapIsProvided() {
Map<String, Object> nullMap = null;
String nullString = null;
String signed = JWTCreator.init()
.withHeader(null)
.withHeader(nullMap)
.withHeader(nullString)
.sign(Algorithm.HMAC256("secret"));

assertThat(signed, is(notNullValue()));
}

@Test
public void shouldSupportJsonValueHeaderWithNestedDataStructure() {
String stringClaim = "someClaim";
Integer intClaim = 1;
List<String> nestedListClaims = Arrays.asList("1", "2");
String claimsJson = "{\"stringClaim\": \"someClaim\", \"intClaim\": 1, \"nestedClaim\": { \"listClaim\": [ \"1\", \"2\" ]}}";

String jwt = JWTCreator.init()
.withHeader(claimsJson)
.sign(Algorithm.HMAC256("secret"));

assertThat(jwt, is(notNullValue()));
String[] parts = jwt.split("\\.");
String headerJson = new String(Base64.getUrlDecoder().decode(parts[0]), StandardCharsets.UTF_8);

assertThat(headerJson, JsonMatcher.hasEntry("stringClaim", stringClaim));
assertThat(headerJson, JsonMatcher.hasEntry("intClaim", intClaim));
assertThat(headerJson, JsonMatcher.hasEntry("listClaim", nestedListClaims));
}

@Test
public void shouldFailWithIllegalArgumentExceptionForInvalidJsonForHeaderClaims() {
String invalidJson = "{ invalidJson }";

exception.expect(IllegalArgumentException.class);
exception.expectMessage("Invalid header JSON");

JWTCreator.init()
.withHeader(invalidJson)
.sign(Algorithm.HMAC256("secret"));
}

@Test
public void shouldOverwriteExistingHeaderIfHeaderMapContainsTheSameKey() {
Map<String, Object> header = new HashMap<>();
Expand All @@ -105,6 +141,7 @@ public void shouldOverwriteExistingHeaderIfHeaderMapContainsTheSameKey() {
assertThat(headerJson, JsonMatcher.hasEntry(HeaderParams.KEY_ID, "xyz"));
}


@Test
public void shouldOverwriteExistingHeadersWhenSettingSameHeaderKey() {
Map<String, Object> header = new HashMap<>();
Expand Down Expand Up @@ -715,8 +752,11 @@ public void withPayloadShouldAddBasicClaim() {

@Test
public void withPayloadShouldCreateJwtWithEmptyBodyIfPayloadNull() {
Map<String, Object> nullMap = null;
String nullString = null;
String jwt = JWTCreator.init()
.withPayload(null)
.withPayload(nullMap)
.withPayload(nullString)
.sign(Algorithm.HMAC256("secret"));

assertThat(jwt, is(notNullValue()));
Expand Down Expand Up @@ -921,10 +961,42 @@ public void withPayloadShouldSupportNullValuesEverywhere() {
assertThat(headerJson, JsonMatcher.hasEntry("objClaim", objClaim));
}

@Test
public void withPayloadShouldSupportJsonValueWithNestedDataStructure() {
String stringClaim = "someClaim";
Integer intClaim = 1;
List<String> nestedListClaims = Arrays.asList("1", "2");
String claimsJson = "{\"stringClaim\": \"someClaim\", \"intClaim\": 1, \"nestedClaim\": { \"listClaim\": [ \"1\", \"2\" ]}}";

String jwt = JWTCreator.init()
.withPayload(claimsJson)
.sign(Algorithm.HMAC256("secret"));

assertThat(jwt, is(notNullValue()));
String[] parts = jwt.split("\\.");
String payloadJson = new String(Base64.getUrlDecoder().decode(parts[1]), StandardCharsets.UTF_8);

assertThat(payloadJson, JsonMatcher.hasEntry("stringClaim", stringClaim));
assertThat(payloadJson, JsonMatcher.hasEntry("intClaim", intClaim));
assertThat(payloadJson, JsonMatcher.hasEntry("listClaim", nestedListClaims));
}

@Test
public void shouldFailWithIllegalArgumentExceptionForInvalidJsonForPayloadClaims() {
String invalidJson = "{ invalidJson }";

exception.expect(IllegalArgumentException.class);
exception.expectMessage("Invalid payload JSON");

JWTCreator.init()
.withPayload(invalidJson)
.sign(Algorithm.HMAC256("secret"));
}

@Test
public void shouldCreatePayloadWithNullForMap() {
String jwt = JWTCreator.init()
.withClaim("name", (Map<String,?>) null)
.withClaim("name", (Map<String, ?>) null)
.sign(Algorithm.HMAC256("secret"));
assertThat(jwt, is(notNullValue()));
assertTrue(JWT.decode(jwt).getClaim("name").isNull());
Expand Down

0 comments on commit a18955b

Please sign in to comment.