Skip to content

Commit 9193d5d

Browse files
authored
Merge branch 'master' into missing-long
2 parents c60687c + f5cf048 commit 9193d5d

File tree

11 files changed

+272
-28
lines changed

11 files changed

+272
-28
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,7 @@ The Claim class is a wrapper for the Claim values. It allows you to get the Clai
338338
To obtain a Claim as a Collection you'll need to provide the **Class Type** of the contents to convert from.
339339

340340
* **as(class)**: Returns the value parsed as **Class Type**. For collections you should use the `asArray` and `asList` methods.
341+
* **asMap()**: Returns the value parsed as **Map<String, Object>**.
341342
* **asArray(class)**: Returns the value parsed as an Array of elements of type **Class Type**, or null if the value isn't a JSON Array.
342343
* **asList(class)**: Returns the value parsed as a List of elements of type **Class Type**, or null if the value isn't a JSON Array.
343344

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import com.auth0.jwt.exceptions.JWTDecodeException;
44
import com.auth0.jwt.impl.JWTParser;
55
import com.auth0.jwt.interfaces.Claim;
6-
import com.auth0.jwt.interfaces.DecodedJWT;
76
import com.auth0.jwt.interfaces.Header;
87
import com.auth0.jwt.interfaces.Payload;
98
import org.apache.commons.codec.binary.Base64;

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

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import com.auth0.jwt.exceptions.InvalidClaimException;
66
import com.auth0.jwt.exceptions.JWTVerificationException;
77
import com.auth0.jwt.exceptions.SignatureVerificationException;
8+
import com.auth0.jwt.exceptions.TokenExpiredException;
89
import com.auth0.jwt.impl.PublicClaims;
910
import com.auth0.jwt.interfaces.Claim;
1011
import com.auth0.jwt.interfaces.Clock;
@@ -434,23 +435,31 @@ private void assertValidStringClaim(String claimName, String value, String expec
434435
}
435436

436437
private void assertValidDateClaim(Date date, long leeway, boolean shouldBeFuture) {
437-
Date today = clock.getToday();
438-
today.setTime((long) Math.floor((today.getTime() / 1000) * 1000)); //truncate millis
439-
boolean isValid;
440-
String errMessage;
441-
if (shouldBeFuture) {
442-
today.setTime(today.getTime() - leeway * 1000);
443-
isValid = date == null || !today.after(date);
444-
errMessage = String.format("The Token has expired on %s.", date);
445-
} else {
446-
today.setTime(today.getTime() + leeway * 1000);
447-
isValid = date == null || !today.before(date);
448-
errMessage = String.format("The Token can't be used before %s.", date);
449-
}
450-
if (!isValid) {
451-
throw new InvalidClaimException(errMessage);
452-
}
453-
}
438+
Date today = clock.getToday();
439+
today.setTime((long) Math.floor((today.getTime() / 1000) * 1000)); // truncate
440+
// millis
441+
if (shouldBeFuture) {
442+
assertDateIsFuture(date, leeway, today);
443+
} else {
444+
assertDateIsPast(date, leeway, today);
445+
}
446+
}
447+
448+
private void assertDateIsFuture(Date date, long leeway, Date today) {
449+
450+
today.setTime(today.getTime() - leeway * 1000);
451+
if (date != null && today.after(date)) {
452+
throw new TokenExpiredException(String.format("The Token has expired on %s.", date));
453+
}
454+
}
455+
456+
private void assertDateIsPast(Date date, long leeway, Date today) {
457+
today.setTime(today.getTime() + leeway * 1000);
458+
if(date!=null && today.before(date)) {
459+
throw new InvalidClaimException(String.format("The Token can't be used before %s.", date));
460+
}
461+
462+
}
454463

455464
private void assertValidAudienceClaim(List<String> audience, List<String> value) {
456465
if (audience == null || !audience.containsAll(value)) {
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.auth0.jwt.exceptions;
2+
3+
public class TokenExpiredException extends JWTVerificationException {
4+
5+
private static final long serialVersionUID = -7076928975713577708L;
6+
7+
public TokenExpiredException(String message) {
8+
super(message);
9+
}
10+
}

lib/src/main/java/com/auth0/jwt/impl/JsonNodeClaim.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.auth0.jwt.exceptions.JWTDecodeException;
44
import com.auth0.jwt.interfaces.Claim;
55
import com.fasterxml.jackson.core.JsonProcessingException;
6+
import com.fasterxml.jackson.core.type.TypeReference;
67
import com.fasterxml.jackson.databind.JsonNode;
78
import com.fasterxml.jackson.databind.ObjectMapper;
89

@@ -93,6 +94,22 @@ public <T> List<T> asList(Class<T> tClazz) throws JWTDecodeException {
9394
return list;
9495
}
9596

97+
@Override
98+
public Map<String, Object> asMap() throws JWTDecodeException {
99+
if (!data.isObject()) {
100+
return null;
101+
}
102+
103+
ObjectMapper mapper = new ObjectMapper();
104+
try {
105+
TypeReference<Map<String, Object>> mapType = new TypeReference<Map<String, Object>>() {
106+
};
107+
return mapper.treeAsTokens(data).readValueAs(mapType);
108+
} catch (IOException e) {
109+
throw new JWTDecodeException("Couldn't map the Claim value to Map", e);
110+
}
111+
}
112+
96113
@Override
97114
public <T> T as(Class<T> tClazz) throws JWTDecodeException {
98115
ObjectMapper mapper = new ObjectMapper();
@@ -105,7 +122,7 @@ public <T> T as(Class<T> tClazz) throws JWTDecodeException {
105122

106123
@Override
107124
public boolean isNull() {
108-
return !(data.isArray() || data.canConvertToLong() || data.isTextual() || data.isNumber() || data.isBoolean());
125+
return false;
109126
}
110127

111128
/**

lib/src/main/java/com/auth0/jwt/impl/NullClaim.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import java.util.Date;
77
import java.util.List;
8+
import java.util.Map;
89

910
/**
1011
* The {@link NullClaim} class is a Claim implementation that returns null when any of it's methods it's called.
@@ -55,6 +56,11 @@ public <T> List<T> asList(Class<T> tClazz) throws JWTDecodeException {
5556
return null;
5657
}
5758

59+
@Override
60+
public Map<String, Object> asMap() throws JWTDecodeException {
61+
return null;
62+
}
63+
5864
@Override
5965
public <T> T as(Class<T> tClazz) throws JWTDecodeException {
6066
return null;

lib/src/main/java/com/auth0/jwt/interfaces/Claim.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,18 @@
44

55
import java.util.Date;
66
import java.util.List;
7+
import java.util.Map;
78

89
/**
910
* The Claim class holds the value in a generic way so that it can be recovered in many representations.
1011
*/
1112
public interface Claim {
1213

14+
/**
15+
* Whether this Claim has a null value or not.
16+
*
17+
* @return whether this Claim has a null value or not.
18+
*/
1319
boolean isNull();
1420

1521
/**
@@ -78,6 +84,14 @@ public interface Claim {
7884
*/
7985
<T> List<T> asList(Class<T> tClazz) throws JWTDecodeException;
8086

87+
/**
88+
* Get this Claim as a generic Map of values.
89+
*
90+
* @return the value as instance of Map.
91+
* @throws JWTDecodeException if the value can't be converted to a Map.
92+
*/
93+
Map<String, Object> asMap() throws JWTDecodeException;
94+
8195
/**
8296
* Get this Claim as a custom type T.
8397
*

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

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import org.apache.commons.codec.binary.Base64;
88
import org.hamcrest.collection.IsCollectionWithSize;
99
import org.hamcrest.core.IsCollectionContaining;
10+
import org.junit.Assert;
1011
import org.junit.Rule;
1112
import org.junit.Test;
1213
import org.junit.rules.ExpectedException;
@@ -192,11 +193,71 @@ public void shouldGetValidClaim() throws Exception {
192193
}
193194

194195
@Test
195-
public void shouldGetNullClaimIfClaimValueIsNull() throws Exception {
196+
public void shouldNotGetNullClaimIfClaimIsEmptyObject() throws Exception {
196197
DecodedJWT jwt = JWTDecoder.decode("eyJhbGciOiJIUzI1NiJ9.eyJvYmplY3QiOnt9fQ.d3nUeeL_69QsrHL0ZWij612LHEQxD8EZg1rNoY3a4aI");
197198
assertThat(jwt, is(notNullValue()));
198199
assertThat(jwt.getClaim("object"), is(notNullValue()));
199-
assertThat(jwt.getClaim("object").isNull(), is(true));
200+
assertThat(jwt.getClaim("object").isNull(), is(false));
201+
}
202+
203+
@Test
204+
public void shouldGetCustomClaimOfTypeInteger() throws Exception {
205+
String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoxMjN9.XZAudnA7h3_Al5kJydzLjw6RzZC3Q6OvnLEYlhNW7HA";
206+
DecodedJWT jwt = JWT.decode(token);
207+
Assert.assertThat(jwt, is(notNullValue()));
208+
Assert.assertThat(jwt.getClaim("name").asInt(), is(123));
209+
}
210+
211+
@Test
212+
public void shouldGetCustomClaimOfTypeDouble() throws Exception {
213+
String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoyMy40NX0.7pyX2OmEGaU9q15T8bGFqRm-d3RVTYnqmZNZtxMKSlA";
214+
DecodedJWT jwt = JWT.decode(token);
215+
Assert.assertThat(jwt, is(notNullValue()));
216+
Assert.assertThat(jwt.getClaim("name").asDouble(), is(23.45));
217+
}
218+
219+
@Test
220+
public void shouldGetCustomClaimOfTypeBoolean() throws Exception {
221+
String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjp0cnVlfQ.FwQ8VfsZNRqBa9PXMinSIQplfLU4-rkCLfIlTLg_MV0";
222+
DecodedJWT jwt = JWT.decode(token);
223+
Assert.assertThat(jwt, is(notNullValue()));
224+
Assert.assertThat(jwt.getClaim("name").asBoolean(), is(true));
225+
}
226+
227+
@Test
228+
public void shouldGetCustomClaimOfTypeDate() throws Exception {
229+
String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoxNDc4ODkxNTIxfQ.mhioumeok8fghQEhTKF3QtQAksSvZ_9wIhJmgZLhJ6c";
230+
Date date = new Date(1478891521000L);
231+
DecodedJWT jwt = JWT.decode(token);
232+
Assert.assertThat(jwt, is(notNullValue()));
233+
Assert.assertThat(jwt.getClaim("name").asDate().getTime(), is(date.getTime()));
234+
}
235+
236+
@Test
237+
public void shouldGetCustomArrayClaimOfTypeString() throws Exception {
238+
String token = "eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjpbInRleHQiLCIxMjMiLCJ0cnVlIl19.lxM8EcmK1uSZRAPd0HUhXGZJdauRmZmLjoeqz4J9yAA";
239+
DecodedJWT jwt = JWT.decode(token);
240+
Assert.assertThat(jwt, is(notNullValue()));
241+
Assert.assertThat(jwt.getClaim("name").asArray(String.class), arrayContaining("text", "123", "true"));
242+
}
243+
244+
@Test
245+
public void shouldGetCustomArrayClaimOfTypeInteger() throws Exception {
246+
String token = "eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjpbMSwyLDNdfQ.UEuMKRQYrzKAiPpPLhIVawWkKWA1zj0_GderrWUIyFE";
247+
DecodedJWT jwt = JWT.decode(token);
248+
Assert.assertThat(jwt, is(notNullValue()));
249+
Assert.assertThat(jwt.getClaim("name").asArray(Integer.class), arrayContaining(1, 2, 3));
250+
}
251+
252+
@Test
253+
public void shouldGetCustomMapClaim() throws Exception {
254+
String token = "eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjp7InN0cmluZyI6InZhbHVlIiwibnVtYmVyIjoxLCJib29sZWFuIjp0cnVlfX0.-8aIaXd2-rp1lLuDEQmCeisCBX9X_zbqdPn2llGxNoc";
255+
DecodedJWT jwt = JWT.decode(token);
256+
Assert.assertThat(jwt, is(notNullValue()));
257+
Map<String, Object> map = jwt.getClaim("name").asMap();
258+
Assert.assertThat(map, hasEntry("string", (Object) "value"));
259+
Assert.assertThat(map, hasEntry("number", (Object) 1));
260+
Assert.assertThat(map, hasEntry("boolean", (Object) true));
200261
}
201262

202263
@Test

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.auth0.jwt.algorithms.Algorithm;
44
import com.auth0.jwt.exceptions.AlgorithmMismatchException;
55
import com.auth0.jwt.exceptions.InvalidClaimException;
6+
import com.auth0.jwt.exceptions.TokenExpiredException;
67
import com.auth0.jwt.interfaces.Clock;
78
import com.auth0.jwt.interfaces.DecodedJWT;
89
import org.junit.Rule;
@@ -403,7 +404,7 @@ public void shouldValidateExpiresAtIfPresent() throws Exception {
403404

404405
@Test
405406
public void shouldThrowOnInvalidExpiresAtIfPresent() throws Exception {
406-
exception.expect(InvalidClaimException.class);
407+
exception.expect(TokenExpiredException.class);
407408
exception.expectMessage(startsWith("The Token has expired on"));
408409
Clock clock = mock(Clock.class);
409410
when(clock.getToday()).thenReturn(new Date(DATE_TOKEN_MS_VALUE + 1000));

0 commit comments

Comments
 (0)