diff --git a/lib/src/main/java/com/auth0/jwt/impl/PayloadDeserializer.java b/lib/src/main/java/com/auth0/jwt/impl/PayloadDeserializer.java index b1d32a12..69df87a1 100644 --- a/lib/src/main/java/com/auth0/jwt/impl/PayloadDeserializer.java +++ b/lib/src/main/java/com/auth0/jwt/impl/PayloadDeserializer.java @@ -74,10 +74,20 @@ Instant getInstantFromSeconds(Map tree, String claimName) { if (node == null || node.isNull()) { return null; } - if (!node.canConvertToLong()) { + // A NumericDate per RFC 7519 is a JSON number and may be written in scientific notation + // (for example 1.7e9). Split the two failure modes so the thrown error is accurate: a + // genuinely non-numeric value, versus a numeric value that is out of the range a long can + // hold (such as 1.733162101e+26). Previously both produced "non-numeric date value", which + // is wrong for the second case and made large/scientific values look unsupported. + if (!node.isNumber()) { throw new JWTDecodeException( String.format("The claim '%s' contained a non-numeric date value.", claimName)); } + if (!node.canConvertToLong()) { + throw new JWTDecodeException(String.format( + "The claim '%s' value (%s) is out of the range representable as a NumericDate.", + claimName, node.asText())); + } return Instant.ofEpochSecond(node.asLong()); } diff --git a/lib/src/test/java/com/auth0/jwt/impl/PayloadDeserializerTest.java b/lib/src/test/java/com/auth0/jwt/impl/PayloadDeserializerTest.java index c3e04013..f734b572 100644 --- a/lib/src/test/java/com/auth0/jwt/impl/PayloadDeserializerTest.java +++ b/lib/src/test/java/com/auth0/jwt/impl/PayloadDeserializerTest.java @@ -241,6 +241,34 @@ public void shouldGetLargeInstantWhenParsingNumericNode() { assertThat(instant.toEpochMilli(), is(2147493647L * 1000)); } + // https://github.com/auth0/java-jwt/issues/706 - a NumericDate may be written in scientific + // notation. An in-range value must still resolve to the right instant. + @Test + public void shouldGetInstantWhenParsingScientificNotationNode() { + Map tree = new HashMap<>(); + DoubleNode node = new DoubleNode(1.7e9); + tree.put("key", node); + + Instant instant = deserializer.getInstantFromSeconds(tree, "key"); + assertThat(instant, is(notNullValue())); + assertThat(instant, is(Instant.ofEpochSecond(1_700_000_000L))); + } + + // A numeric value that does not fit a long (the exact value reported in issue #706) must be + // rejected as out of range, not mislabeled as non-numeric. + @Test + public void shouldThrowOutOfRangeWhenNumericDateExceedsLong() { + exception.expect(JWTDecodeException.class); + exception.expectMessage( + "The claim 'key' value (1.733162101E26) is out of the range representable as a NumericDate."); + + Map tree = new HashMap<>(); + DoubleNode node = new DoubleNode(1.733162101e+26); + tree.put("key", node); + + deserializer.getInstantFromSeconds(tree, "key"); + } + @Test public void shouldGetNullStringWhenParsingNullNode() { Map tree = new HashMap<>();