Skip to content

Commit

Permalink
jwtk#235 Update JWT for default Instant usage
Browse files Browse the repository at this point in the history
The JWT handling has been updated to default to Java's Instant class. This change removed support for various date and time classes such as ZonedDateTime, OffsetDateTime, Date, and Calendar in favor of an Instant-based approach throughout the codebase. The appropriate tests and documentation were updated to reflect this change.
  • Loading branch information
Pieter Van Eeckhout committed Dec 26, 2023
1 parent 9bdbb85 commit 0921e29
Show file tree
Hide file tree
Showing 8 changed files with 23 additions and 132 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2992,7 +2992,7 @@ Jwts.parser()
<a name="json-jackson-custom-types"></a>
#### Parsing of Custom Claim Types

By default JJWT will only convert simple claim types: String, Date, Long, Integer, Short and Byte. If you need to
By default JJWT will only convert simple claim types: String, Instant, Long, Integer, Short and Byte. If you need to
deserialize other types you can configure the `JacksonDeserializer` by passing a `Map` of claim names to types in
through a constructor. For example:

Expand Down
2 changes: 1 addition & 1 deletion api/src/main/java/io/jsonwebtoken/Claims.java
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ public interface Claims extends Map<String, Object>, Identifiable {
* Returns the JWTs claim ({@code claimName}) value as a {@code requiredType} instance, or {@code null} if not
* present.
*
* <p>JJWT only converts simple String, Date, OffsetDateTime, ZonedDateTime, Long, Integer, Short and Byte types automatically. Anything more
* <p>JJWT only converts simple String, Instant, Long, Integer, Short and Byte types automatically. Anything more
* complex is expected to be already converted to your desired type by the JSON parser. You may specify a custom
* JSON processor using the {@code JwtParserBuilder}'s
* {@link JwtParserBuilder#json(io.jsonwebtoken.io.Deserializer) json(Deserializer)} method. See the JJWT
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,7 @@
import java.math.BigDecimal;
import java.math.BigInteger;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZonedDateTime;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.Map;

/**
Expand Down Expand Up @@ -96,26 +92,10 @@ private Object toJSONInstance(Object object) throws IOException {
return object;
}

if(object instanceof Instant) {
if (object instanceof Instant) {
return DateFormats.formatIso8601((Instant) object);
}

if(object instanceof OffsetDateTime) {
return DateFormats.formatIso8601(((OffsetDateTime) object).toInstant());
}

if(object instanceof ZonedDateTime) {
return DateFormats.formatIso8601(((ZonedDateTime) object).toInstant());
}

if (object instanceof Calendar) {
return DateFormats.formatIso8601(((Calendar) object).getTime().toInstant());
}

if (object instanceof Date) {
return DateFormats.formatIso8601(((Date) object).toInstant());
}

if (object instanceof byte[]) {
return Encoders.BASE64.encode((byte[]) object);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ import org.junit.Before
import org.junit.Test

import java.time.Instant
import java.time.OffsetDateTime
import java.time.ZonedDateTime

import static org.junit.Assert.*

Expand Down Expand Up @@ -201,22 +199,6 @@ class OrgJsonSerializerTest {
assertEquals "\"$formatted\"" as String, ser(instant)
}

@Test
void testOffsetDateTime() {
OffsetDateTime offsetDateTime = OffsetDateTime.now()
def now = offsetDateTime.toInstant()
String formatted = DateFormats.formatIso8601(now)
assertEquals "\"$formatted\"" as String, ser(offsetDateTime)
}

@Test
void testZonedDateTime() {
ZonedDateTime zonedDateTime = ZonedDateTime.now()
def now = zonedDateTime.toInstant()
String formatted = DateFormats.formatIso8601(now)
assertEquals "\"$formatted\"" as String, ser(zonedDateTime)
}

@Test
void testDate() {
Date date = new Date()
Expand All @@ -225,14 +207,6 @@ class OrgJsonSerializerTest {
assertEquals "\"$formatted\"" as String, ser(date)
}

@Test
void testCalendar() {
def cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"))
def now = cal.toInstant()
String formatted = DateFormats.formatIso8601(now)
assertEquals "\"$formatted\"" as String, ser(cal)
}

@Test
void testSimpleIntArray() {
assertEquals '[1,2]', ser([1, 2] as int[])
Expand Down
2 changes: 1 addition & 1 deletion impl/src/main/java/io/jsonwebtoken/impl/DefaultClaims.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
public class DefaultClaims extends ParameterMap implements Claims {

private static final String CONVERSION_ERROR_MSG = "Cannot convert existing claim value of type '%s' to desired type " +
"'%s'. JJWT only converts simple String, Date, Instant, OffsetDateTime, ZonedDateTime, Long, Integer, Short " +
"'%s'. JJWT only converts simple String, Instant, Long, Integer, Short " +
"and Byte types automatically. Anything more complex is expected to be already converted to your desired type " +
"by the JSON Deserializer implementation. You may specify a custom Deserializer for a JwtParser with the " +
"desired conversion configuration via the JwtParserBuilder.deserializer() method. " +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,7 @@
import io.jsonwebtoken.lang.DateFormats;

import java.time.Instant;
import java.time.ZonedDateTime;
import java.time.OffsetDateTime;
import java.time.format.DateTimeParseException;
import java.util.Date;
import java.util.Calendar;

public class JwtDateConverter implements Converter<Instant, Object> {

Expand Down Expand Up @@ -51,7 +47,7 @@ public static Instant toSpecInstant(Object value) {
long seconds = ((Number) value).longValue();
value = Instant.ofEpochSecond(seconds);
}
// would have been normalized to Instant if it was a number value, so perform normal date conversion:
// would have been normalized to Instant if it was a number value, so perform normal instant conversion:
return toInstant(value);
}

Expand All @@ -66,20 +62,9 @@ public static Instant toInstant(Object v) {
return null;
} else if (v instanceof Instant) {
return (Instant) v;
} else if (v instanceof ZonedDateTime) {
return ((ZonedDateTime) v).toInstant();
}else if (v instanceof OffsetDateTime) {
return ((OffsetDateTime) v).toInstant();
} else if (v instanceof Date) {
//assume UTC
return ((Date) v).toInstant();
} else if (v instanceof Calendar) { //since 0.10.0
//assume UTC
return ((Calendar) v).getTime().toInstant();
} else if (v instanceof Number) {
//assume millis:
long millis = ((Number) v).longValue();
//assume UTC
return Instant.ofEpochMilli(millis);
} else if (v instanceof String) {
return parseIso8601Date((String) v); //ISO-8601 parsing since 0.10.0
Expand Down
9 changes: 0 additions & 9 deletions impl/src/test/groovy/io/jsonwebtoken/JwtsTest.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -53,19 +53,10 @@ class JwtsTest {
return new Date(secondOnlyPrecisionMillis)
}

private static Instant instantWithOnlySecondPrecision(long millis) {
return instantWithOnlySecondPrecision(Instant.ofEpochMilli(millis))
}

private static Instant instantWithOnlySecondPrecision(Instant instant) {
return instant.truncatedTo(ChronoUnit.SECONDS)
}

private static Date now() {
Date date = dateWithOnlySecondPrecision(System.currentTimeMillis())
return date
}

private static int later() {
def date = laterDate(10000)
def seconds = date.getTime() / 1000
Expand Down
75 changes: 18 additions & 57 deletions impl/src/test/groovy/io/jsonwebtoken/impl/DefaultClaimsTest.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,7 @@ import org.junit.Before
import org.junit.Test

import java.time.Instant
import java.time.OffsetDateTime
import java.time.ZonedDateTime
import java.time.temporal.ChronoUnit
import java.time.temporal.TemporalField
import java.time.temporal.TemporalUnit

import static org.junit.Assert.*

Expand Down Expand Up @@ -192,39 +188,6 @@ class DefaultClaimsTest {
assertEquals expected, result
}

@Test
void testGetRequiredDateFromOffsetDateTime() {
def expected = OffsetDateTime.now()
claims.put("anOffsetDateTime", expected)
OffsetDateTime result = claims.get("anOffsetDateTime", OffsetDateTime.class)
assertEquals expected, result
}

@Test
void testGetRequiredDateFromZonedDateTime() {
def expected = ZonedDateTime.now()
claims.put("aZonedDateTime", expected)
ZonedDateTime result = claims.get("aZonedDateTime", ZonedDateTime.class)
assertEquals expected, result
}

@Test
void testGetRequiredDateFromDate() {
def expected = new Date()
claims.put("aDate", expected)
Date result = claims.get("aDate", Date.class)
assertEquals expected, result
}

@Test
void testGetRequiredDateFromCalendar() {
def c = Calendar.getInstance(TimeZone.getTimeZone("UTC"))
def expected = c.toInstant()
claims.put("aCalender", c)
Instant result = claims.get('aCalender', Instant.class)
assertEquals expected, result
}

@Test
void testGetRequiredDateFromLong() {
def expected = Instant.now()
Expand Down Expand Up @@ -342,13 +305,12 @@ class DefaultClaimsTest {
}

@Test
void testGetSpecDateWithCalendar() {
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"))
Instant instant = cal.toInstant()
void testGetSpecDateWithInstant() {
Instant instant = Instant.now()
long seconds = instant.getEpochSecond()
claims.put(Claims.EXPIRATION, cal)
claims.put(Claims.ISSUED_AT, cal)
claims.put(Claims.NOT_BEFORE, cal)
claims.put(Claims.EXPIRATION, instant)
claims.put(Claims.ISSUED_AT, instant)
claims.put(Claims.NOT_BEFORE, instant)
assertEquals instant, claims.getExpiration()
assertEquals instant, claims.getIssuedAt()
assertEquals instant, claims.getNotBefore()
Expand All @@ -358,11 +320,10 @@ class DefaultClaimsTest {
}

@Test
void testToSpecDateWithDate() {
long millis = System.currentTimeMillis()
Date d = new Date(millis)
claims.put('exp', d)
assertEquals d.toInstant(), claims.getExpiration()
void testToSpecDateWithInstant() {
Instant i = Instant.now()
claims.put('exp', i)
assertEquals i, claims.getExpiration()
}

void trySpecDateNonDate(Parameter<?> param) {
Expand Down Expand Up @@ -411,7 +372,7 @@ class DefaultClaimsTest {
void testPutWithIat() {
long millis = System.currentTimeMillis()
long seconds = millis / 1000 as long
Date now = new Date(millis)
Instant now = Instant.ofEpochMilli(millis)
claims.put('iat', now) //this should convert 'now' to seconds since epoch
assertEquals seconds, claims.get('iat') //conversion should have happened
}
Expand All @@ -420,7 +381,7 @@ class DefaultClaimsTest {
void testPutAllWithIat() {
long millis = System.currentTimeMillis()
long seconds = millis / 1000 as long
Date now = new Date(millis)
Instant now = Instant.ofEpochMilli(millis)
claims.putAll([iat: now]) //this should convert 'now' to seconds since epoch
assertEquals seconds, claims.get('iat') //conversion should have happened
}
Expand All @@ -429,7 +390,7 @@ class DefaultClaimsTest {
void testConstructorWithIat() {
long millis = System.currentTimeMillis()
long seconds = millis / 1000 as long
Date now = new Date(millis)
Instant now = Instant.ofEpochMilli(millis)
this.claims = new DefaultClaims([iat: now]) //this should convert 'now' to seconds since epoch
assertEquals seconds, claims.get('iat') //conversion should have happened
}
Expand All @@ -438,7 +399,7 @@ class DefaultClaimsTest {
void testPutWithNbf() {
long millis = System.currentTimeMillis()
long seconds = millis / 1000 as long
Date now = new Date(millis)
Instant now = Instant.ofEpochMilli(millis)
claims.put('nbf', now) //this should convert 'now' to seconds since epoch
assertEquals seconds, claims.get('nbf') //conversion should have happened
}
Expand All @@ -447,7 +408,7 @@ class DefaultClaimsTest {
void testPutAllWithNbf() {
long millis = System.currentTimeMillis()
long seconds = millis / 1000 as long
Date now = new Date(millis)
Instant now = Instant.ofEpochMilli(millis)
claims.putAll([nbf: now]) //this should convert 'now' to seconds since epoch
assertEquals seconds, claims.get('nbf') //conversion should have happened
}
Expand All @@ -456,7 +417,7 @@ class DefaultClaimsTest {
void testConstructorWithNbf() {
long millis = System.currentTimeMillis()
long seconds = millis / 1000 as long
Date now = new Date(millis)
Instant now = Instant.ofEpochMilli(millis)
this.claims = new DefaultClaims([nbf: now]) //this should convert 'now' to seconds since epoch
assertEquals seconds, claims.get('nbf') //conversion should have happened
}
Expand All @@ -465,7 +426,7 @@ class DefaultClaimsTest {
void testPutWithExp() {
long millis = System.currentTimeMillis()
long seconds = millis / 1000 as long
Date now = new Date(millis)
Instant now = Instant.ofEpochMilli(millis)
claims.put('exp', now) //this should convert 'now' to seconds since epoch
assertEquals seconds, claims.get('exp') //conversion should have happened
}
Expand All @@ -474,7 +435,7 @@ class DefaultClaimsTest {
void testPutAllWithExp() {
long millis = System.currentTimeMillis()
long seconds = millis / 1000 as long
Date now = new Date(millis)
Instant now = Instant.ofEpochMilli(millis)
claims.putAll([exp: now]) //this should convert 'now' to seconds since epoch
assertEquals seconds, claims.get('exp') //conversion should have happened
}
Expand All @@ -483,7 +444,7 @@ class DefaultClaimsTest {
void testConstructorWithExp() {
long millis = System.currentTimeMillis()
long seconds = millis / 1000 as long
Date now = new Date(millis)
Instant now = Instant.ofEpochMilli(millis)
this.claims = new DefaultClaims([exp: now]) //this should convert 'now' to seconds since epoch
assertEquals seconds, claims.get('exp') //conversion should have happened
}
Expand Down

0 comments on commit 0921e29

Please sign in to comment.