From e02bd44547104d5fa0064f38d143f4175d465366 Mon Sep 17 00:00:00 2001 From: Jim Anderson Date: Wed, 8 Jan 2020 13:51:47 -0600 Subject: [PATCH 01/14] Use JDK 8 --- lib/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/build.gradle b/lib/build.gradle index 4cb57f61..df1fd86f 100644 --- a/lib/build.gradle +++ b/lib/build.gradle @@ -29,8 +29,8 @@ oss { } compileJava { - sourceCompatibility '1.7' - targetCompatibility '1.7' + sourceCompatibility '1.8' + targetCompatibility '1.8' } dependencies { From af98a1c8417fb314f0c68121f468444a4be13a2a Mon Sep 17 00:00:00 2001 From: Jim Anderson Date: Wed, 12 Feb 2020 15:12:56 -0600 Subject: [PATCH 02/14] Update tests to use valid Base64 URL-encoded tokens --- .../jwt/algorithms/ECDSAAlgorithmTest.java | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/lib/src/test/java/com/auth0/jwt/algorithms/ECDSAAlgorithmTest.java b/lib/src/test/java/com/auth0/jwt/algorithms/ECDSAAlgorithmTest.java index 8968ed9f..b50832d1 100644 --- a/lib/src/test/java/com/auth0/jwt/algorithms/ECDSAAlgorithmTest.java +++ b/lib/src/test/java/com/auth0/jwt/algorithms/ECDSAAlgorithmTest.java @@ -21,7 +21,6 @@ import java.security.interfaces.ECPublicKey; import java.util.Arrays; - import static com.auth0.jwt.PemUtils.readPrivateKeyFromFile; import static com.auth0.jwt.PemUtils.readPublicKeyFromFile; import static com.auth0.jwt.algorithms.CryptoTestHelper.asJWT; @@ -75,7 +74,7 @@ public void shouldThrowOnECDSA256VerificationWithDERSignature() throws Exception exception.expectCause(isA(SignatureException.class)); exception.expectCause(hasMessage(is("Invalid JOSE signature format."))); - String jwt = "eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJhdXRoMCJ9.MEYCIQDiJWTf5jS/hFPj/0hpCWn7x1n/h+xPMjKWCs9MMusS9AIhAMcFPJVLe2A9uvb8hl8sRO2IpGoKDRpDmyH14ixNPAHW"; + String jwt = "eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJhdXRoMCJ9.MEYCIQDiJWTf5jShFPj0hpCWn7x1nhxPMjKWCs9MMusS9AIhAMcFPJVLe2A9uvb8hl8sRO2IpGoKDRpDmyH14ixNPAHW"; ECKey key = (ECKey) readPublicKeyFromFile(PUBLIC_KEY_FILE_256, "EC"); Algorithm algorithm = Algorithm.ECDSA256(key); algorithm.verify(JWT.decode(jwt)); @@ -95,7 +94,7 @@ public void shouldThrowOnECDSA256VerificationWithDERSignatureWithBothKeys() thro exception.expectCause(isA(SignatureException.class)); exception.expectCause(hasMessage(is("Invalid JOSE signature format."))); - String jwt = "eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJhdXRoMCJ9.MEYCIQDiJWTf5jS/hFPj/0hpCWn7x1n/h+xPMjKWCs9MMusS9AIhAMcFPJVLe2A9uvb8hl8sRO2IpGoKDRpDmyH14ixNPAHW"; + String jwt = "eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJhdXRoMCJ9.MEYCIQDiJWTf5jShFPj0hpCWn7x1nhxPMjKWCs9MMusS9AIhAMcFPJVLe2A9uvb8hl8sRO2IpGoKDRpDmyH14ixNPAHW"; Algorithm algorithm = Algorithm.ECDSA256((ECPublicKey) readPublicKeyFromFile(PUBLIC_KEY_FILE_256, "EC"), (ECPrivateKey) readPrivateKeyFromFile(PRIVATE_KEY_FILE_256, "EC")); algorithm.verify(JWT.decode(jwt)); } @@ -200,7 +199,7 @@ public void shouldThrowOnECDSA384VerificationWithDERSignature() throws Exception exception.expectCause(isA(SignatureException.class)); exception.expectCause(hasMessage(is("Invalid JOSE signature format."))); - String jwt = "eyJhbGciOiJFUzM4NCJ9.eyJpc3MiOiJhdXRoMCJ9.MGUCMQDnRRTlUo10XXB/KRjyNAEqm+4dmh7ohkEmbk2+gHxtH6GdGDq2L4Idua+hG2Ut+ccCMH8CE2v/HCTMuk3pzAtoOtxkB8rXPK2KF6m8LUuEdCqPwF2yxVJn8ZxpzAur+DEv8w=="; + String jwt = "eyJhbGciOiJFUzM4NCJ9.eyJpc3MiOiJhdXRoMCJ9.MGUCMQDnRRTlUo10XXBKRjyNAEqm4dmh7ohkEmbk2gHxtH6GdGDq2L4IduahG2UtccCMH8CE2vHCTMuk3pzAtoOtxkB8rXPK2KF6m8LUuEdCqPwF2yxVJn8ZxpzAurDEv8w"; ECKey key = (ECKey) readPublicKeyFromFile(PUBLIC_KEY_FILE_384, "EC"); Algorithm algorithm = Algorithm.ECDSA384(key); algorithm.verify(JWT.decode(jwt)); @@ -220,7 +219,7 @@ public void shouldThrowOnECDSA384VerificationWithDERSignatureWithBothKeys() thro exception.expectCause(isA(SignatureException.class)); exception.expectCause(hasMessage(is("Invalid JOSE signature format."))); - String jwt = "eyJhbGciOiJFUzM4NCJ9.eyJpc3MiOiJhdXRoMCJ9.MGUCMQDnRRTlUo10XXB/KRjyNAEqm+4dmh7ohkEmbk2+gHxtH6GdGDq2L4Idua+hG2Ut+ccCMH8CE2v/HCTMuk3pzAtoOtxkB8rXPK2KF6m8LUuEdCqPwF2yxVJn8ZxpzAur+DEv8w=="; + String jwt = "eyJhbGciOiJFUzM4NCJ9.eyJpc3MiOiJhdXRoMCJ9.MGUCMQDnRRTlUo10XXBKRjyNAEqm4dmh7ohkEmbk2gHxtH6GdGDq2L4IduahG2UccCMH8CE2vHCTMuk3pzAtoOtxkB8rXPK2KF6m8LUuEdCqPwF2yxVJn8ZxpzAurDEv8w"; Algorithm algorithm = Algorithm.ECDSA384((ECPublicKey) readPublicKeyFromFile(PUBLIC_KEY_FILE_384, "EC"), (ECPrivateKey) readPrivateKeyFromFile(PRIVATE_KEY_FILE_384, "EC")); algorithm.verify(JWT.decode(jwt)); } @@ -325,7 +324,7 @@ public void shouldThrowOnECDSA512VerificationWithDERSignature() throws Exception exception.expectCause(isA(SignatureException.class)); exception.expectCause(hasMessage(is("Invalid JOSE signature format."))); - String jwt = "eyJhbGciOiJFUzUxMiJ9.eyJpc3MiOiJhdXRoMCJ9.MIGIAkIB4Ik8MixIeHBFIZkJjquymLzN6Q7DQr2pgw2uJ0/UW726GsDVCsb4RTFeUTTrK+aHZHtHPRoTuTEHCuerwvxo4EICQgGALKocz3lL8qfH1444LNBLaOSNJp3RNkB5YHDEhQEsox21PMA9kau2TcxkOW9jGX6b9N9FhlGo0/mmWFhVCR1YNg=="; + String jwt = "eyJhbGciOiJFUzUxMiJ9.eyJpc3MiOiJhdXRoMCJ9.MIGIAkIB4Ik8MixIeHBFIZkJjquymLzN6Q7DQr2pgw2uJ0UW726GsDVCsb4RTFeUTTrKaHZHtHPRoTuTEHCuerwvxo4EICQgGALKocz3lL8qfH1444LNBLaOSNJp3RNkB5YHDEhQEsox21PMA9kau2TcxkOW9jGX6b9N9FhlGo0mmWFhVCR1YNg"; ECKey key = (ECKey) readPublicKeyFromFile(PUBLIC_KEY_FILE_512, "EC"); Algorithm algorithm = Algorithm.ECDSA512(key); algorithm.verify(JWT.decode(jwt)); @@ -345,7 +344,7 @@ public void shouldThrowECDSA512VerificationWithDERSignatureWithBothKeys() throws exception.expectCause(isA(SignatureException.class)); exception.expectCause(hasMessage(is("Invalid JOSE signature format."))); - String jwt = "eyJhbGciOiJFUzUxMiJ9.eyJpc3MiOiJhdXRoMCJ9.MIGIAkIB4Ik8MixIeHBFIZkJjquymLzN6Q7DQr2pgw2uJ0/UW726GsDVCsb4RTFeUTTrK+aHZHtHPRoTuTEHCuerwvxo4EICQgGALKocz3lL8qfH1444LNBLaOSNJp3RNkB5YHDEhQEsox21PMA9kau2TcxkOW9jGX6b9N9FhlGo0/mmWFhVCR1YNg=="; + String jwt = "eyJhbGciOiJFUzUxMiJ9.eyJpc3MiOiJhdXRoMCJ9.MIGIAkIB4Ik8MixIeHBFIZkJjquymLzN6Q7DQr2pgw2uJ0UW726GsDVCsb4RTFeUTTrKaHZHtHPRoTuTEHCuerwvxo4EICQgGALKocz3lL8qfH1444LNBLaOSNJp3RNkB5YHDEhQEsox21PMA9kau2TcxkOW9jGX6b9N9FhlGo0mmWFhVCR1YNg"; Algorithm algorithm = Algorithm.ECDSA512((ECPublicKey) readPublicKeyFromFile(PUBLIC_KEY_FILE_512, "EC"), (ECPrivateKey) readPrivateKeyFromFile(PRIVATE_KEY_FILE_512, "EC")); algorithm.verify(JWT.decode(jwt)); } @@ -519,7 +518,7 @@ public void shouldThrowOnVerifyWhenTheSignatureIsNotPrepared() throws Exception private static final byte[] ES512HeaderBytes = ES512Header.getBytes(StandardCharsets.UTF_8); private static final byte[] auth0IssPayloadBytes = auth0IssPayload.getBytes(StandardCharsets.UTF_8); - + @Test public void shouldDoECDSA256Signing() throws Exception { Algorithm algorithm = Algorithm.ECDSA256((ECKey) readPrivateKeyFromFile(PRIVATE_KEY_FILE_256, "EC")); @@ -549,7 +548,7 @@ public void shouldDoECDSA256SigningWithProvidedPrivateKey() throws Exception { when(provider.getPrivateKey()).thenReturn((ECPrivateKey) privateKey); when(provider.getPublicKeyById(null)).thenReturn((ECPublicKey) publicKey); Algorithm algorithm = Algorithm.ECDSA256(provider); - + String jwt = asJWT(algorithm, ES256Header, auth0IssPayload); assertSignaturePresent(jwt); @@ -607,7 +606,7 @@ public void shouldDoECDSA384SigningWithProvidedPrivateKey() throws Exception { when(provider.getPrivateKey()).thenReturn((ECPrivateKey) privateKey); when(provider.getPublicKeyById(null)).thenReturn((ECPublicKey) publicKey); Algorithm algorithm = Algorithm.ECDSA384(provider); - + String jwt = asJWT(algorithm, ES384Header, auth0IssPayload); assertSignaturePresent(jwt); @@ -642,7 +641,7 @@ public void shouldFailOnECDSA384SigningWhenUsingPublicKey() throws Exception { public void shouldDoECDSA512Signing() throws Exception { Algorithm algorithmSign = Algorithm.ECDSA512((ECKey) readPrivateKeyFromFile(PRIVATE_KEY_FILE_512, "EC")); Algorithm algorithmVerify = Algorithm.ECDSA512((ECKey) readPublicKeyFromFile(PUBLIC_KEY_FILE_512, "EC")); - + String jwt = asJWT(algorithmSign, ES512Header, auth0IssPayload); assertSignaturePresent(jwt); @@ -652,7 +651,7 @@ public void shouldDoECDSA512Signing() throws Exception { @Test public void shouldDoECDSA512SigningWithBothKeys() throws Exception { Algorithm algorithm = Algorithm.ECDSA512((ECPublicKey) readPublicKeyFromFile(PUBLIC_KEY_FILE_512, "EC"), (ECPrivateKey) readPrivateKeyFromFile(PRIVATE_KEY_FILE_512, "EC")); - + String jwt = asJWT(algorithm, ES512Header, auth0IssPayload); assertSignaturePresent(jwt); @@ -668,7 +667,7 @@ public void shouldDoECDSA512SigningWithProvidedPrivateKey() throws Exception { when(provider.getPrivateKey()).thenReturn((ECPrivateKey) privateKey); when(provider.getPublicKeyById(null)).thenReturn((ECPublicKey) publicKey); Algorithm algorithm = Algorithm.ECDSA512(provider); - + String jwt = asJWT(algorithm, ES512Header, auth0IssPayload); assertSignaturePresent(jwt); @@ -843,7 +842,7 @@ public void shouldSignAndVerifyWithECDSA256() throws Exception { public void shouldSignAndVerifyWithECDSA384() throws Exception { ECDSAAlgorithm algorithm384 = (ECDSAAlgorithm) Algorithm.ECDSA384((ECPublicKey) readPublicKeyFromFile(PUBLIC_KEY_FILE_384, "EC"), (ECPrivateKey) readPrivateKeyFromFile(PRIVATE_KEY_FILE_384, "EC")); String header384 = "eyJhbGciOiJFUzM4NCJ9"; - String body = "eyJpc3MiOiJhdXRoMCJ9"; + String body = "eyJpc3MiOiJhdXRoMCJ9"; for (int i = 0; i < 10; i++) { String jwt = asJWT(algorithm384, header384, body); @@ -855,7 +854,7 @@ public void shouldSignAndVerifyWithECDSA384() throws Exception { public void shouldSignAndVerifyWithECDSA512() throws Exception { ECDSAAlgorithm algorithm512 = (ECDSAAlgorithm) Algorithm.ECDSA512((ECPublicKey) readPublicKeyFromFile(PUBLIC_KEY_FILE_512, "EC"), (ECPrivateKey) readPrivateKeyFromFile(PRIVATE_KEY_FILE_512, "EC")); String header512 = "eyJhbGciOiJFUzUxMiJ9"; - String body = "eyJpc3MiOiJhdXRoMCJ9"; + String body = "eyJpc3MiOiJhdXRoMCJ9"; for (int i = 0; i < 10; i++) { String jwt = asJWT(algorithm512, header512, body); @@ -1171,7 +1170,7 @@ public void shouldBeEqualSignatureMethodDecodeResults() throws Exception { /** * Test deprecated signing method error handling. - * + * * @see {@linkplain #shouldFailOnECDSA256SigningWhenProvidedPrivateKeyIsNull} * @throws Exception expected exception */ From 6e995bbaa075736bdf5c59e16e6aa9d1d00c4b56 Mon Sep 17 00:00:00 2001 From: Jim Anderson Date: Wed, 12 Feb 2020 15:39:00 -0600 Subject: [PATCH 03/14] Update to Gradle 6.1.1 --- gradle/wrapper/gradle-wrapper.jar | Bin 56177 -> 55616 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 18 +++++++++++++++++- gradlew.bat | 18 +++++++++++++++++- lib/build.gradle | 2 +- 5 files changed, 36 insertions(+), 4 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 29953ea141f55e3b8fc691d31b5ca8816d89fa87..5c2d1cf016b3885f6930543d57b744ea8c220a1a 100644 GIT binary patch delta 47360 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 delta 47987 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 P{tQ5B1&<&s7}); Date: Mon, 20 Aug 2018 17:01:58 +0200 Subject: [PATCH 04/14] Support map / list claims --- .../main/java/com/auth0/jwt/JWTCreator.java | 90 +++++++++++++++++++ .../java/com/auth0/jwt/JWTCreatorTest.java | 29 +++++- 2 files changed, 118 insertions(+), 1 deletion(-) diff --git a/lib/src/main/java/com/auth0/jwt/JWTCreator.java b/lib/src/main/java/com/auth0/jwt/JWTCreator.java index deda9813..c33fcbe3 100644 --- a/lib/src/main/java/com/auth0/jwt/JWTCreator.java +++ b/lib/src/main/java/com/auth0/jwt/JWTCreator.java @@ -15,7 +15,9 @@ import java.nio.charset.StandardCharsets; import java.util.Date; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.Map.Entry; /** * The JWTCreator class holds the sign method to generate a complete JWT (with Signature) from a given Header and Payload content. @@ -303,6 +305,94 @@ public Builder withArrayClaim(String name, Long[] items) throws IllegalArgumentE return this; } + /** + * Add a custom Map Claim with the given items.

+ * + * Accepted nested types are {@linkplain Map} and {@linkplain List} with basic types + * Boolean, Integer, Long, Double, String and Date. + * + * @param name the Claim's name. + * @param items the Claim's key-values. + * @return this same Builder instance. + * @throws IllegalArgumentException if the name is null. + */ + public Builder withClaim(String name, Map map) throws IllegalArgumentException { + assertNonNull(name); + if(!validateClaim(map)) { + throw new IllegalArgumentException("Expected map containing Map, List, Boolean, Integer, Long, Double, String and Date"); + } + addClaim(name, map); + return this; + } + + /** + * Add a custom List Claim with the given items. + * + * Accepted nested types are {@linkplain Map} and {@linkplain List} with basic types + * Boolean, Integer, Long, Double, String and Date. + * + * @param name the Claim's name. + * @param items the Claim's list of values. + * @return this same Builder instance. + * @throws IllegalArgumentException if the name is null. + */ + + public Builder withClaim(String name, List map) throws IllegalArgumentException { + assertNonNull(name); + // validate map contents + if(!validateClaim(map)) { + throw new IllegalArgumentException("Expected list containing Map, List, Boolean, Integer, Long, Double, String and Date"); + } + addClaim(name, map); + return this; + } + + private static boolean validateClaim(Map map) { + for (Entry entry : map.entrySet()) { + Object value = entry.getValue(); + if(value != null && !isSupported(value)) { + return false; + } + + if(entry.getKey() == null || !(entry.getKey() instanceof String)) { + return false; + } + } + return true; + } + + private static boolean validateClaim(List list) { + for (Object object : list) { + if(object != null && !isSupported(object)) { + return false; + } + } + return true; + } + + @SuppressWarnings("unchecked") + private static boolean isSupported(Object value) { + if(value != null) { + if(value instanceof List) { + return validateClaim((List)value); + } else if(value instanceof Map) { + return validateClaim((Map)value); + } else { + return isBasicType(value); + } + } + return true; + } + + private static boolean isBasicType(Object value) { + Class c = value.getClass(); + + if(c.isArray()) { + return c == Integer[].class || c == Long[].class || c == String[].class; + } + return c == String.class || c == Integer.class || c == Long.class || c == Double.class || c == Date.class || c == Boolean.class; + } + /** * Creates a new JWT and signs is with the given algorithm * diff --git a/lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java b/lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java index 471ff38b..629232f7 100644 --- a/lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java +++ b/lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java @@ -431,4 +431,31 @@ public void shouldAcceptCustomArrayClaimOfTypeLong() throws Exception { String[] parts = jwt.split("\\."); assertThat(parts[1], is("eyJuYW1lIjpbMSwyLDNdfQ")); } -} + + @Test + public void shouldAcceptCustomClaimOfTypeObject() throws Exception { + Map data = new HashMap<>(); + data.put("test1", "abc"); + data.put("test2", "def"); + String jwt = JWTCreator.init() + .withClaim("data", data) + .sign(Algorithm.HMAC256("secret")); + + assertThat(jwt, is(notNullValue())); + String[] parts = jwt.split("\\."); + assertThat(parts[1], is("eyJkYXRhIjp7InRlc3QyIjoiZGVmIiwidGVzdDEiOiJhYmMifX0")); + } + + @Test + public void shouldRefuseCustomClaimOfTypeUserPojo() throws Exception{ + Map data = new HashMap<>(); + data.put("test1", new UserPojo("Michael", 255)); + + exception.expect(IllegalArgumentException.class); + + JWTCreator.init() + .withClaim("pojo", data) + .sign(Algorithm.HMAC256("secret")); + } + +} \ No newline at end of file From 7aefb4f5f9de8d653a7e8f02756580be9039c35a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Skj=C3=B8lberg?= Date: Thu, 9 May 2019 12:19:56 +0200 Subject: [PATCH 05/14] Improve code coverage for JWTCreator --- .../main/java/com/auth0/jwt/JWTCreator.java | 19 +- .../java/com/auth0/jwt/JWTCreatorTest.java | 183 ++++++++++++++++++ 2 files changed, 192 insertions(+), 10 deletions(-) diff --git a/lib/src/main/java/com/auth0/jwt/JWTCreator.java b/lib/src/main/java/com/auth0/jwt/JWTCreator.java index c33fcbe3..624406b1 100644 --- a/lib/src/main/java/com/auth0/jwt/JWTCreator.java +++ b/lib/src/main/java/com/auth0/jwt/JWTCreator.java @@ -348,9 +348,10 @@ public Builder withClaim(String name, List map) throws IllegalArgumentEx } private static boolean validateClaim(Map map) { + // do not accept null values in maps for (Entry entry : map.entrySet()) { Object value = entry.getValue(); - if(value != null && !isSupported(value)) { + if(value == null || !isSupported(value)) { return false; } @@ -362,6 +363,7 @@ private static boolean validateClaim(Map map) { } private static boolean validateClaim(List list) { + // accept null values in list for (Object object : list) { if(object != null && !isSupported(object)) { return false; @@ -372,16 +374,13 @@ private static boolean validateClaim(List list) { @SuppressWarnings("unchecked") private static boolean isSupported(Object value) { - if(value != null) { - if(value instanceof List) { - return validateClaim((List)value); - } else if(value instanceof Map) { - return validateClaim((Map)value); - } else { - return isBasicType(value); - } + if(value instanceof List) { + return validateClaim((List)value); + } else if(value instanceof Map) { + return validateClaim((Map)value); + } else { + return isBasicType(value); } - return true; } private static boolean isBasicType(Object value) { diff --git a/lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java b/lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java index 629232f7..7871973f 100644 --- a/lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java +++ b/lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java @@ -4,6 +4,8 @@ import com.auth0.jwt.impl.PublicClaims; import com.auth0.jwt.interfaces.ECDSAKeyProvider; import com.auth0.jwt.interfaces.RSAKeyProvider; +import com.fasterxml.jackson.databind.ObjectMapper; + import org.apache.commons.codec.binary.Base64; import org.junit.Rule; import org.junit.Test; @@ -12,9 +14,12 @@ import java.nio.charset.StandardCharsets; import java.security.interfaces.ECPrivateKey; import java.security.interfaces.RSAPrivateKey; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.HashMap; +import java.util.List; import java.util.Map; import static org.hamcrest.Matchers.is; @@ -458,4 +463,182 @@ public void shouldRefuseCustomClaimOfTypeUserPojo() throws Exception{ .sign(Algorithm.HMAC256("secret")); } + + @SuppressWarnings("unchecked") + @Test + public void shouldAcceptCustomMapClaimOfBasicObjectTypes() throws Exception { + Map data = new HashMap<>(); + + // simple types + data.put("string", "abc"); + data.put("integer", 1); + data.put("long", Long.MAX_VALUE); + data.put("double", 123.456d); + data.put("date", new Date(123L)); + data.put("boolean", true); + + // array types + data.put("intArray", new Integer[]{3, 5}); + data.put("longArray", new Long[]{Long.MAX_VALUE, Long.MIN_VALUE}); + data.put("stringArray", new String[]{"string"}); + + data.put("list", Arrays.asList("a", "b", "c")); + + Map sub = new HashMap<>(); + sub.put("subKey", "subValue"); + + data.put("map", sub); + + String jwt = JWTCreator.init() + .withClaim("data", data) + .sign(Algorithm.HMAC256("secret")); + + assertThat(jwt, is(notNullValue())); + String[] parts = jwt.split("\\."); + + String body = new String(Base64.decodeBase64(parts[1]), StandardCharsets.UTF_8); + ObjectMapper mapper = new ObjectMapper(); + Map map = (Map) mapper.readValue(body, Map.class).get("data"); + + assertThat(map.get("string"), is("abc")); + assertThat(map.get("integer"), is(1)); + assertThat(map.get("long"), is(Long.MAX_VALUE)); + assertThat(map.get("double"), is(123.456d)); + assertThat(map.get("date"), is(123)); + assertThat(map.get("boolean"), is(true)); + + // array types + assertThat(map.get("intArray"), is(Arrays.asList(new Integer[]{3, 5}))); + assertThat(map.get("longArray"), is(Arrays.asList(new Long[]{Long.MAX_VALUE, Long.MIN_VALUE}))); + assertThat(map.get("stringArray"), is(Arrays.asList(new String[]{"string"}))); + + // list + assertThat(map.get("list"), is(Arrays.asList("a", "b", "c"))); + assertThat(map.get("map"), is(sub)); + + } + + @SuppressWarnings("unchecked") + @Test + public void shouldAcceptCustomListClaimOfBasicObjectTypes() throws Exception { + List data = new ArrayList<>(); + + // simple types + data.add("abc"); + data.add(1); + data.add(Long.MAX_VALUE); + data.add(123.456d); + data.add(new Date(123L)); + data.add(true); + + // array types + data.add(new Integer[]{3, 5}); + data.add(new Long[]{Long.MAX_VALUE, Long.MIN_VALUE}); + data.add(new String[]{"string"}); + + data.add(Arrays.asList("a", "b", "c")); + + Map sub = new HashMap<>(); + sub.put("subKey", "subValue"); + + data.add(sub); + + String jwt = JWTCreator.init() + .withClaim("data", data) + .sign(Algorithm.HMAC256("secret")); + + assertThat(jwt, is(notNullValue())); + String[] parts = jwt.split("\\."); + + String body = new String(Base64.decodeBase64(parts[1]), StandardCharsets.UTF_8); + ObjectMapper mapper = new ObjectMapper(); + List list = (List) mapper.readValue(body, Map.class).get("data"); + + assertThat(list.get(0), is("abc")); + assertThat(list.get(1), is(1)); + assertThat(list.get(2), is(Long.MAX_VALUE)); + assertThat(list.get(3), is(123.456d)); + assertThat(list.get(4), is(123)); + assertThat(list.get(5), is(true)); + + // array types + assertThat(list.get(6), is(Arrays.asList(new Integer[]{3, 5}))); + assertThat(list.get(7), is(Arrays.asList(new Long[]{Long.MAX_VALUE, Long.MIN_VALUE}))); + assertThat(list.get(8), is(Arrays.asList(new String[]{"string"}))); + + // list + assertThat(list.get(9), is(Arrays.asList("a", "b", "c"))); + assertThat(list.get(10), is(sub)); + + } + + @Test + public void shouldAcceptCustomClaimForNullListItem() throws Exception{ + Map data = new HashMap<>(); + data.put("test1", Arrays.asList("a", null, "c")); + + JWTCreator.init() + .withClaim("pojo", data) + .sign(Algorithm.HMAC256("secret")); + } + + @Test + public void shouldRefuseCustomClaimForNullMapValue() throws Exception{ + Map data = new HashMap<>(); + data.put("subKey", null); + + exception.expect(IllegalArgumentException.class); + + JWTCreator.init() + .withClaim("pojo", data) + .sign(Algorithm.HMAC256("secret")); + } + + @Test + public void shouldRefuseCustomClaimForNullMapKey() throws Exception{ + Map data = new HashMap<>(); + data.put(null, "subValue"); + + exception.expect(IllegalArgumentException.class); + + JWTCreator.init() + .withClaim("pojo", data) + .sign(Algorithm.HMAC256("secret")); + } + + @Test + public void shouldRefuseCustomMapClaimForNonStringKey() throws Exception{ + Map data = new HashMap<>(); + data.put(new Object(), "value"); + + exception.expect(IllegalArgumentException.class); + + JWTCreator.init() + .withClaim("pojo", (Map)data) + .sign(Algorithm.HMAC256("secret")); + } + + @Test + public void shouldRefuseCustomListClaimForUnknownListElement() throws Exception{ + List list = Arrays.asList(new UserPojo("Michael", 255)); + + exception.expect(IllegalArgumentException.class); + + JWTCreator.init() + .withClaim("list", list) + .sign(Algorithm.HMAC256("secret")); + } + + @Test + public void shouldRefuseCustomListClaimForUnknownArrayType() throws Exception{ + List list = new ArrayList<>(); + list.add(new Object[] {"test"}); + + exception.expect(IllegalArgumentException.class); + + JWTCreator.init() + .withClaim("list", list) + .sign(Algorithm.HMAC256("secret")); + } + } \ No newline at end of file From e8fc5fc7bd4d4799879ec2b77aa1054f7a48a234 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Skj=C3=B8lberg?= Date: Tue, 11 Feb 2020 21:42:47 +0100 Subject: [PATCH 06/14] Initial QA changes --- .../main/java/com/auth0/jwt/JWTCreator.java | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/lib/src/main/java/com/auth0/jwt/JWTCreator.java b/lib/src/main/java/com/auth0/jwt/JWTCreator.java index 624406b1..f4be98f7 100644 --- a/lib/src/main/java/com/auth0/jwt/JWTCreator.java +++ b/lib/src/main/java/com/auth0/jwt/JWTCreator.java @@ -297,7 +297,7 @@ public Builder withArrayClaim(String name, Integer[] items) throws IllegalArgume * @param name the Claim's name. * @param items the Claim's value. * @return this same Builder instance. - * @throws IllegalArgumentException if the name is null. + * @throws IllegalArgumentException if the name is null, or if the value is null or of an unsupported type */ public Builder withArrayClaim(String name, Long[] items) throws IllegalArgumentException { assertNonNull(name); @@ -306,18 +306,18 @@ public Builder withArrayClaim(String name, Long[] items) throws IllegalArgumentE } /** - * Add a custom Map Claim with the given items.

- * + * Add a custom Map Claim with the given items. * Accepted nested types are {@linkplain Map} and {@linkplain List} with basic types * Boolean, Integer, Long, Double, String and Date. * * @param name the Claim's name. - * @param items the Claim's key-values. + * @param map the Claim's key-values. * @return this same Builder instance. - * @throws IllegalArgumentException if the name is null. + * @throws IllegalArgumentException if the name is null, or if the value is null or of an unsupported type */ public Builder withClaim(String name, Map map) throws IllegalArgumentException { assertNonNull(name); + // validate map contents if(!validateClaim(map)) { throw new IllegalArgumentException("Expected map containing Map, List, Boolean, Integer, Long, Double, String and Date"); } @@ -332,18 +332,18 @@ public Builder withClaim(String name, Map map) throws IllegalArg * Boolean, Integer, Long, Double, String and Date. * * @param name the Claim's name. - * @param items the Claim's list of values. + * @param list the Claim's list of values. * @return this same Builder instance. - * @throws IllegalArgumentException if the name is null. + * @throws IllegalArgumentException if the name is null, or if the value is null or of an unsupported type */ - public Builder withClaim(String name, List map) throws IllegalArgumentException { + public Builder withClaim(String name, List list) throws IllegalArgumentException { assertNonNull(name); - // validate map contents - if(!validateClaim(map)) { + // validate list contents + if(!validateClaim(list)) { throw new IllegalArgumentException("Expected list containing Map, List, Boolean, Integer, Long, Double, String and Date"); } - addClaim(name, map); + addClaim(name, list); return this; } @@ -351,7 +351,7 @@ private static boolean validateClaim(Map map) { // do not accept null values in maps for (Entry entry : map.entrySet()) { Object value = entry.getValue(); - if(value == null || !isSupported(value)) { + if(value == null || !isSupportedType(value)) { return false; } @@ -365,7 +365,7 @@ private static boolean validateClaim(Map map) { private static boolean validateClaim(List list) { // accept null values in list for (Object object : list) { - if(object != null && !isSupported(object)) { + if(object != null && !isSupportedType(object)) { return false; } } @@ -373,11 +373,11 @@ private static boolean validateClaim(List list) { } @SuppressWarnings("unchecked") - private static boolean isSupported(Object value) { + private static boolean isSupportedType(Object value) { if(value instanceof List) { return validateClaim((List)value); } else if(value instanceof Map) { - return validateClaim((Map)value); + return validateClaim((Map)value); } else { return isBasicType(value); } From 8845a28a9a68b59e5f5c02a4bbfdf1479f830b03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Skj=C3=B8lberg?= Date: Thu, 13 Feb 2020 11:33:29 +0100 Subject: [PATCH 07/14] Improve Javadoc, use anon types where possible, add unit test for Map in List, formatting --- .../main/java/com/auth0/jwt/JWTCreator.java | 32 +++++---- .../java/com/auth0/jwt/JWTCreatorTest.java | 70 +++++++++++-------- 2 files changed, 60 insertions(+), 42 deletions(-) diff --git a/lib/src/main/java/com/auth0/jwt/JWTCreator.java b/lib/src/main/java/com/auth0/jwt/JWTCreator.java index f4be98f7..7b5e3e77 100644 --- a/lib/src/main/java/com/auth0/jwt/JWTCreator.java +++ b/lib/src/main/java/com/auth0/jwt/JWTCreator.java @@ -297,7 +297,7 @@ public Builder withArrayClaim(String name, Integer[] items) throws IllegalArgume * @param name the Claim's name. * @param items the Claim's value. * @return this same Builder instance. - * @throws IllegalArgumentException if the name is null, or if the value is null or of an unsupported type + * @throws IllegalArgumentException if the name is null */ public Builder withArrayClaim(String name, Long[] items) throws IllegalArgumentException { assertNonNull(name); @@ -307,15 +307,18 @@ public Builder withArrayClaim(String name, Long[] items) throws IllegalArgumentE /** * Add a custom Map Claim with the given items. - * Accepted nested types are {@linkplain Map} and {@linkplain List} with basic types - * Boolean, Integer, Long, Double, String and Date. * + * Accepted nested types are {@linkplain Map} and {@linkplain List} with basic types + * {@linkplain Boolean}, {@linkplain Integer}, {@linkplain Long}, {@linkplain Double}, + * {@linkplain String} and {@linkplain Date}. {@linkplain Map}s cannot contain null keys or values. + * {@linkplain List}s can contain null elements. + * * @param name the Claim's name. * @param map the Claim's key-values. * @return this same Builder instance. - * @throws IllegalArgumentException if the name is null, or if the value is null or of an unsupported type + * @throws IllegalArgumentException if the name is null, or if the map contents does not validate. */ - public Builder withClaim(String name, Map map) throws IllegalArgumentException { + public Builder withClaim(String name, Map map) throws IllegalArgumentException { assertNonNull(name); // validate map contents if(!validateClaim(map)) { @@ -328,16 +331,18 @@ public Builder withClaim(String name, Map map) throws IllegalArg /** * Add a custom List Claim with the given items. * - * Accepted nested types are {@linkplain Map} and {@linkplain List} with basic types - * Boolean, Integer, Long, Double, String and Date. - * + * Accepted nested types are {@linkplain Map} and {@linkplain List} with basic types + * {@linkplain Boolean}, {@linkplain Integer}, {@linkplain Long}, {@linkplain Double}, + * {@linkplain String} and {@linkplain Date}. {@linkplain Map}s cannot contain null keys or values. + * {@linkplain List}s can contain null elements. + * * @param name the Claim's name. * @param list the Claim's list of values. * @return this same Builder instance. - * @throws IllegalArgumentException if the name is null, or if the value is null or of an unsupported type + * @throws IllegalArgumentException if the name is null, or if the list contents does not validate. */ - public Builder withClaim(String name, List list) throws IllegalArgumentException { + public Builder withClaim(String name, List list) throws IllegalArgumentException { assertNonNull(name); // validate list contents if(!validateClaim(list)) { @@ -347,9 +352,9 @@ public Builder withClaim(String name, List list) throws IllegalArgumentE return this; } - private static boolean validateClaim(Map map) { + private static boolean validateClaim(Map map) { // do not accept null values in maps - for (Entry entry : map.entrySet()) { + for (Entry entry : map.entrySet()) { Object value = entry.getValue(); if(value == null || !isSupportedType(value)) { return false; @@ -372,12 +377,11 @@ private static boolean validateClaim(List list) { return true; } - @SuppressWarnings("unchecked") private static boolean isSupportedType(Object value) { if(value instanceof List) { return validateClaim((List)value); } else if(value instanceof Map) { - return validateClaim((Map)value); + return validateClaim((Map)value); } else { return isBasicType(value); } diff --git a/lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java b/lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java index 7871973f..7df869c1 100644 --- a/lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java +++ b/lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java @@ -436,9 +436,9 @@ public void shouldAcceptCustomArrayClaimOfTypeLong() throws Exception { String[] parts = jwt.split("\\."); assertThat(parts[1], is("eyJuYW1lIjpbMSwyLDNdfQ")); } - + @Test - public void shouldAcceptCustomClaimOfTypeObject() throws Exception { + public void shouldAcceptCustomClaimOfTypeMap() throws Exception { Map data = new HashMap<>(); data.put("test1", "abc"); data.put("test2", "def"); @@ -450,25 +450,24 @@ public void shouldAcceptCustomClaimOfTypeObject() throws Exception { String[] parts = jwt.split("\\."); assertThat(parts[1], is("eyJkYXRhIjp7InRlc3QyIjoiZGVmIiwidGVzdDEiOiJhYmMifX0")); } - + @Test public void shouldRefuseCustomClaimOfTypeUserPojo() throws Exception{ - Map data = new HashMap<>(); + Map data = new HashMap<>(); data.put("test1", new UserPojo("Michael", 255)); - + exception.expect(IllegalArgumentException.class); - JWTCreator.init() + JWTCreator.init() .withClaim("pojo", data) .sign(Algorithm.HMAC256("secret")); } - - + @SuppressWarnings("unchecked") - @Test + @Test public void shouldAcceptCustomMapClaimOfBasicObjectTypes() throws Exception { Map data = new HashMap<>(); - + // simple types data.put("string", "abc"); data.put("integer", 1); @@ -476,7 +475,7 @@ public void shouldAcceptCustomMapClaimOfBasicObjectTypes() throws Exception { data.put("double", 123.456d); data.put("date", new Date(123L)); data.put("boolean", true); - + // array types data.put("intArray", new Integer[]{3, 5}); data.put("longArray", new Long[]{Long.MAX_VALUE, Long.MIN_VALUE}); @@ -486,7 +485,7 @@ public void shouldAcceptCustomMapClaimOfBasicObjectTypes() throws Exception { Map sub = new HashMap<>(); sub.put("subKey", "subValue"); - + data.put("map", sub); String jwt = JWTCreator.init() @@ -506,7 +505,7 @@ public void shouldAcceptCustomMapClaimOfBasicObjectTypes() throws Exception { assertThat(map.get("double"), is(123.456d)); assertThat(map.get("date"), is(123)); assertThat(map.get("boolean"), is(true)); - + // array types assertThat(map.get("intArray"), is(Arrays.asList(new Integer[]{3, 5}))); assertThat(map.get("longArray"), is(Arrays.asList(new Long[]{Long.MAX_VALUE, Long.MIN_VALUE}))); @@ -517,12 +516,12 @@ public void shouldAcceptCustomMapClaimOfBasicObjectTypes() throws Exception { assertThat(map.get("map"), is(sub)); } - + @SuppressWarnings("unchecked") - @Test + @Test public void shouldAcceptCustomListClaimOfBasicObjectTypes() throws Exception { List data = new ArrayList<>(); - + // simple types data.add("abc"); data.add(1); @@ -540,7 +539,7 @@ public void shouldAcceptCustomListClaimOfBasicObjectTypes() throws Exception { Map sub = new HashMap<>(); sub.put("subKey", "subValue"); - + data.add(sub); String jwt = JWTCreator.init() @@ -571,7 +570,7 @@ public void shouldAcceptCustomListClaimOfBasicObjectTypes() throws Exception { assertThat(list.get(10), is(sub)); } - + @Test public void shouldAcceptCustomClaimForNullListItem() throws Exception{ Map data = new HashMap<>(); @@ -581,7 +580,7 @@ public void shouldAcceptCustomClaimForNullListItem() throws Exception{ .withClaim("pojo", data) .sign(Algorithm.HMAC256("secret")); } - + @Test public void shouldRefuseCustomClaimForNullMapValue() throws Exception{ Map data = new HashMap<>(); @@ -593,7 +592,7 @@ public void shouldRefuseCustomClaimForNullMapValue() throws Exception{ .withClaim("pojo", data) .sign(Algorithm.HMAC256("secret")); } - + @Test public void shouldRefuseCustomClaimForNullMapKey() throws Exception{ Map data = new HashMap<>(); @@ -604,8 +603,9 @@ public void shouldRefuseCustomClaimForNullMapKey() throws Exception{ JWTCreator.init() .withClaim("pojo", data) .sign(Algorithm.HMAC256("secret")); - } - + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) @Test public void shouldRefuseCustomMapClaimForNonStringKey() throws Exception{ Map data = new HashMap<>(); @@ -617,28 +617,42 @@ public void shouldRefuseCustomMapClaimForNonStringKey() throws Exception{ .withClaim("pojo", (Map)data) .sign(Algorithm.HMAC256("secret")); } - + @Test public void shouldRefuseCustomListClaimForUnknownListElement() throws Exception{ - List list = Arrays.asList(new UserPojo("Michael", 255)); + List list = Arrays.asList(new UserPojo("Michael", 255)); + + exception.expect(IllegalArgumentException.class); + + JWTCreator.init() + .withClaim("list", list) + .sign(Algorithm.HMAC256("secret")); + } + + @Test + public void shouldRefuseCustomListClaimForUnknownListElementWrappedInAMap() throws Exception{ + List list = Arrays.asList(new UserPojo("Michael", 255)); + Map data = new HashMap<>(); + data.put("someList", list); + exception.expect(IllegalArgumentException.class); JWTCreator.init() .withClaim("list", list) .sign(Algorithm.HMAC256("secret")); } - + @Test public void shouldRefuseCustomListClaimForUnknownArrayType() throws Exception{ - List list = new ArrayList<>(); + List list = new ArrayList<>(); list.add(new Object[] {"test"}); - + exception.expect(IllegalArgumentException.class); JWTCreator.init() .withClaim("list", list) .sign(Algorithm.HMAC256("secret")); } - + } \ No newline at end of file From acd0325d62ff93bfd20d02e6efef1eec3aefa5eb Mon Sep 17 00:00:00 2001 From: CodeDead Date: Wed, 4 Mar 2020 18:25:38 +0100 Subject: [PATCH 08/14] * Remove unneeded null check --- lib/src/main/java/com/auth0/jwt/JWTCreator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/main/java/com/auth0/jwt/JWTCreator.java b/lib/src/main/java/com/auth0/jwt/JWTCreator.java index 7b5e3e77..16ce739a 100644 --- a/lib/src/main/java/com/auth0/jwt/JWTCreator.java +++ b/lib/src/main/java/com/auth0/jwt/JWTCreator.java @@ -360,7 +360,7 @@ private static boolean validateClaim(Map map) { return false; } - if(entry.getKey() == null || !(entry.getKey() instanceof String)) { + if(!(entry.getKey() instanceof String)) { return false; } } From d95d3f7e0dd0cbdbcb862e36900be210830c95e6 Mon Sep 17 00:00:00 2001 From: CodeDead Date: Fri, 28 Feb 2020 02:56:53 +0100 Subject: [PATCH 09/14] * Added EOL character to some files (GIT-compliance) * Classes and interfaces adhere to default coding style * Removed unused import statements * Removed unnecessary throws statements * Removed unnecessary Java 7 features --- .../main/java/com/auth0/jwt/ClockImpl.java | 1 + lib/src/main/java/com/auth0/jwt/JWT.java | 1 - .../main/java/com/auth0/jwt/JWTCreator.java | 2 - .../main/java/com/auth0/jwt/JWTDecoder.java | 1 - .../main/java/com/auth0/jwt/JWTVerifier.java | 1 - .../main/java/com/auth0/jwt/TokenUtils.java | 1 - .../com/auth0/jwt/algorithms/Algorithm.java | 3 - .../auth0/jwt/algorithms/CryptoHelper.java | 11 -- .../auth0/jwt/algorithms/ECDSAAlgorithm.java | 11 +- .../auth0/jwt/algorithms/HMACAlgorithm.java | 1 - .../auth0/jwt/algorithms/RSAAlgorithm.java | 2 - .../AlgorithmMismatchException.java | 1 + .../jwt/exceptions/InvalidClaimException.java | 2 +- .../jwt/exceptions/JWTCreationException.java | 1 + .../jwt/exceptions/JWTDecodeException.java | 1 + .../exceptions/JWTVerificationException.java | 1 + .../SignatureGenerationException.java | 1 + .../SignatureVerificationException.java | 1 + .../jwt/exceptions/TokenExpiredException.java | 1 - .../auth0/jwt/impl/HeaderDeserializer.java | 1 - .../com/auth0/jwt/impl/JsonNodeClaim.java | 1 - .../auth0/jwt/impl/PayloadDeserializer.java | 1 - .../java/com/auth0/jwt/impl/PayloadImpl.java | 1 - .../com/auth0/jwt/impl/PayloadSerializer.java | 50 ++++---- .../java/com/auth0/jwt/impl/PublicClaims.java | 3 - .../java/com/auth0/jwt/interfaces/Claim.java | 1 - .../java/com/auth0/jwt/interfaces/Clock.java | 1 - .../jwt/interfaces/ECDSAKeyProvider.java | 1 + .../java/com/auth0/jwt/interfaces/Header.java | 1 - .../auth0/jwt/interfaces/JWTPartsParser.java | 1 - .../com/auth0/jwt/interfaces/JWTVerifier.java | 1 - .../com/auth0/jwt/interfaces/KeyProvider.java | 1 - .../com/auth0/jwt/interfaces/Payload.java | 1 - .../auth0/jwt/interfaces/RSAKeyProvider.java | 1 + .../java/com/auth0/jwt/ClockImplTest.java | 2 +- .../com/auth0/jwt/ConcurrentVerifyTest.java | 8 +- .../java/com/auth0/jwt/JWTCreatorTest.java | 93 +++++++------- .../java/com/auth0/jwt/JWTDecoderTest.java | 68 +++++------ lib/src/test/java/com/auth0/jwt/JWTTest.java | 52 ++++---- .../java/com/auth0/jwt/JWTVerifierTest.java | 101 ++++++++-------- .../test/java/com/auth0/jwt/JsonMatcher.java | 3 +- lib/src/test/java/com/auth0/jwt/PemUtils.java | 1 - .../java/com/auth0/jwt/TokenUtilsTest.java | 8 +- lib/src/test/java/com/auth0/jwt/UserPojo.java | 2 +- .../auth0/jwt/algorithms/AlgorithmTest.java | 114 +++++++++--------- .../jwt/algorithms/CryptoTestHelper.java | 1 - .../jwt/algorithms/ECDSAAlgorithmTest.java | 24 ++-- .../ECDSABouncyCastleProviderTests.java | 25 ++-- .../jwt/algorithms/HMACAlgorithmTest.java | 46 ++++--- .../jwt/algorithms/NoneAlgorithmTest.java | 11 +- .../jwt/algorithms/RSAAlgorithmTest.java | 19 ++- .../com/auth0/jwt/impl/BasicHeaderTest.java | 29 +++-- .../com/auth0/jwt/impl/ClaimsHolderTest.java | 6 +- .../jwt/impl/HeaderDeserializerTest.java | 11 +- .../com/auth0/jwt/impl/JWTParserTest.java | 19 ++- .../com/auth0/jwt/impl/JsonNodeClaimTest.java | 84 +++++++------ .../com/auth0/jwt/impl/NullClaimTest.java | 27 ++--- .../jwt/impl/PayloadDeserializerTest.java | 33 +++-- .../com/auth0/jwt/impl/PayloadImplTest.java | 43 ++++--- .../auth0/jwt/impl/PayloadSerializerTest.java | 6 +- 60 files changed, 440 insertions(+), 506 deletions(-) diff --git a/lib/src/main/java/com/auth0/jwt/ClockImpl.java b/lib/src/main/java/com/auth0/jwt/ClockImpl.java index 45e3edfc..274d4417 100644 --- a/lib/src/main/java/com/auth0/jwt/ClockImpl.java +++ b/lib/src/main/java/com/auth0/jwt/ClockImpl.java @@ -7,6 +7,7 @@ final class ClockImpl implements Clock { ClockImpl() { + } @Override diff --git a/lib/src/main/java/com/auth0/jwt/JWT.java b/lib/src/main/java/com/auth0/jwt/JWT.java index ca05deff..a68925e2 100644 --- a/lib/src/main/java/com/auth0/jwt/JWT.java +++ b/lib/src/main/java/com/auth0/jwt/JWT.java @@ -8,7 +8,6 @@ @SuppressWarnings("WeakerAccess") public class JWT { - private final JWTParser parser; /** diff --git a/lib/src/main/java/com/auth0/jwt/JWTCreator.java b/lib/src/main/java/com/auth0/jwt/JWTCreator.java index 16ce739a..81c649de 100644 --- a/lib/src/main/java/com/auth0/jwt/JWTCreator.java +++ b/lib/src/main/java/com/auth0/jwt/JWTCreator.java @@ -24,7 +24,6 @@ */ @SuppressWarnings("WeakerAccess") public final class JWTCreator { - private final Algorithm algorithm; private final String headerJson; private final String payloadJson; @@ -341,7 +340,6 @@ public Builder withClaim(String name, Map map) throws IllegalArgument * @return this same Builder instance. * @throws IllegalArgumentException if the name is null, or if the list contents does not validate. */ - public Builder withClaim(String name, List list) throws IllegalArgumentException { assertNonNull(name); // validate list contents diff --git a/lib/src/main/java/com/auth0/jwt/JWTDecoder.java b/lib/src/main/java/com/auth0/jwt/JWTDecoder.java index b14c2ac3..ffaf25ec 100644 --- a/lib/src/main/java/com/auth0/jwt/JWTDecoder.java +++ b/lib/src/main/java/com/auth0/jwt/JWTDecoder.java @@ -19,7 +19,6 @@ */ @SuppressWarnings("WeakerAccess") final class JWTDecoder implements DecodedJWT, Serializable { - private static final long serialVersionUID = 1873362438023312895L; private final String[] parts; diff --git a/lib/src/main/java/com/auth0/jwt/JWTVerifier.java b/lib/src/main/java/com/auth0/jwt/JWTVerifier.java index ff25db09..7b0449d5 100644 --- a/lib/src/main/java/com/auth0/jwt/JWTVerifier.java +++ b/lib/src/main/java/com/auth0/jwt/JWTVerifier.java @@ -223,7 +223,6 @@ private void requireClaim(String name, Object value) { } } - /** * Perform the verification against the given Token, using any previous configured options. * diff --git a/lib/src/main/java/com/auth0/jwt/TokenUtils.java b/lib/src/main/java/com/auth0/jwt/TokenUtils.java index cb6cff3e..8960b063 100644 --- a/lib/src/main/java/com/auth0/jwt/TokenUtils.java +++ b/lib/src/main/java/com/auth0/jwt/TokenUtils.java @@ -3,7 +3,6 @@ import com.auth0.jwt.exceptions.JWTDecodeException; abstract class TokenUtils { - /** * Splits the given token on the "." chars into a String array with 3 parts. * diff --git a/lib/src/main/java/com/auth0/jwt/algorithms/Algorithm.java b/lib/src/main/java/com/auth0/jwt/algorithms/Algorithm.java index 24ad025b..50d76796 100644 --- a/lib/src/main/java/com/auth0/jwt/algorithms/Algorithm.java +++ b/lib/src/main/java/com/auth0/jwt/algorithms/Algorithm.java @@ -6,7 +6,6 @@ import com.auth0.jwt.interfaces.ECDSAKeyProvider; import com.auth0.jwt.interfaces.RSAKeyProvider; -import java.io.ByteArrayOutputStream; import java.security.interfaces.*; /** @@ -14,7 +13,6 @@ */ @SuppressWarnings("WeakerAccess") public abstract class Algorithm { - private final String name; private final String description; @@ -392,5 +390,4 @@ public byte[] sign(byte[] headerBytes, byte[] payloadBytes) throws SignatureGene @Deprecated public abstract byte[] sign(byte[] contentBytes) throws SignatureGenerationException; - } diff --git a/lib/src/main/java/com/auth0/jwt/algorithms/CryptoHelper.java b/lib/src/main/java/com/auth0/jwt/algorithms/CryptoHelper.java index dc92ff97..090d2cff 100644 --- a/lib/src/main/java/com/auth0/jwt/algorithms/CryptoHelper.java +++ b/lib/src/main/java/com/auth0/jwt/algorithms/CryptoHelper.java @@ -7,7 +7,6 @@ import java.security.*; class CryptoHelper { - private static final byte JWT_PART_SEPARATOR = (byte)46; /** @@ -22,7 +21,6 @@ class CryptoHelper { * @throws NoSuchAlgorithmException if the algorithm is not supported. * @throws InvalidKeyException if the given key is inappropriate for initializing the specified algorithm. */ - boolean verifySignatureFor(String algorithm, byte[] secretBytes, String header, String payload, byte[] signatureBytes) throws NoSuchAlgorithmException, InvalidKeyException { return verifySignatureFor(algorithm, secretBytes, header.getBytes(StandardCharsets.UTF_8), payload.getBytes(StandardCharsets.UTF_8), signatureBytes); } @@ -39,7 +37,6 @@ boolean verifySignatureFor(String algorithm, byte[] secretBytes, String header, * @throws NoSuchAlgorithmException if the algorithm is not supported. * @throws InvalidKeyException if the given key is inappropriate for initializing the specified algorithm. */ - boolean verifySignatureFor(String algorithm, byte[] secretBytes, byte[] headerBytes, byte[] payloadBytes, byte[] signatureBytes) throws NoSuchAlgorithmException, InvalidKeyException { return MessageDigest.isEqual(createSignatureFor(algorithm, secretBytes, headerBytes, payloadBytes), signatureBytes); } @@ -55,7 +52,6 @@ boolean verifySignatureFor(String algorithm, byte[] secretBytes, byte[] headerBy * @throws NoSuchAlgorithmException if the algorithm is not supported. * @throws InvalidKeyException if the given key is inappropriate for initializing the specified algorithm. */ - byte[] createSignatureFor(String algorithm, byte[] secretBytes, byte[] headerBytes, byte[] payloadBytes) throws NoSuchAlgorithmException, InvalidKeyException { final Mac mac = Mac.getInstance(algorithm); mac.init(new SecretKeySpec(secretBytes, algorithm)); @@ -76,7 +72,6 @@ byte[] createSignatureFor(String algorithm, byte[] secretBytes, byte[] headerByt * @throws NoSuchAlgorithmException if the algorithm is not supported. * @throws InvalidKeyException if the given key is inappropriate for initializing the specified algorithm. */ - boolean verifySignatureFor(String algorithm, PublicKey publicKey, String header, String payload, byte[] signatureBytes) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { return verifySignatureFor(algorithm, publicKey, header.getBytes(StandardCharsets.UTF_8), payload.getBytes(StandardCharsets.UTF_8), signatureBytes); } @@ -93,7 +88,6 @@ boolean verifySignatureFor(String algorithm, PublicKey publicKey, String header, * @throws NoSuchAlgorithmException if the algorithm is not supported. * @throws InvalidKeyException if the given key is inappropriate for initializing the specified algorithm. */ - boolean verifySignatureFor(String algorithm, PublicKey publicKey, byte[] headerBytes, byte[] payloadBytes, byte[] signatureBytes) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { final Signature s = Signature.getInstance(algorithm); s.initVerify(publicKey); @@ -115,7 +109,6 @@ boolean verifySignatureFor(String algorithm, PublicKey publicKey, byte[] headerB * @throws InvalidKeyException if the given key is inappropriate for initializing the specified algorithm. * @throws SignatureException if this signature object is not initialized properly or if this signature algorithm is unable to process the input data provided. */ - byte[] createSignatureFor(String algorithm, PrivateKey privateKey, byte[] headerBytes, byte[] payloadBytes) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { final Signature s = Signature.getInstance(algorithm); s.initSign(privateKey); @@ -137,7 +130,6 @@ byte[] createSignatureFor(String algorithm, PrivateKey privateKey, byte[] header * @throws InvalidKeyException if the given key is inappropriate for initializing the specified algorithm. * @deprecated rather use corresponding method which takes header and payload as separate inputs */ - @Deprecated boolean verifySignatureFor(String algorithm, byte[] secretBytes, byte[] contentBytes, byte[] signatureBytes) throws NoSuchAlgorithmException, InvalidKeyException { return MessageDigest.isEqual(createSignatureFor(algorithm, secretBytes, contentBytes), signatureBytes); @@ -154,7 +146,6 @@ boolean verifySignatureFor(String algorithm, byte[] secretBytes, byte[] contentB * @throws InvalidKeyException if the given key is inappropriate for initializing the specified algorithm. * @deprecated rather use corresponding method which takes header and payload as separate inputs */ - @Deprecated byte[] createSignatureFor(String algorithm, byte[] secretBytes, byte[] contentBytes) throws NoSuchAlgorithmException, InvalidKeyException { final Mac mac = Mac.getInstance(algorithm); @@ -175,7 +166,6 @@ byte[] createSignatureFor(String algorithm, byte[] secretBytes, byte[] contentBy * @throws SignatureException if this signature object is not initialized properly or if this signature algorithm is unable to process the input data provided. * @deprecated rather use corresponding method which takes header and payload as separate inputs */ - @Deprecated boolean verifySignatureFor(String algorithm, PublicKey publicKey, byte[] contentBytes, byte[] signatureBytes) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { final Signature s = Signature.getInstance(algorithm); @@ -196,7 +186,6 @@ boolean verifySignatureFor(String algorithm, PublicKey publicKey, byte[] content * @throws SignatureException if this signature object is not initialized properly or if this signature algorithm is unable to process the input data provided. * @deprecated rather use corresponding method which takes header and payload as separate inputs */ - @Deprecated byte[] createSignatureFor(String algorithm, PrivateKey privateKey, byte[] contentBytes) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { final Signature s = Signature.getInstance(algorithm); diff --git a/lib/src/main/java/com/auth0/jwt/algorithms/ECDSAAlgorithm.java b/lib/src/main/java/com/auth0/jwt/algorithms/ECDSAAlgorithm.java index 12ddca70..043942b2 100644 --- a/lib/src/main/java/com/auth0/jwt/algorithms/ECDSAAlgorithm.java +++ b/lib/src/main/java/com/auth0/jwt/algorithms/ECDSAAlgorithm.java @@ -13,11 +13,12 @@ import java.security.interfaces.ECPublicKey; class ECDSAAlgorithm extends Algorithm { - private final ECDSAKeyProvider keyProvider; private final CryptoHelper crypto; private final int ecNumberSize; + private static final String INVALID_DER_SIGNATURE_FORMAT_MESSAGE = "Invalid DER signature format."; + //Visible for testing ECDSAAlgorithm(CryptoHelper crypto, String id, String algorithm, int ecNumberSize, ECDSAKeyProvider keyProvider) throws IllegalArgumentException { super(id, algorithm); @@ -91,7 +92,7 @@ byte[] DERToJOSE(byte[] derSignature) throws SignatureException { // DER Structure: http://crypto.stackexchange.com/a/1797 boolean derEncoded = derSignature[0] == 0x30 && derSignature.length != ecNumberSize * 2; if (!derEncoded) { - throw new SignatureException("Invalid DER signature format."); + throw new SignatureException(INVALID_DER_SIGNATURE_FORMAT_MESSAGE); } final byte[] joseSignature = new byte[ecNumberSize * 2]; @@ -106,7 +107,7 @@ byte[] DERToJOSE(byte[] derSignature) throws SignatureException { //Convert to unsigned. Should match DER length - offset int encodedLength = derSignature[offset++] & 0xff; if (encodedLength != derSignature.length - offset) { - throw new SignatureException("Invalid DER signature format."); + throw new SignatureException(INVALID_DER_SIGNATURE_FORMAT_MESSAGE); } //Skip 0x02 @@ -115,7 +116,7 @@ byte[] DERToJOSE(byte[] derSignature) throws SignatureException { //Obtain R number length (Includes padding) and skip it int rLength = derSignature[offset++]; if (rLength > ecNumberSize + 1) { - throw new SignatureException("Invalid DER signature format."); + throw new SignatureException(INVALID_DER_SIGNATURE_FORMAT_MESSAGE); } int rPadding = ecNumberSize - rLength; //Retrieve R number @@ -127,7 +128,7 @@ byte[] DERToJOSE(byte[] derSignature) throws SignatureException { //Obtain S number length. (Includes padding) int sLength = derSignature[offset++]; if (sLength > ecNumberSize + 1) { - throw new SignatureException("Invalid DER signature format."); + throw new SignatureException(INVALID_DER_SIGNATURE_FORMAT_MESSAGE); } int sPadding = ecNumberSize - sLength; //Retrieve R number diff --git a/lib/src/main/java/com/auth0/jwt/algorithms/HMACAlgorithm.java b/lib/src/main/java/com/auth0/jwt/algorithms/HMACAlgorithm.java index 5df23a83..8f5e74a4 100644 --- a/lib/src/main/java/com/auth0/jwt/algorithms/HMACAlgorithm.java +++ b/lib/src/main/java/com/auth0/jwt/algorithms/HMACAlgorithm.java @@ -10,7 +10,6 @@ import java.security.NoSuchAlgorithmException; class HMACAlgorithm extends Algorithm { - private final CryptoHelper crypto; private final byte[] secret; diff --git a/lib/src/main/java/com/auth0/jwt/algorithms/RSAAlgorithm.java b/lib/src/main/java/com/auth0/jwt/algorithms/RSAAlgorithm.java index 15cce55a..f4267996 100644 --- a/lib/src/main/java/com/auth0/jwt/algorithms/RSAAlgorithm.java +++ b/lib/src/main/java/com/auth0/jwt/algorithms/RSAAlgorithm.java @@ -6,7 +6,6 @@ import com.auth0.jwt.interfaces.RSAKeyProvider; import org.apache.commons.codec.binary.Base64; -import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SignatureException; @@ -14,7 +13,6 @@ import java.security.interfaces.RSAPublicKey; class RSAAlgorithm extends Algorithm { - private final RSAKeyProvider keyProvider; private final CryptoHelper crypto; diff --git a/lib/src/main/java/com/auth0/jwt/exceptions/AlgorithmMismatchException.java b/lib/src/main/java/com/auth0/jwt/exceptions/AlgorithmMismatchException.java index 1d7ba1ce..f9680e7a 100644 --- a/lib/src/main/java/com/auth0/jwt/exceptions/AlgorithmMismatchException.java +++ b/lib/src/main/java/com/auth0/jwt/exceptions/AlgorithmMismatchException.java @@ -1,6 +1,7 @@ package com.auth0.jwt.exceptions; public class AlgorithmMismatchException extends JWTVerificationException { + public AlgorithmMismatchException(String message) { super(message); } diff --git a/lib/src/main/java/com/auth0/jwt/exceptions/InvalidClaimException.java b/lib/src/main/java/com/auth0/jwt/exceptions/InvalidClaimException.java index ab348323..244cfa55 100644 --- a/lib/src/main/java/com/auth0/jwt/exceptions/InvalidClaimException.java +++ b/lib/src/main/java/com/auth0/jwt/exceptions/InvalidClaimException.java @@ -1,7 +1,7 @@ package com.auth0.jwt.exceptions; - public class InvalidClaimException extends JWTVerificationException { + public InvalidClaimException(String message) { super(message); } diff --git a/lib/src/main/java/com/auth0/jwt/exceptions/JWTCreationException.java b/lib/src/main/java/com/auth0/jwt/exceptions/JWTCreationException.java index 5bf4facb..68edcac4 100644 --- a/lib/src/main/java/com/auth0/jwt/exceptions/JWTCreationException.java +++ b/lib/src/main/java/com/auth0/jwt/exceptions/JWTCreationException.java @@ -1,6 +1,7 @@ package com.auth0.jwt.exceptions; public class JWTCreationException extends RuntimeException { + public JWTCreationException(String message, Throwable cause) { super(message, cause); } diff --git a/lib/src/main/java/com/auth0/jwt/exceptions/JWTDecodeException.java b/lib/src/main/java/com/auth0/jwt/exceptions/JWTDecodeException.java index 93799d31..2f1bf22c 100644 --- a/lib/src/main/java/com/auth0/jwt/exceptions/JWTDecodeException.java +++ b/lib/src/main/java/com/auth0/jwt/exceptions/JWTDecodeException.java @@ -1,6 +1,7 @@ package com.auth0.jwt.exceptions; public class JWTDecodeException extends JWTVerificationException { + public JWTDecodeException(String message) { this(message, null); } diff --git a/lib/src/main/java/com/auth0/jwt/exceptions/JWTVerificationException.java b/lib/src/main/java/com/auth0/jwt/exceptions/JWTVerificationException.java index 2ccfcccf..993567b2 100644 --- a/lib/src/main/java/com/auth0/jwt/exceptions/JWTVerificationException.java +++ b/lib/src/main/java/com/auth0/jwt/exceptions/JWTVerificationException.java @@ -1,6 +1,7 @@ package com.auth0.jwt.exceptions; public class JWTVerificationException extends RuntimeException { + public JWTVerificationException(String message) { this(message, null); } diff --git a/lib/src/main/java/com/auth0/jwt/exceptions/SignatureGenerationException.java b/lib/src/main/java/com/auth0/jwt/exceptions/SignatureGenerationException.java index 3637f97a..50cdf623 100644 --- a/lib/src/main/java/com/auth0/jwt/exceptions/SignatureGenerationException.java +++ b/lib/src/main/java/com/auth0/jwt/exceptions/SignatureGenerationException.java @@ -3,6 +3,7 @@ import com.auth0.jwt.algorithms.Algorithm; public class SignatureGenerationException extends JWTCreationException { + public SignatureGenerationException(Algorithm algorithm, Throwable cause) { super("The Token's Signature couldn't be generated when signing using the Algorithm: " + algorithm, cause); } diff --git a/lib/src/main/java/com/auth0/jwt/exceptions/SignatureVerificationException.java b/lib/src/main/java/com/auth0/jwt/exceptions/SignatureVerificationException.java index 12bd3429..9ea839b0 100644 --- a/lib/src/main/java/com/auth0/jwt/exceptions/SignatureVerificationException.java +++ b/lib/src/main/java/com/auth0/jwt/exceptions/SignatureVerificationException.java @@ -3,6 +3,7 @@ import com.auth0.jwt.algorithms.Algorithm; public class SignatureVerificationException extends JWTVerificationException { + public SignatureVerificationException(Algorithm algorithm) { this(algorithm, null); } diff --git a/lib/src/main/java/com/auth0/jwt/exceptions/TokenExpiredException.java b/lib/src/main/java/com/auth0/jwt/exceptions/TokenExpiredException.java index f5d2e67a..4f747f75 100644 --- a/lib/src/main/java/com/auth0/jwt/exceptions/TokenExpiredException.java +++ b/lib/src/main/java/com/auth0/jwt/exceptions/TokenExpiredException.java @@ -1,7 +1,6 @@ package com.auth0.jwt.exceptions; public class TokenExpiredException extends JWTVerificationException { - private static final long serialVersionUID = -7076928975713577708L; public TokenExpiredException(String message) { diff --git a/lib/src/main/java/com/auth0/jwt/impl/HeaderDeserializer.java b/lib/src/main/java/com/auth0/jwt/impl/HeaderDeserializer.java index cea2944a..93fdabaf 100644 --- a/lib/src/main/java/com/auth0/jwt/impl/HeaderDeserializer.java +++ b/lib/src/main/java/com/auth0/jwt/impl/HeaderDeserializer.java @@ -12,7 +12,6 @@ import java.util.Map; class HeaderDeserializer extends StdDeserializer { - private final ObjectReader objectReader; HeaderDeserializer(ObjectReader objectReader) { diff --git a/lib/src/main/java/com/auth0/jwt/impl/JsonNodeClaim.java b/lib/src/main/java/com/auth0/jwt/impl/JsonNodeClaim.java index 4e2aef63..8238091c 100644 --- a/lib/src/main/java/com/auth0/jwt/impl/JsonNodeClaim.java +++ b/lib/src/main/java/com/auth0/jwt/impl/JsonNodeClaim.java @@ -6,7 +6,6 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectReader; import java.io.IOException; 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 0c9de6c7..901a9385 100644 --- a/lib/src/main/java/com/auth0/jwt/impl/PayloadDeserializer.java +++ b/lib/src/main/java/com/auth0/jwt/impl/PayloadDeserializer.java @@ -14,7 +14,6 @@ import java.util.*; class PayloadDeserializer extends StdDeserializer { - private final ObjectReader objectReader; PayloadDeserializer(ObjectReader reader) { diff --git a/lib/src/main/java/com/auth0/jwt/impl/PayloadImpl.java b/lib/src/main/java/com/auth0/jwt/impl/PayloadImpl.java index 2c5558f2..38f7906d 100644 --- a/lib/src/main/java/com/auth0/jwt/impl/PayloadImpl.java +++ b/lib/src/main/java/com/auth0/jwt/impl/PayloadImpl.java @@ -14,7 +14,6 @@ * The PayloadImpl class implements the Payload interface. */ class PayloadImpl implements Payload, Serializable { - private static final long serialVersionUID = 1659021498824562311L; private final String issuer; diff --git a/lib/src/main/java/com/auth0/jwt/impl/PayloadSerializer.java b/lib/src/main/java/com/auth0/jwt/impl/PayloadSerializer.java index dd0d2f42..ea9f95f1 100644 --- a/lib/src/main/java/com/auth0/jwt/impl/PayloadSerializer.java +++ b/lib/src/main/java/com/auth0/jwt/impl/PayloadSerializer.java @@ -20,37 +20,33 @@ private PayloadSerializer(Class t) { @Override public void serialize(ClaimsHolder holder, JsonGenerator gen, SerializerProvider provider) throws IOException { - gen.writeStartObject(); for (Map.Entry e : holder.getClaims().entrySet()) { - switch (e.getKey()) { - case PublicClaims.AUDIENCE: - if (e.getValue() instanceof String) { - gen.writeFieldName(e.getKey()); - gen.writeString((String)e.getValue()); - break; - } - String[] audArray = (String[]) e.getValue(); - if (audArray.length == 1) { - gen.writeFieldName(e.getKey()); - gen.writeString(audArray[0]); - } else if (audArray.length > 1) { - gen.writeFieldName(e.getKey()); - gen.writeStartArray(); - for(String aud : audArray) { - gen.writeString(aud); - } - gen.writeEndArray(); - } - break; - default: + if (PublicClaims.AUDIENCE.equals(e.getKey())) { + if (e.getValue() instanceof String) { + gen.writeFieldName(e.getKey()); + gen.writeString((String) e.getValue()); + continue; + } + String[] audArray = (String[]) e.getValue(); + if (audArray.length == 1) { + gen.writeFieldName(e.getKey()); + gen.writeString(audArray[0]); + } else if (audArray.length > 1) { gen.writeFieldName(e.getKey()); - if (e.getValue() instanceof Date) { // true for EXPIRES_AT, ISSUED_AT, NOT_BEFORE - gen.writeNumber(dateToSeconds((Date) e.getValue())); - } else { - gen.writeObject(e.getValue()); + gen.writeStartArray(); + for (String aud : audArray) { + gen.writeString(aud); } - break; + gen.writeEndArray(); + } + } else { + gen.writeFieldName(e.getKey()); + if (e.getValue() instanceof Date) { // true for EXPIRES_AT, ISSUED_AT, NOT_BEFORE + gen.writeNumber(dateToSeconds((Date) e.getValue())); + } else { + gen.writeObject(e.getValue()); + } } } diff --git a/lib/src/main/java/com/auth0/jwt/impl/PublicClaims.java b/lib/src/main/java/com/auth0/jwt/impl/PublicClaims.java index cc2b3db8..eb18866a 100644 --- a/lib/src/main/java/com/auth0/jwt/impl/PublicClaims.java +++ b/lib/src/main/java/com/auth0/jwt/impl/PublicClaims.java @@ -1,8 +1,6 @@ package com.auth0.jwt.impl; - public interface PublicClaims { - //Header String ALGORITHM = "alg"; String CONTENT_TYPE = "cty"; @@ -17,5 +15,4 @@ public interface PublicClaims { String ISSUED_AT = "iat"; String JWT_ID = "jti"; String AUDIENCE = "aud"; - } diff --git a/lib/src/main/java/com/auth0/jwt/interfaces/Claim.java b/lib/src/main/java/com/auth0/jwt/interfaces/Claim.java index b3d41b1d..cde08e4d 100644 --- a/lib/src/main/java/com/auth0/jwt/interfaces/Claim.java +++ b/lib/src/main/java/com/auth0/jwt/interfaces/Claim.java @@ -10,7 +10,6 @@ * The Claim class holds the value in a generic way so that it can be recovered in many representations. */ public interface Claim { - /** * Whether this Claim has a null value or not. * diff --git a/lib/src/main/java/com/auth0/jwt/interfaces/Clock.java b/lib/src/main/java/com/auth0/jwt/interfaces/Clock.java index 7dd9c43b..454de9be 100644 --- a/lib/src/main/java/com/auth0/jwt/interfaces/Clock.java +++ b/lib/src/main/java/com/auth0/jwt/interfaces/Clock.java @@ -6,7 +6,6 @@ * The Clock class is used to wrap calls to Date class. */ public interface Clock { - /** * Returns a new Date representing Today's time. * diff --git a/lib/src/main/java/com/auth0/jwt/interfaces/ECDSAKeyProvider.java b/lib/src/main/java/com/auth0/jwt/interfaces/ECDSAKeyProvider.java index 55df451d..19f96336 100644 --- a/lib/src/main/java/com/auth0/jwt/interfaces/ECDSAKeyProvider.java +++ b/lib/src/main/java/com/auth0/jwt/interfaces/ECDSAKeyProvider.java @@ -7,4 +7,5 @@ * Elliptic Curve (EC) Public/Private Key provider. */ public interface ECDSAKeyProvider extends KeyProvider { + } diff --git a/lib/src/main/java/com/auth0/jwt/interfaces/Header.java b/lib/src/main/java/com/auth0/jwt/interfaces/Header.java index 0a83d1ce..706ebe86 100644 --- a/lib/src/main/java/com/auth0/jwt/interfaces/Header.java +++ b/lib/src/main/java/com/auth0/jwt/interfaces/Header.java @@ -4,7 +4,6 @@ * The Header class represents the 1st part of the JWT, where the Header value is hold. */ public interface Header { - /** * Getter for the Algorithm "alg" claim defined in the JWT's Header. If the claim is missing it will return null. * diff --git a/lib/src/main/java/com/auth0/jwt/interfaces/JWTPartsParser.java b/lib/src/main/java/com/auth0/jwt/interfaces/JWTPartsParser.java index 520e35c8..4f76b924 100644 --- a/lib/src/main/java/com/auth0/jwt/interfaces/JWTPartsParser.java +++ b/lib/src/main/java/com/auth0/jwt/interfaces/JWTPartsParser.java @@ -6,7 +6,6 @@ * The JWTPartsParser class defines which parts of the JWT should be converted to it's specific Object representation instance. */ public interface JWTPartsParser { - /** * Parses the given JSON into a Payload instance. * diff --git a/lib/src/main/java/com/auth0/jwt/interfaces/JWTVerifier.java b/lib/src/main/java/com/auth0/jwt/interfaces/JWTVerifier.java index 140af8e6..29dec95f 100644 --- a/lib/src/main/java/com/auth0/jwt/interfaces/JWTVerifier.java +++ b/lib/src/main/java/com/auth0/jwt/interfaces/JWTVerifier.java @@ -4,7 +4,6 @@ public interface JWTVerifier { - /** * Performs the verification against the given Token * diff --git a/lib/src/main/java/com/auth0/jwt/interfaces/KeyProvider.java b/lib/src/main/java/com/auth0/jwt/interfaces/KeyProvider.java index fa3b13c9..27e125ba 100644 --- a/lib/src/main/java/com/auth0/jwt/interfaces/KeyProvider.java +++ b/lib/src/main/java/com/auth0/jwt/interfaces/KeyProvider.java @@ -10,7 +10,6 @@ * @param the class that represents the Private Key */ interface KeyProvider { - /** * Getter for the Public Key instance with the given Id. Used to verify the signature on the JWT verification stage. * diff --git a/lib/src/main/java/com/auth0/jwt/interfaces/Payload.java b/lib/src/main/java/com/auth0/jwt/interfaces/Payload.java index 0f639ab9..89779936 100644 --- a/lib/src/main/java/com/auth0/jwt/interfaces/Payload.java +++ b/lib/src/main/java/com/auth0/jwt/interfaces/Payload.java @@ -8,7 +8,6 @@ * The Payload class represents the 2nd part of the JWT, where the Payload value is hold. */ public interface Payload { - /** * Get the value of the "iss" claim, or null if it's not available. * diff --git a/lib/src/main/java/com/auth0/jwt/interfaces/RSAKeyProvider.java b/lib/src/main/java/com/auth0/jwt/interfaces/RSAKeyProvider.java index 55376f4d..834ac74f 100644 --- a/lib/src/main/java/com/auth0/jwt/interfaces/RSAKeyProvider.java +++ b/lib/src/main/java/com/auth0/jwt/interfaces/RSAKeyProvider.java @@ -7,4 +7,5 @@ * RSA Public/Private Key provider. */ public interface RSAKeyProvider extends KeyProvider { + } diff --git a/lib/src/test/java/com/auth0/jwt/ClockImplTest.java b/lib/src/test/java/com/auth0/jwt/ClockImplTest.java index 0eec07d7..21a9f11b 100644 --- a/lib/src/test/java/com/auth0/jwt/ClockImplTest.java +++ b/lib/src/test/java/com/auth0/jwt/ClockImplTest.java @@ -12,7 +12,7 @@ public class ClockImplTest { @Test - public void shouldGetToday() throws Exception{ + public void shouldGetToday() { Clock clock = new ClockImpl(); Date clockToday = clock.getToday(); assertThat(clockToday, is(notNullValue())); diff --git a/lib/src/test/java/com/auth0/jwt/ConcurrentVerifyTest.java b/lib/src/test/java/com/auth0/jwt/ConcurrentVerifyTest.java index 41098c25..7ab558a4 100644 --- a/lib/src/test/java/com/auth0/jwt/ConcurrentVerifyTest.java +++ b/lib/src/test/java/com/auth0/jwt/ConcurrentVerifyTest.java @@ -19,7 +19,6 @@ //@Ignore("Skipping concurrency tests") public class ConcurrentVerifyTest { - private static final long TIMEOUT = 10 * 1000 * 1000; //1 min private static final int THREAD_COUNT = 100; private static final int REPEAT_COUNT = 1000; @@ -33,16 +32,15 @@ public class ConcurrentVerifyTest { private static ExecutorService executor; @BeforeClass - public static void beforeAll() throws Exception { + public static void beforeAll() { executor = Executors.newFixedThreadPool(THREAD_COUNT); } @AfterClass - public static void afterAll() throws Exception { + public static void afterAll() { executor.shutdown(); } - @SuppressWarnings("Convert2Lambda") private void concurrentVerify(final JWTVerifier verifier, final String token) throws TimeoutException, InterruptedException { final Waiter waiter = new Waiter(); List tasks = Collections.nCopies(REPEAT_COUNT, new VerifyTask(waiter, verifier, token)); @@ -63,7 +61,7 @@ private static class VerifyTask implements Callable { } @Override - public DecodedJWT call() throws Exception { + public DecodedJWT call() { DecodedJWT jwt = null; try { jwt = verifier.verify(token); diff --git a/lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java b/lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java index 7df869c1..a23d8638 100644 --- a/lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java +++ b/lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java @@ -29,16 +29,14 @@ import static org.mockito.Mockito.when; public class JWTCreatorTest { - private static final String PRIVATE_KEY_FILE_RSA = "src/test/resources/rsa-private.pem"; private static final String PRIVATE_KEY_FILE_EC_256 = "src/test/resources/ec256-key-private.pem"; - @Rule public ExpectedException exception = ExpectedException.none(); @Test - public void shouldThrowWhenRequestingSignWithoutAlgorithm() throws Exception { + public void shouldThrowWhenRequestingSignWithoutAlgorithm() { exception.expect(IllegalArgumentException.class); exception.expectMessage("The Algorithm cannot be null"); JWTCreator.init() @@ -47,7 +45,7 @@ public void shouldThrowWhenRequestingSignWithoutAlgorithm() throws Exception { @SuppressWarnings("Convert2Diamond") @Test - public void shouldAddHeaderClaim() throws Exception { + public void shouldAddHeaderClaim() { Map header = new HashMap(); header.put("asd", 123); String signed = JWTCreator.init() @@ -61,7 +59,7 @@ public void shouldAddHeaderClaim() throws Exception { } @Test - public void shouldReturnBuilderIfNullMapIsProvided() throws Exception { + public void shouldReturnBuilderIfNullMapIsProvided() { String signed = JWTCreator.init() .withHeader(null) .sign(Algorithm.HMAC256("secret")); @@ -70,7 +68,7 @@ public void shouldReturnBuilderIfNullMapIsProvided() throws Exception { } @Test - public void shouldOverwriteExistingHeaderIfHeaderMapContainsTheSameKey() throws Exception { + public void shouldOverwriteExistingHeaderIfHeaderMapContainsTheSameKey() { Map header = new HashMap(); header.put(PublicClaims.KEY_ID, "xyz"); @@ -86,7 +84,7 @@ public void shouldOverwriteExistingHeaderIfHeaderMapContainsTheSameKey() throws } @Test - public void shouldOverwriteExistingHeadersWhenSettingSameHeaderKey() throws Exception { + public void shouldOverwriteExistingHeadersWhenSettingSameHeaderKey() { Map header = new HashMap(); header.put(PublicClaims.KEY_ID, "xyz"); @@ -102,7 +100,7 @@ public void shouldOverwriteExistingHeadersWhenSettingSameHeaderKey() throws Exce } @Test - public void shouldRemoveHeaderIfTheValueIsNull() throws Exception { + public void shouldRemoveHeaderIfTheValueIsNull() { Map header = new HashMap(); header.put(PublicClaims.KEY_ID, null); header.put("test2", "isSet"); @@ -120,7 +118,7 @@ public void shouldRemoveHeaderIfTheValueIsNull() throws Exception { } @Test - public void shouldAddKeyId() throws Exception { + public void shouldAddKeyId() { String signed = JWTCreator.init() .withKeyId("56a8bd44da435300010000015f5ed") .sign(Algorithm.HMAC256("secret")); @@ -198,7 +196,7 @@ public void shouldNotOverwriteKeyIdIfAddedFromECDSAAlgorithms() throws Exception } @Test - public void shouldAddIssuer() throws Exception { + public void shouldAddIssuer() { String signed = JWTCreator.init() .withIssuer("auth0") .sign(Algorithm.HMAC256("secret")); @@ -208,7 +206,7 @@ public void shouldAddIssuer() throws Exception { } @Test - public void shouldAddSubject() throws Exception { + public void shouldAddSubject() { String signed = JWTCreator.init() .withSubject("1234567890") .sign(Algorithm.HMAC256("secret")); @@ -218,7 +216,7 @@ public void shouldAddSubject() throws Exception { } @Test - public void shouldAddAudience() throws Exception { + public void shouldAddAudience() { String signed = JWTCreator.init() .withAudience("Mark") .sign(Algorithm.HMAC256("secret")); @@ -236,7 +234,7 @@ public void shouldAddAudience() throws Exception { } @Test - public void shouldAddExpiresAt() throws Exception { + public void shouldAddExpiresAt() { String signed = JWTCreator.init() .withExpiresAt(new Date(1477592000)) .sign(Algorithm.HMAC256("secret")); @@ -246,7 +244,7 @@ public void shouldAddExpiresAt() throws Exception { } @Test - public void shouldAddNotBefore() throws Exception { + public void shouldAddNotBefore() { String signed = JWTCreator.init() .withNotBefore(new Date(1477592000)) .sign(Algorithm.HMAC256("secret")); @@ -256,7 +254,7 @@ public void shouldAddNotBefore() throws Exception { } @Test - public void shouldAddIssuedAt() throws Exception { + public void shouldAddIssuedAt() { String signed = JWTCreator.init() .withIssuedAt(new Date(1477592000)) .sign(Algorithm.HMAC256("secret")); @@ -266,7 +264,7 @@ public void shouldAddIssuedAt() throws Exception { } @Test - public void shouldAddJWTId() throws Exception { + public void shouldAddJWTId() { String signed = JWTCreator.init() .withJWTId("jwt_id_123") .sign(Algorithm.HMAC256("secret")); @@ -276,7 +274,7 @@ public void shouldAddJWTId() throws Exception { } @Test - public void shouldRemoveClaimWhenPassingNull() throws Exception { + public void shouldRemoveClaimWhenPassingNull() { String signed = JWTCreator.init() .withIssuer("iss") .withIssuer(null) @@ -287,7 +285,7 @@ public void shouldRemoveClaimWhenPassingNull() throws Exception { } @Test - public void shouldSetCorrectAlgorithmInTheHeader() throws Exception { + public void shouldSetCorrectAlgorithmInTheHeader() { String signed = JWTCreator.init() .sign(Algorithm.HMAC256("secret")); @@ -298,7 +296,7 @@ public void shouldSetCorrectAlgorithmInTheHeader() throws Exception { } @Test - public void shouldSetDefaultTypeInTheHeader() throws Exception { + public void shouldSetDefaultTypeInTheHeader() { String signed = JWTCreator.init() .sign(Algorithm.HMAC256("secret")); @@ -309,7 +307,7 @@ public void shouldSetDefaultTypeInTheHeader() throws Exception { } @Test - public void shouldSetCustomTypeInTheHeader() throws Exception { + public void shouldSetCustomTypeInTheHeader() { Map header = Collections.singletonMap("typ", "passport"); String signed = JWTCreator.init() .withHeader(header) @@ -322,7 +320,7 @@ public void shouldSetCustomTypeInTheHeader() throws Exception { } @Test - public void shouldSetEmptySignatureIfAlgorithmIsNone() throws Exception { + public void shouldSetEmptySignatureIfAlgorithmIsNone() { String signed = JWTCreator.init() .sign(Algorithm.none()); assertThat(signed, is(notNullValue())); @@ -330,7 +328,7 @@ public void shouldSetEmptySignatureIfAlgorithmIsNone() throws Exception { } @Test - public void shouldThrowOnNullCustomClaimName() throws Exception { + public void shouldThrowOnNullCustomClaimName() { exception.expect(IllegalArgumentException.class); exception.expectMessage("The Custom Claim's name can't be null."); JWTCreator.init() @@ -338,7 +336,7 @@ public void shouldThrowOnNullCustomClaimName() throws Exception { } @Test - public void shouldAcceptCustomClaimOfTypeString() throws Exception { + public void shouldAcceptCustomClaimOfTypeString() { String jwt = JWTCreator.init() .withClaim("name", "value") .sign(Algorithm.HMAC256("secret")); @@ -349,7 +347,7 @@ public void shouldAcceptCustomClaimOfTypeString() throws Exception { } @Test - public void shouldAcceptCustomClaimOfTypeInteger() throws Exception { + public void shouldAcceptCustomClaimOfTypeInteger() { String jwt = JWTCreator.init() .withClaim("name", 123) .sign(Algorithm.HMAC256("secret")); @@ -360,7 +358,7 @@ public void shouldAcceptCustomClaimOfTypeInteger() throws Exception { } @Test - public void shouldAcceptCustomClaimOfTypeLong() throws Exception { + public void shouldAcceptCustomClaimOfTypeLong() { String jwt = JWTCreator.init() .withClaim("name", Long.MAX_VALUE) .sign(Algorithm.HMAC256("secret")); @@ -371,7 +369,7 @@ public void shouldAcceptCustomClaimOfTypeLong() throws Exception { } @Test - public void shouldAcceptCustomClaimOfTypeDouble() throws Exception { + public void shouldAcceptCustomClaimOfTypeDouble() { String jwt = JWTCreator.init() .withClaim("name", 23.45) .sign(Algorithm.HMAC256("secret")); @@ -382,7 +380,7 @@ public void shouldAcceptCustomClaimOfTypeDouble() throws Exception { } @Test - public void shouldAcceptCustomClaimOfTypeBoolean() throws Exception { + public void shouldAcceptCustomClaimOfTypeBoolean() { String jwt = JWTCreator.init() .withClaim("name", true) .sign(Algorithm.HMAC256("secret")); @@ -393,7 +391,7 @@ public void shouldAcceptCustomClaimOfTypeBoolean() throws Exception { } @Test - public void shouldAcceptCustomClaimOfTypeDate() throws Exception { + public void shouldAcceptCustomClaimOfTypeDate() { Date date = new Date(1478891521000L); String jwt = JWTCreator.init() .withClaim("name", date) @@ -405,7 +403,7 @@ public void shouldAcceptCustomClaimOfTypeDate() throws Exception { } @Test - public void shouldAcceptCustomArrayClaimOfTypeString() throws Exception { + public void shouldAcceptCustomArrayClaimOfTypeString() { String jwt = JWTCreator.init() .withArrayClaim("name", new String[]{"text", "123", "true"}) .sign(Algorithm.HMAC256("secret")); @@ -416,7 +414,7 @@ public void shouldAcceptCustomArrayClaimOfTypeString() throws Exception { } @Test - public void shouldAcceptCustomArrayClaimOfTypeInteger() throws Exception { + public void shouldAcceptCustomArrayClaimOfTypeInteger() { String jwt = JWTCreator.init() .withArrayClaim("name", new Integer[]{1, 2, 3}) .sign(Algorithm.HMAC256("secret")); @@ -427,7 +425,7 @@ public void shouldAcceptCustomArrayClaimOfTypeInteger() throws Exception { } @Test - public void shouldAcceptCustomArrayClaimOfTypeLong() throws Exception { + public void shouldAcceptCustomArrayClaimOfTypeLong() { String jwt = JWTCreator.init() .withArrayClaim("name", new Long[]{1L, 2L, 3L}) .sign(Algorithm.HMAC256("secret")); @@ -438,7 +436,7 @@ public void shouldAcceptCustomArrayClaimOfTypeLong() throws Exception { } @Test - public void shouldAcceptCustomClaimOfTypeMap() throws Exception { + public void shouldAcceptCustomClaimOfTypeMap() { Map data = new HashMap<>(); data.put("test1", "abc"); data.put("test2", "def"); @@ -452,7 +450,7 @@ public void shouldAcceptCustomClaimOfTypeMap() throws Exception { } @Test - public void shouldRefuseCustomClaimOfTypeUserPojo() throws Exception{ + public void shouldRefuseCustomClaimOfTypeUserPojo() { Map data = new HashMap<>(); data.put("test1", new UserPojo("Michael", 255)); @@ -507,9 +505,9 @@ public void shouldAcceptCustomMapClaimOfBasicObjectTypes() throws Exception { assertThat(map.get("boolean"), is(true)); // array types - assertThat(map.get("intArray"), is(Arrays.asList(new Integer[]{3, 5}))); - assertThat(map.get("longArray"), is(Arrays.asList(new Long[]{Long.MAX_VALUE, Long.MIN_VALUE}))); - assertThat(map.get("stringArray"), is(Arrays.asList(new String[]{"string"}))); + assertThat(map.get("intArray"), is(Arrays.asList(3, 5))); + assertThat(map.get("longArray"), is(Arrays.asList(Long.MAX_VALUE, Long.MIN_VALUE))); + assertThat(map.get("stringArray"), is(Arrays.asList("string"))); // list assertThat(map.get("list"), is(Arrays.asList("a", "b", "c"))); @@ -561,9 +559,9 @@ public void shouldAcceptCustomListClaimOfBasicObjectTypes() throws Exception { assertThat(list.get(5), is(true)); // array types - assertThat(list.get(6), is(Arrays.asList(new Integer[]{3, 5}))); - assertThat(list.get(7), is(Arrays.asList(new Long[]{Long.MAX_VALUE, Long.MIN_VALUE}))); - assertThat(list.get(8), is(Arrays.asList(new String[]{"string"}))); + assertThat(list.get(6), is(Arrays.asList(3, 5))); + assertThat(list.get(7), is(Arrays.asList(Long.MAX_VALUE, Long.MIN_VALUE))); + assertThat(list.get(8), is(Arrays.asList("string"))); // list assertThat(list.get(9), is(Arrays.asList("a", "b", "c"))); @@ -572,7 +570,7 @@ public void shouldAcceptCustomListClaimOfBasicObjectTypes() throws Exception { } @Test - public void shouldAcceptCustomClaimForNullListItem() throws Exception{ + public void shouldAcceptCustomClaimForNullListItem() { Map data = new HashMap<>(); data.put("test1", Arrays.asList("a", null, "c")); @@ -582,7 +580,7 @@ public void shouldAcceptCustomClaimForNullListItem() throws Exception{ } @Test - public void shouldRefuseCustomClaimForNullMapValue() throws Exception{ + public void shouldRefuseCustomClaimForNullMapValue() { Map data = new HashMap<>(); data.put("subKey", null); @@ -594,7 +592,7 @@ public void shouldRefuseCustomClaimForNullMapValue() throws Exception{ } @Test - public void shouldRefuseCustomClaimForNullMapKey() throws Exception{ + public void shouldRefuseCustomClaimForNullMapKey() { Map data = new HashMap<>(); data.put(null, "subValue"); @@ -607,7 +605,7 @@ public void shouldRefuseCustomClaimForNullMapKey() throws Exception{ @SuppressWarnings({ "unchecked", "rawtypes" }) @Test - public void shouldRefuseCustomMapClaimForNonStringKey() throws Exception{ + public void shouldRefuseCustomMapClaimForNonStringKey() { Map data = new HashMap<>(); data.put(new Object(), "value"); @@ -619,7 +617,7 @@ public void shouldRefuseCustomMapClaimForNonStringKey() throws Exception{ } @Test - public void shouldRefuseCustomListClaimForUnknownListElement() throws Exception{ + public void shouldRefuseCustomListClaimForUnknownListElement() { List list = Arrays.asList(new UserPojo("Michael", 255)); exception.expect(IllegalArgumentException.class); @@ -630,7 +628,7 @@ public void shouldRefuseCustomListClaimForUnknownListElement() throws Exception{ } @Test - public void shouldRefuseCustomListClaimForUnknownListElementWrappedInAMap() throws Exception{ + public void shouldRefuseCustomListClaimForUnknownListElementWrappedInAMap() { List list = Arrays.asList(new UserPojo("Michael", 255)); Map data = new HashMap<>(); @@ -644,7 +642,7 @@ public void shouldRefuseCustomListClaimForUnknownListElementWrappedInAMap() thro } @Test - public void shouldRefuseCustomListClaimForUnknownArrayType() throws Exception{ + public void shouldRefuseCustomListClaimForUnknownArrayType() { List list = new ArrayList<>(); list.add(new Object[] {"test"}); @@ -654,5 +652,4 @@ public void shouldRefuseCustomListClaimForUnknownArrayType() throws Exception{ .withClaim("list", list) .sign(Algorithm.HMAC256("secret")); } - -} \ No newline at end of file +} diff --git a/lib/src/test/java/com/auth0/jwt/JWTDecoderTest.java b/lib/src/test/java/com/auth0/jwt/JWTDecoderTest.java index dfe59182..ce55c3bf 100644 --- a/lib/src/test/java/com/auth0/jwt/JWTDecoderTest.java +++ b/lib/src/test/java/com/auth0/jwt/JWTDecoderTest.java @@ -33,21 +33,21 @@ public void getSubject() throws Exception { // Exceptions @Test - public void shouldThrowIfLessThan3Parts() throws Exception { + public void shouldThrowIfLessThan3Parts() { exception.expect(JWTDecodeException.class); exception.expectMessage("The token was expected to have 3 parts, but got 2."); JWT.decode("two.parts"); } @Test - public void shouldThrowIfMoreThan3Parts() throws Exception { + public void shouldThrowIfMoreThan3Parts() { exception.expect(JWTDecodeException.class); exception.expectMessage("The token was expected to have 3 parts, but got 4."); JWT.decode("this.has.four.parts"); } @Test - public void shouldThrowIfPayloadHasInvalidJSONFormat() throws Exception { + public void shouldThrowIfPayloadHasInvalidJSONFormat() { String validJson = "{}"; String invalidJson = "}{"; exception.expect(JWTDecodeException.class); @@ -56,7 +56,7 @@ public void shouldThrowIfPayloadHasInvalidJSONFormat() throws Exception { } @Test - public void shouldThrowIfHeaderHasInvalidJSONFormat() throws Exception { + public void shouldThrowIfHeaderHasInvalidJSONFormat() { String validJson = "{}"; String invalidJson = "}{"; exception.expect(JWTDecodeException.class); @@ -67,7 +67,7 @@ public void shouldThrowIfHeaderHasInvalidJSONFormat() throws Exception { // Parts @Test - public void shouldGetStringToken() throws Exception { + public void shouldGetStringToken() { DecodedJWT jwt = JWT.decode("eyJhbGciOiJIUzI1NiJ9.e30.XmNK3GpH3Ys_7wsYBfq4C3M6goz71I7dTgUkuIa5lyQ"); assertThat(jwt, is(notNullValue())); assertThat(jwt.getToken(), is(notNullValue())); @@ -75,21 +75,21 @@ public void shouldGetStringToken() throws Exception { } @Test - public void shouldGetHeader() throws Exception { + public void shouldGetHeader() { DecodedJWT jwt = JWT.decode("eyJhbGciOiJIUzI1NiJ9.e30.XmNK3GpH3Ys_7wsYBfq4C3M6goz71I7dTgUkuIa5lyQ"); assertThat(jwt, is(notNullValue())); assertThat(jwt.getHeader(), is("eyJhbGciOiJIUzI1NiJ9")); } @Test - public void shouldGetPayload() throws Exception { + public void shouldGetPayload() { DecodedJWT jwt = JWT.decode("eyJhbGciOiJIUzI1NiJ9.e30.XmNK3GpH3Ys_7wsYBfq4C3M6goz71I7dTgUkuIa5lyQ"); assertThat(jwt, is(notNullValue())); assertThat(jwt.getPayload(), is("e30")); } @Test - public void shouldGetSignature() throws Exception { + public void shouldGetSignature() { DecodedJWT jwt = JWT.decode("eyJhbGciOiJIUzI1NiJ9.e30.XmNK3GpH3Ys_7wsYBfq4C3M6goz71I7dTgUkuIa5lyQ"); assertThat(jwt, is(notNullValue())); assertThat(jwt.getSignature(), is("XmNK3GpH3Ys_7wsYBfq4C3M6goz71I7dTgUkuIa5lyQ")); @@ -98,21 +98,21 @@ public void shouldGetSignature() throws Exception { // Public PublicClaims @Test - public void shouldGetIssuer() throws Exception { + public void shouldGetIssuer() { DecodedJWT jwt = JWT.decode("eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJKb2huIERvZSJ9.SgXosfRR_IwCgHq5lF3tlM-JHtpucWCRSaVuoHTbWbQ"); assertThat(jwt, is(notNullValue())); assertThat(jwt.getIssuer(), is("John Doe")); } @Test - public void shouldGetSubject() throws Exception { + public void shouldGetSubject() { DecodedJWT jwt = JWT.decode("eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJUb2szbnMifQ.RudAxkslimoOY3BLl2Ghny3BrUKu9I1ZrXzCZGDJtNs"); assertThat(jwt, is(notNullValue())); assertThat(jwt.getSubject(), is("Tok3ns")); } @Test - public void shouldGetArrayAudience() throws Exception { + public void shouldGetArrayAudience() { DecodedJWT jwt = JWT.decode("eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOlsiSG9wZSIsIlRyYXZpcyIsIlNvbG9tb24iXX0.Tm4W8WnfPjlmHSmKFakdij0on2rWPETpoM7Sh0u6-S4"); assertThat(jwt, is(notNullValue())); assertThat(jwt.getAudience(), is(IsCollectionWithSize.hasSize(3))); @@ -120,7 +120,7 @@ public void shouldGetArrayAudience() throws Exception { } @Test - public void shouldGetStringAudience() throws Exception { + public void shouldGetStringAudience() { DecodedJWT jwt = JWT.decode("eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJKYWNrIFJleWVzIn0.a4I9BBhPt1OB1GW67g2P1bEHgi6zgOjGUL4LvhE9Dgc"); assertThat(jwt, is(notNullValue())); assertThat(jwt.getAudience(), is(IsCollectionWithSize.hasSize(1))); @@ -128,7 +128,7 @@ public void shouldGetStringAudience() throws Exception { } @Test - public void shouldGetExpirationTime() throws Exception { + public void shouldGetExpirationTime() { DecodedJWT jwt = JWT.decode("eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE0NzY3MjcwODZ9.L9dcPHEDQew2u9MkDCORFkfDGcSOsgoPqNY-LUMLEHg"); assertThat(jwt, is(notNullValue())); assertThat(jwt.getExpiresAt(), is(instanceOf(Date.class))); @@ -139,7 +139,7 @@ public void shouldGetExpirationTime() throws Exception { } @Test - public void shouldGetNotBefore() throws Exception { + public void shouldGetNotBefore() { DecodedJWT jwt = JWT.decode("eyJhbGciOiJIUzI1NiJ9.eyJuYmYiOjE0NzY3MjcwODZ9.tkpD3iCPQPVqjnjpDVp2bJMBAgpVCG9ZjlBuMitass0"); assertThat(jwt, is(notNullValue())); assertThat(jwt.getNotBefore(), is(instanceOf(Date.class))); @@ -150,7 +150,7 @@ public void shouldGetNotBefore() throws Exception { } @Test - public void shouldGetIssuedAt() throws Exception { + public void shouldGetIssuedAt() { DecodedJWT jwt = JWT.decode("eyJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE0NzY3MjcwODZ9.KPjGoW665E8V5_27Jugab8qSTxLk2cgquhPCBfAP0_w"); assertThat(jwt, is(notNullValue())); assertThat(jwt.getIssuedAt(), is(instanceOf(Date.class))); @@ -161,28 +161,28 @@ public void shouldGetIssuedAt() throws Exception { } @Test - public void shouldGetId() throws Exception { + public void shouldGetId() { DecodedJWT jwt = JWT.decode("eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxMjM0NTY3ODkwIn0.m3zgEfVUFOd-CvL3xG5BuOWLzb0zMQZCqiVNQQOPOvA"); assertThat(jwt, is(notNullValue())); assertThat(jwt.getId(), is("1234567890")); } @Test - public void shouldGetContentType() throws Exception { + public void shouldGetContentType() { DecodedJWT jwt = JWT.decode("eyJhbGciOiJIUzI1NiIsImN0eSI6ImF3ZXNvbWUifQ.e30.AIm-pJDOaAyct9qKMlN-lQieqNDqc3d4erqUZc5SHAs"); assertThat(jwt, is(notNullValue())); assertThat(jwt.getContentType(), is("awesome")); } @Test - public void shouldGetType() throws Exception { + public void shouldGetType() { DecodedJWT jwt = JWT.decode("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXUyJ9.e30.WdFmrzx8b9v_a-r6EHC2PTAaWywgm_8LiP8RBRhYwkI"); assertThat(jwt, is(notNullValue())); assertThat(jwt.getType(), is("JWS")); } @Test - public void shouldGetAlgorithm() throws Exception { + public void shouldGetAlgorithm() { DecodedJWT jwt = JWT.decode("eyJhbGciOiJIUzI1NiJ9.e30.XmNK3GpH3Ys_7wsYBfq4C3M6goz71I7dTgUkuIa5lyQ"); assertThat(jwt, is(notNullValue())); assertThat(jwt.getAlgorithm(), is("HS256")); @@ -191,7 +191,7 @@ public void shouldGetAlgorithm() throws Exception { //Private PublicClaims @Test - public void shouldGetMissingClaimIfClaimDoesNotExist() throws Exception { + public void shouldGetMissingClaimIfClaimDoesNotExist() { DecodedJWT jwt = JWT.decode("eyJhbGciOiJIUzI1NiJ9.e30.K17vlwhE8FCMShdl1_65jEYqsQqBOVMPUU9IgG-QlTM"); assertThat(jwt, is(notNullValue())); assertThat(jwt.getClaim("notExisting"), is(notNullValue())); @@ -199,7 +199,7 @@ public void shouldGetMissingClaimIfClaimDoesNotExist() throws Exception { } @Test - public void shouldGetValidClaim() throws Exception { + public void shouldGetValidClaim() { DecodedJWT jwt = JWT.decode("eyJhbGciOiJIUzI1NiJ9.eyJvYmplY3QiOnsibmFtZSI6ImpvaG4ifX0.lrU1gZlOdlmTTeZwq0VI-pZx2iV46UWYd5-lCjy6-c4"); assertThat(jwt, is(notNullValue())); assertThat(jwt.getClaim("object"), is(notNullValue())); @@ -207,7 +207,7 @@ public void shouldGetValidClaim() throws Exception { } @Test - public void shouldNotGetNullClaimIfClaimIsEmptyObject() throws Exception { + public void shouldNotGetNullClaimIfClaimIsEmptyObject() { DecodedJWT jwt = JWT.decode("eyJhbGciOiJIUzI1NiJ9.eyJvYmplY3QiOnt9fQ.d3nUeeL_69QsrHL0ZWij612LHEQxD8EZg1rNoY3a4aI"); assertThat(jwt, is(notNullValue())); assertThat(jwt.getClaim("object"), is(notNullValue())); @@ -215,7 +215,7 @@ public void shouldNotGetNullClaimIfClaimIsEmptyObject() throws Exception { } @Test - public void shouldGetCustomClaimOfTypeInteger() throws Exception { + public void shouldGetCustomClaimOfTypeInteger() { String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoxMjN9.XZAudnA7h3_Al5kJydzLjw6RzZC3Q6OvnLEYlhNW7HA"; DecodedJWT jwt = JWT.decode(token); Assert.assertThat(jwt, is(notNullValue())); @@ -223,7 +223,7 @@ public void shouldGetCustomClaimOfTypeInteger() throws Exception { } @Test - public void shouldGetCustomClaimOfTypeDouble() throws Exception { + public void shouldGetCustomClaimOfTypeDouble() { String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoyMy40NX0.7pyX2OmEGaU9q15T8bGFqRm-d3RVTYnqmZNZtxMKSlA"; DecodedJWT jwt = JWT.decode(token); Assert.assertThat(jwt, is(notNullValue())); @@ -231,7 +231,7 @@ public void shouldGetCustomClaimOfTypeDouble() throws Exception { } @Test - public void shouldGetCustomClaimOfTypeBoolean() throws Exception { + public void shouldGetCustomClaimOfTypeBoolean() { String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjp0cnVlfQ.FwQ8VfsZNRqBa9PXMinSIQplfLU4-rkCLfIlTLg_MV0"; DecodedJWT jwt = JWT.decode(token); Assert.assertThat(jwt, is(notNullValue())); @@ -239,7 +239,7 @@ public void shouldGetCustomClaimOfTypeBoolean() throws Exception { } @Test - public void shouldGetCustomClaimOfTypeDate() throws Exception { + public void shouldGetCustomClaimOfTypeDate() { String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoxNDc4ODkxNTIxfQ.mhioumeok8fghQEhTKF3QtQAksSvZ_9wIhJmgZLhJ6c"; Date date = new Date(1478891521000L); DecodedJWT jwt = JWT.decode(token); @@ -248,7 +248,7 @@ public void shouldGetCustomClaimOfTypeDate() throws Exception { } @Test - public void shouldGetCustomArrayClaimOfTypeString() throws Exception { + public void shouldGetCustomArrayClaimOfTypeString() { String token = "eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjpbInRleHQiLCIxMjMiLCJ0cnVlIl19.lxM8EcmK1uSZRAPd0HUhXGZJdauRmZmLjoeqz4J9yAA"; DecodedJWT jwt = JWT.decode(token); Assert.assertThat(jwt, is(notNullValue())); @@ -256,7 +256,7 @@ public void shouldGetCustomArrayClaimOfTypeString() throws Exception { } @Test - public void shouldGetCustomArrayClaimOfTypeInteger() throws Exception { + public void shouldGetCustomArrayClaimOfTypeInteger() { String token = "eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjpbMSwyLDNdfQ.UEuMKRQYrzKAiPpPLhIVawWkKWA1zj0_GderrWUIyFE"; DecodedJWT jwt = JWT.decode(token); Assert.assertThat(jwt, is(notNullValue())); @@ -264,18 +264,18 @@ public void shouldGetCustomArrayClaimOfTypeInteger() throws Exception { } @Test - public void shouldGetCustomMapClaim() throws Exception { + public void shouldGetCustomMapClaim() { String token = "eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjp7InN0cmluZyI6InZhbHVlIiwibnVtYmVyIjoxLCJib29sZWFuIjp0cnVlfX0.-8aIaXd2-rp1lLuDEQmCeisCBX9X_zbqdPn2llGxNoc"; DecodedJWT jwt = JWT.decode(token); Assert.assertThat(jwt, is(notNullValue())); Map map = jwt.getClaim("name").asMap(); - Assert.assertThat(map, hasEntry("string", (Object) "value")); - Assert.assertThat(map, hasEntry("number", (Object) 1)); - Assert.assertThat(map, hasEntry("boolean", (Object) true)); + Assert.assertThat(map, hasEntry("string", "value")); + Assert.assertThat(map, hasEntry("number", 1)); + Assert.assertThat(map, hasEntry("boolean", true)); } @Test - public void shouldGetAvailableClaims() throws Exception { + public void shouldGetAvailableClaims() { DecodedJWT jwt = JWT.decode("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjEyMzQ1Njc4OTAsImlhdCI6MTIzNDU2Nzg5MCwibmJmIjoxMjM0NTY3ODkwLCJqdGkiOiJodHRwczovL2p3dC5pby8iLCJhdWQiOiJodHRwczovL2RvbWFpbi5hdXRoMC5jb20iLCJzdWIiOiJsb2dpbiIsImlzcyI6ImF1dGgwIiwiZXh0cmFDbGFpbSI6IkpvaG4gRG9lIn0.2_0nxDPJwOk64U5V5V9pt8U92jTPJbGsHYQ35HYhbdE"); assertThat(jwt, is(notNullValue())); assertThat(jwt.getClaims(), is(notNullValue())); @@ -339,4 +339,4 @@ private static Object deserialize(byte[] bytes) throws IOException, ClassNotFoun return o.readObject(); } -} \ No newline at end of file +} diff --git a/lib/src/test/java/com/auth0/jwt/JWTTest.java b/lib/src/test/java/com/auth0/jwt/JWTTest.java index 7cbe4b22..c90c01ad 100644 --- a/lib/src/test/java/com/auth0/jwt/JWTTest.java +++ b/lib/src/test/java/com/auth0/jwt/JWTTest.java @@ -21,7 +21,6 @@ import static org.mockito.Mockito.when; public class JWTTest { - private static final String PUBLIC_KEY_FILE_RSA = "src/test/resources/rsa-public.pem"; private static final String PRIVATE_KEY_FILE_RSA = "src/test/resources/rsa-private.pem"; @@ -39,7 +38,7 @@ public class JWTTest { // Decode @Test - public void shouldDecodeAStringToken() throws Exception { + public void shouldDecodeAStringToken() { String token = "eyJhbGciOiJIUzI1NiIsImN0eSI6IkpXVCJ9.eyJpc3MiOiJhdXRoMCJ9.mZ0m_N1J4PgeqWmi903JuUoDRZDBPB7HwkS4nVyWH1M"; DecodedJWT jwt = JWT.decode(token); @@ -47,7 +46,7 @@ public void shouldDecodeAStringToken() throws Exception { } @Test - public void shouldDecodeAStringTokenUsingInstance() throws Exception { + public void shouldDecodeAStringTokenUsingInstance() { String token = "eyJhbGciOiJIUzI1NiIsImN0eSI6IkpXVCJ9.eyJpc3MiOiJhdXRoMCJ9.mZ0m_N1J4PgeqWmi903JuUoDRZDBPB7HwkS4nVyWH1M"; JWT jwt = new JWT(); DecodedJWT decodedJWT = jwt.decodeJwt(token); @@ -57,7 +56,7 @@ public void shouldDecodeAStringTokenUsingInstance() throws Exception { // getToken @Test - public void shouldGetStringToken() throws Exception { + public void shouldGetStringToken() { DecodedJWT jwt = JWT.decode("eyJhbGciOiJIUzI1NiJ9.e30.XmNK3GpH3Ys_7wsYBfq4C3M6goz71I7dTgUkuIa5lyQ"); assertThat(jwt, is(notNullValue())); assertThat(jwt.getToken(), is(notNullValue())); @@ -66,7 +65,7 @@ public void shouldGetStringToken() throws Exception { // getToken @Test - public void shouldGetStringTokenUsingInstance() throws Exception { + public void shouldGetStringTokenUsingInstance() { JWT jwt = new JWT(); DecodedJWT decodedJWT = jwt.decodeJwt("eyJhbGciOiJIUzI1NiJ9.e30.XmNK3GpH3Ys_7wsYBfq4C3M6goz71I7dTgUkuIa5lyQ"); assertThat(decodedJWT, is(notNullValue())); @@ -89,7 +88,7 @@ public void shouldVerifyDecodedToken() throws Exception { } @Test - public void shouldAcceptNoneAlgorithm() throws Exception { + public void shouldAcceptNoneAlgorithm() { String token = "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJhdXRoMCJ9."; DecodedJWT jwt = JWT.require(Algorithm.none()) .build() @@ -99,7 +98,7 @@ public void shouldAcceptNoneAlgorithm() throws Exception { } @Test - public void shouldAcceptHMAC256Algorithm() throws Exception { + public void shouldAcceptHMAC256Algorithm() { String token = "eyJhbGciOiJIUzI1NiIsImN0eSI6IkpXVCJ9.eyJpc3MiOiJhdXRoMCJ9.mZ0m_N1J4PgeqWmi903JuUoDRZDBPB7HwkS4nVyWH1M"; DecodedJWT jwt = JWT.require(Algorithm.HMAC256("secret")) .build() @@ -109,7 +108,7 @@ public void shouldAcceptHMAC256Algorithm() throws Exception { } @Test - public void shouldAcceptHMAC384Algorithm() throws Exception { + public void shouldAcceptHMAC384Algorithm() { String token = "eyJhbGciOiJIUzM4NCIsImN0eSI6IkpXVCJ9.eyJpc3MiOiJhdXRoMCJ9.uztpK_wUMYJhrRv8SV-1LU4aPnwl-EM1q-wJnqgyb5DHoDteP6lN_gE1xnZJH5vw"; DecodedJWT jwt = JWT.require(Algorithm.HMAC384("secret")) .build() @@ -119,7 +118,7 @@ public void shouldAcceptHMAC384Algorithm() throws Exception { } @Test - public void shouldAcceptHMAC512Algorithm() throws Exception { + public void shouldAcceptHMAC512Algorithm() { String token = "eyJhbGciOiJIUzUxMiIsImN0eSI6IkpXVCJ9.eyJpc3MiOiJhdXRoMCJ9.VUo2Z9SWDV-XcOc_Hr6Lff3vl7L9e5Vb8ThXpmGDFjHxe3Dr1ZBmUChYF-xVA7cAdX1P_D4ZCUcsv3IefpVaJw"; DecodedJWT jwt = JWT.require(Algorithm.HMAC512("secret")) .build() @@ -197,11 +196,10 @@ public void shouldAcceptECDSA512Algorithm() throws Exception { assertThat(jwt, is(notNullValue())); } - // Public Claims @Test - public void shouldGetAlgorithm() throws Exception { + public void shouldGetAlgorithm() { String token = "eyJhbGciOiJIUzI1NiJ9.e30.XmNK3GpH3Ys_7wsYBfq4C3M6goz71I7dTgUkuIa5lyQ"; DecodedJWT jwt = JWT.require(Algorithm.HMAC256("secret")) .build() @@ -212,7 +210,7 @@ public void shouldGetAlgorithm() throws Exception { } @Test - public void shouldGetSignature() throws Exception { + public void shouldGetSignature() { String token = "eyJhbGciOiJIUzI1NiJ9.e30.XmNK3GpH3Ys_7wsYBfq4C3M6goz71I7dTgUkuIa5lyQ"; DecodedJWT jwt = JWT.require(Algorithm.HMAC256("secret")) .build() @@ -223,7 +221,7 @@ public void shouldGetSignature() throws Exception { } @Test - public void shouldGetIssuer() throws Exception { + public void shouldGetIssuer() { String token = "eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJKb2huIERvZSJ9.SgXosfRR_IwCgHq5lF3tlM-JHtpucWCRSaVuoHTbWbQ"; DecodedJWT jwt = JWT.require(Algorithm.HMAC256("secret")) .build() @@ -234,7 +232,7 @@ public void shouldGetIssuer() throws Exception { } @Test - public void shouldGetSubject() throws Exception { + public void shouldGetSubject() { String token = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJUb2szbnMifQ.RudAxkslimoOY3BLl2Ghny3BrUKu9I1ZrXzCZGDJtNs"; DecodedJWT jwt = JWT.require(Algorithm.HMAC256("secret")) .build() @@ -245,7 +243,7 @@ public void shouldGetSubject() throws Exception { } @Test - public void shouldGetArrayAudience() throws Exception { + public void shouldGetArrayAudience() { String token = "eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOlsiSG9wZSIsIlRyYXZpcyIsIlNvbG9tb24iXX0.Tm4W8WnfPjlmHSmKFakdij0on2rWPETpoM7Sh0u6-S4"; DecodedJWT jwt = JWT.require(Algorithm.HMAC256("secret")) .build() @@ -257,7 +255,7 @@ public void shouldGetArrayAudience() throws Exception { } @Test - public void shouldGetStringAudience() throws Exception { + public void shouldGetStringAudience() { String token = "eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJKYWNrIFJleWVzIn0.a4I9BBhPt1OB1GW67g2P1bEHgi6zgOjGUL4LvhE9Dgc"; DecodedJWT jwt = JWT.require(Algorithm.HMAC256("secret")) .build() @@ -269,7 +267,7 @@ public void shouldGetStringAudience() throws Exception { } @Test - public void shouldGetExpirationTime() throws Exception { + public void shouldGetExpirationTime() { Date expectedDate = new Date(1477592 * 1000); Clock clock = mock(Clock.class); when(clock.getToday()).thenReturn(expectedDate); @@ -287,7 +285,7 @@ public void shouldGetExpirationTime() throws Exception { } @Test - public void shouldGetNotBefore() throws Exception { + public void shouldGetNotBefore() { Date expectedDate = new Date(1477592 * 1000); Clock clock = mock(Clock.class); when(clock.getToday()).thenReturn(expectedDate); @@ -305,7 +303,7 @@ public void shouldGetNotBefore() throws Exception { } @Test - public void shouldGetIssuedAt() throws Exception { + public void shouldGetIssuedAt() { Date expectedDate = new Date(1477592 * 1000); Clock clock = mock(Clock.class); when(clock.getToday()).thenReturn(expectedDate); @@ -323,7 +321,7 @@ public void shouldGetIssuedAt() throws Exception { } @Test - public void shouldGetId() throws Exception { + public void shouldGetId() { String token = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxMjM0NTY3ODkwIn0.m3zgEfVUFOd-CvL3xG5BuOWLzb0zMQZCqiVNQQOPOvA"; JWTVerifier.BaseVerification verification = (JWTVerifier.BaseVerification) JWT.require(Algorithm.HMAC256("secret")); DecodedJWT jwt = verification @@ -335,7 +333,7 @@ public void shouldGetId() throws Exception { } @Test - public void shouldGetContentType() throws Exception { + public void shouldGetContentType() { String token = "eyJhbGciOiJIUzI1NiIsImN0eSI6ImF3ZXNvbWUifQ.e30.AIm-pJDOaAyct9qKMlN-lQieqNDqc3d4erqUZc5SHAs"; DecodedJWT jwt = JWT.require(Algorithm.HMAC256("secret")) .build() @@ -346,7 +344,7 @@ public void shouldGetContentType() throws Exception { } @Test - public void shouldGetType() throws Exception { + public void shouldGetType() { String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXUyJ9.e30.WdFmrzx8b9v_a-r6EHC2PTAaWywgm_8LiP8RBRhYwkI"; DecodedJWT jwt = JWT.require(Algorithm.HMAC256("secret")) .build() @@ -357,7 +355,7 @@ public void shouldGetType() throws Exception { } @Test - public void shouldGetKeyId() throws Exception { + public void shouldGetKeyId() { String token = "eyJhbGciOiJIUzI1NiIsImtpZCI6ImtleSJ9.e30.von1Vt9tq9cn5ZYdX1f4cf2EE7fUvb5BCBlKOTm9YWs"; DecodedJWT jwt = JWT.require(Algorithm.HMAC256("secret")) .build() @@ -368,7 +366,7 @@ public void shouldGetKeyId() throws Exception { } @Test - public void shouldGetCustomClaims() throws Exception { + public void shouldGetCustomClaims() { String token = "eyJhbGciOiJIUzI1NiIsImlzQWRtaW4iOnRydWV9.eyJpc0FkbWluIjoibm9wZSJ9.YDKBAgUDbh0PkhioDcLNzdQ8c2Gdf_yS6zdEtJQS3F0"; DecodedJWT jwt = JWT.require(Algorithm.HMAC256("secret")) .build() @@ -384,7 +382,7 @@ public void shouldGetCustomClaims() throws Exception { // Sign @Test - public void shouldCreateAnEmptyHMAC256SignedToken() throws Exception { + public void shouldCreateAnEmptyHMAC256SignedToken() { String signed = JWT.create().sign(Algorithm.HMAC256("secret")); assertThat(signed, is(notNullValue())); @@ -400,7 +398,7 @@ public void shouldCreateAnEmptyHMAC256SignedToken() throws Exception { } @Test - public void shouldCreateAnEmptyHMAC384SignedToken() throws Exception { + public void shouldCreateAnEmptyHMAC384SignedToken() { String signed = JWT.create().sign(Algorithm.HMAC384("secret")); assertThat(signed, is(notNullValue())); @@ -416,7 +414,7 @@ public void shouldCreateAnEmptyHMAC384SignedToken() throws Exception { } @Test - public void shouldCreateAnEmptyHMAC512SignedToken() throws Exception { + public void shouldCreateAnEmptyHMAC512SignedToken() { String signed = JWT.create().sign(Algorithm.HMAC512("secret")); assertThat(signed, is(notNullValue())); diff --git a/lib/src/test/java/com/auth0/jwt/JWTVerifierTest.java b/lib/src/test/java/com/auth0/jwt/JWTVerifierTest.java index e297c59c..f77cf22a 100644 --- a/lib/src/test/java/com/auth0/jwt/JWTVerifierTest.java +++ b/lib/src/test/java/com/auth0/jwt/JWTVerifierTest.java @@ -20,20 +20,19 @@ import static org.mockito.Mockito.when; public class JWTVerifierTest { - private static final long DATE_TOKEN_MS_VALUE = 1477592 * 1000; @Rule public ExpectedException exception = ExpectedException.none(); @Test - public void shouldThrowWhenInitializedWithoutAlgorithm() throws Exception { + public void shouldThrowWhenInitializedWithoutAlgorithm() { exception.expect(IllegalArgumentException.class); exception.expectMessage("The Algorithm cannot be null"); JWTVerifier.init(null); } @Test - public void shouldThrowWhenAlgorithmDoesntMatchTheTokensAlgorithm() throws Exception { + public void shouldThrowWhenAlgorithmDoesntMatchTheTokensAlgorithm() { exception.expect(AlgorithmMismatchException.class); exception.expectMessage("The provided Algorithm doesn't match the one defined in the JWT's Header."); JWTVerifier verifier = JWTVerifier.init(Algorithm.HMAC512("secret")).build(); @@ -41,7 +40,7 @@ public void shouldThrowWhenAlgorithmDoesntMatchTheTokensAlgorithm() throws Excep } @Test - public void shouldValidateIssuer() throws Exception { + public void shouldValidateIssuer() { String token = "eyJhbGciOiJIUzI1NiIsImN0eSI6IkpXVCJ9.eyJpc3MiOiJhdXRoMCJ9.mZ0m_N1J4PgeqWmi903JuUoDRZDBPB7HwkS4nVyWH1M"; DecodedJWT jwt = JWTVerifier.init(Algorithm.HMAC256("secret")) .withIssuer("auth0") @@ -64,7 +63,7 @@ public void shouldValidateMultipleIssuers() { } @Test - public void shouldThrowOnInvalidIssuer() throws Exception { + public void shouldThrowOnInvalidIssuer() { exception.expect(InvalidClaimException.class); exception.expectMessage("The Claim 'iss' value doesn't match the required issuer."); String token = "eyJhbGciOiJIUzI1NiIsImN0eSI6IkpXVCJ9.eyJpc3MiOiJhdXRoMCJ9.mZ0m_N1J4PgeqWmi903JuUoDRZDBPB7HwkS4nVyWH1M"; @@ -75,7 +74,7 @@ public void shouldThrowOnInvalidIssuer() throws Exception { } @Test - public void shouldThrowOnNullIssuer() throws Exception { + public void shouldThrowOnNullIssuer() { exception.expect(InvalidClaimException.class); exception.expectMessage("The Claim 'iss' value doesn't match the required issuer."); @@ -87,7 +86,7 @@ public void shouldThrowOnNullIssuer() throws Exception { } @Test - public void shouldValidateSubject() throws Exception { + public void shouldValidateSubject() { String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.Rq8IxqeX7eA6GgYxlcHdPFVRNFFZc5rEI3MQTZZbK3I"; DecodedJWT jwt = JWTVerifier.init(Algorithm.HMAC256("secret")) .withSubject("1234567890") @@ -98,7 +97,7 @@ public void shouldValidateSubject() throws Exception { } @Test - public void shouldThrowOnInvalidSubject() throws Exception { + public void shouldThrowOnInvalidSubject() { exception.expect(InvalidClaimException.class); exception.expectMessage("The Claim 'sub' value doesn't match the required one."); String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.Rq8IxqeX7eA6GgYxlcHdPFVRNFFZc5rEI3MQTZZbK3I"; @@ -109,7 +108,7 @@ public void shouldThrowOnInvalidSubject() throws Exception { } @Test - public void shouldValidateAudience() throws Exception { + public void shouldValidateAudience() { String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJNYXJrIn0.xWB6czYI0XObbVhLAxe55TwChWZg7zO08RxONWU2iY4"; DecodedJWT jwt = JWTVerifier.init(Algorithm.HMAC256("secret")) .withAudience("Mark") @@ -128,7 +127,7 @@ public void shouldValidateAudience() throws Exception { } @Test - public void shouldAcceptPartialAudience() throws Exception { + public void shouldAcceptPartialAudience() { //Token 'aud' = ["Mark", "David", "John"] String tokenArr = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiTWFyayIsIkRhdmlkIiwiSm9obiJdfQ.DX5xXiCaYvr54x_iL0LZsJhK7O6HhAdHeDYkgDeb0Rw"; DecodedJWT jwtArr = JWTVerifier.init(Algorithm.HMAC256("secret")) @@ -140,7 +139,7 @@ public void shouldAcceptPartialAudience() throws Exception { } @Test - public void shouldThrowOnInvalidAudience() throws Exception { + public void shouldThrowOnInvalidAudience() { exception.expect(InvalidClaimException.class); exception.expectMessage("The Claim 'aud' value doesn't contain the required audience."); String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.Rq8IxqeX7eA6GgYxlcHdPFVRNFFZc5rEI3MQTZZbK3I"; @@ -151,7 +150,7 @@ public void shouldThrowOnInvalidAudience() throws Exception { } @Test - public void shouldRemoveAudienceWhenPassingNull() throws Exception { + public void shouldRemoveAudienceWhenPassingNull() { Algorithm algorithm = mock(Algorithm.class); JWTVerifier verifier = JWTVerifier.init(algorithm) .withAudience("John") @@ -163,7 +162,7 @@ public void shouldRemoveAudienceWhenPassingNull() throws Exception { } @Test - public void shouldThrowOnNullCustomClaimName() throws Exception { + public void shouldThrowOnNullCustomClaimName() { exception.expect(IllegalArgumentException.class); exception.expectMessage("The Custom Claim's name can't be null."); JWTVerifier.init(Algorithm.HMAC256("secret")) @@ -171,7 +170,7 @@ public void shouldThrowOnNullCustomClaimName() throws Exception { } @Test - public void shouldThrowOnInvalidCustomClaimValueOfTypeString() throws Exception { + public void shouldThrowOnInvalidCustomClaimValueOfTypeString() { exception.expect(InvalidClaimException.class); exception.expectMessage("The Claim 'name' value doesn't match the required one."); String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjpbInNvbWV0aGluZyJdfQ.3ENLez6tU_fG0SVFrGmISltZPiXLSHaz_dyn-XFTEGQ"; @@ -182,7 +181,7 @@ public void shouldThrowOnInvalidCustomClaimValueOfTypeString() throws Exception } @Test - public void shouldThrowOnInvalidCustomClaimValueOfTypeInteger() throws Exception { + public void shouldThrowOnInvalidCustomClaimValueOfTypeInteger() { exception.expect(InvalidClaimException.class); exception.expectMessage("The Claim 'name' value doesn't match the required one."); String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjpbInNvbWV0aGluZyJdfQ.3ENLez6tU_fG0SVFrGmISltZPiXLSHaz_dyn-XFTEGQ"; @@ -193,7 +192,7 @@ public void shouldThrowOnInvalidCustomClaimValueOfTypeInteger() throws Exception } @Test - public void shouldThrowOnInvalidCustomClaimValueOfTypeDouble() throws Exception { + public void shouldThrowOnInvalidCustomClaimValueOfTypeDouble() { exception.expect(InvalidClaimException.class); exception.expectMessage("The Claim 'name' value doesn't match the required one."); String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjpbInNvbWV0aGluZyJdfQ.3ENLez6tU_fG0SVFrGmISltZPiXLSHaz_dyn-XFTEGQ"; @@ -204,7 +203,7 @@ public void shouldThrowOnInvalidCustomClaimValueOfTypeDouble() throws Exception } @Test - public void shouldThrowOnInvalidCustomClaimValueOfTypeBoolean() throws Exception { + public void shouldThrowOnInvalidCustomClaimValueOfTypeBoolean() { exception.expect(InvalidClaimException.class); exception.expectMessage("The Claim 'name' value doesn't match the required one."); String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjpbInNvbWV0aGluZyJdfQ.3ENLez6tU_fG0SVFrGmISltZPiXLSHaz_dyn-XFTEGQ"; @@ -216,7 +215,7 @@ public void shouldThrowOnInvalidCustomClaimValueOfTypeBoolean() throws Exception @Test - public void shouldThrowOnInvalidCustomClaimValueOfTypeDate() throws Exception { + public void shouldThrowOnInvalidCustomClaimValueOfTypeDate() { exception.expect(InvalidClaimException.class); exception.expectMessage("The Claim 'name' value doesn't match the required one."); String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjpbInNvbWV0aGluZyJdfQ.3ENLez6tU_fG0SVFrGmISltZPiXLSHaz_dyn-XFTEGQ"; @@ -227,7 +226,7 @@ public void shouldThrowOnInvalidCustomClaimValueOfTypeDate() throws Exception { } @Test - public void shouldThrowOnInvalidCustomClaimValue() throws Exception { + public void shouldThrowOnInvalidCustomClaimValue() { exception.expect(InvalidClaimException.class); exception.expectMessage("The Claim 'name' value doesn't match the required one."); String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjpbInNvbWV0aGluZyJdfQ.3ENLez6tU_fG0SVFrGmISltZPiXLSHaz_dyn-XFTEGQ"; @@ -238,7 +237,7 @@ public void shouldThrowOnInvalidCustomClaimValue() throws Exception { } @Test - public void shouldValidateCustomClaimOfTypeString() throws Exception { + public void shouldValidateCustomClaimOfTypeString() { String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoidmFsdWUifQ.Jki8pvw6KGbxpMinufrgo6RDL1cu7AtNMJYVh6t-_cE"; DecodedJWT jwt = JWTVerifier.init(Algorithm.HMAC256("secret")) .withClaim("name", "value") @@ -249,7 +248,7 @@ public void shouldValidateCustomClaimOfTypeString() throws Exception { } @Test - public void shouldValidateCustomClaimOfTypeInteger() throws Exception { + public void shouldValidateCustomClaimOfTypeInteger() { String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoxMjN9.XZAudnA7h3_Al5kJydzLjw6RzZC3Q6OvnLEYlhNW7HA"; DecodedJWT jwt = JWTVerifier.init(Algorithm.HMAC256("secret")) .withClaim("name", 123) @@ -260,7 +259,7 @@ public void shouldValidateCustomClaimOfTypeInteger() throws Exception { } @Test - public void shouldValidateCustomClaimOfTypeLong() throws Exception { + public void shouldValidateCustomClaimOfTypeLong() { String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjo5MjIzMzcyMDM2ODU0Nzc2MDB9.km-IwQ5IDnTZFmuJzhSgvjTzGkn_Z5X29g4nAuVC56I"; DecodedJWT jwt = JWTVerifier.init(Algorithm.HMAC256("secret")) .withClaim("name", 922337203685477600L) @@ -271,7 +270,7 @@ public void shouldValidateCustomClaimOfTypeLong() throws Exception { } @Test - public void shouldValidateCustomClaimOfTypeDouble() throws Exception { + public void shouldValidateCustomClaimOfTypeDouble() { String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoyMy40NX0.7pyX2OmEGaU9q15T8bGFqRm-d3RVTYnqmZNZtxMKSlA"; DecodedJWT jwt = JWTVerifier.init(Algorithm.HMAC256("secret")) .withClaim("name", 23.45) @@ -282,7 +281,7 @@ public void shouldValidateCustomClaimOfTypeDouble() throws Exception { } @Test - public void shouldValidateCustomClaimOfTypeBoolean() throws Exception { + public void shouldValidateCustomClaimOfTypeBoolean() { String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjp0cnVlfQ.FwQ8VfsZNRqBa9PXMinSIQplfLU4-rkCLfIlTLg_MV0"; DecodedJWT jwt = JWTVerifier.init(Algorithm.HMAC256("secret")) .withClaim("name", true) @@ -293,7 +292,7 @@ public void shouldValidateCustomClaimOfTypeBoolean() throws Exception { } @Test - public void shouldValidateCustomClaimOfTypeDate() throws Exception { + public void shouldValidateCustomClaimOfTypeDate() { String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoxNDc4ODkxNTIxfQ.mhioumeok8fghQEhTKF3QtQAksSvZ_9wIhJmgZLhJ6c"; Date date = new Date(1478891521000L); DecodedJWT jwt = JWTVerifier.init(Algorithm.HMAC256("secret")) @@ -305,7 +304,7 @@ public void shouldValidateCustomClaimOfTypeDate() throws Exception { } @Test - public void shouldValidateCustomArrayClaimOfTypeString() throws Exception { + public void shouldValidateCustomArrayClaimOfTypeString() { String token = "eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjpbInRleHQiLCIxMjMiLCJ0cnVlIl19.lxM8EcmK1uSZRAPd0HUhXGZJdauRmZmLjoeqz4J9yAA"; DecodedJWT jwt = JWTVerifier.init(Algorithm.HMAC256("secret")) .withArrayClaim("name", "text", "123", "true") @@ -316,7 +315,7 @@ public void shouldValidateCustomArrayClaimOfTypeString() throws Exception { } @Test - public void shouldValidateCustomArrayClaimOfTypeInteger() throws Exception { + public void shouldValidateCustomArrayClaimOfTypeInteger() { String token = "eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjpbMSwyLDNdfQ.UEuMKRQYrzKAiPpPLhIVawWkKWA1zj0_GderrWUIyFE"; DecodedJWT jwt = JWTVerifier.init(Algorithm.HMAC256("secret")) .withArrayClaim("name", 1, 2, 3) @@ -329,7 +328,7 @@ public void shouldValidateCustomArrayClaimOfTypeInteger() throws Exception { // Generic Delta @SuppressWarnings("RedundantCast") @Test - public void shouldAddDefaultLeewayToDateClaims() throws Exception { + public void shouldAddDefaultLeewayToDateClaims() { Algorithm algorithm = mock(Algorithm.class); JWTVerifier verifier = JWTVerifier.init(algorithm) .build(); @@ -342,7 +341,7 @@ public void shouldAddDefaultLeewayToDateClaims() throws Exception { @SuppressWarnings("RedundantCast") @Test - public void shouldAddCustomLeewayToDateClaims() throws Exception { + public void shouldAddCustomLeewayToDateClaims() { Algorithm algorithm = mock(Algorithm.class); JWTVerifier verifier = JWTVerifier.init(algorithm) .acceptLeeway(1234L) @@ -356,7 +355,7 @@ public void shouldAddCustomLeewayToDateClaims() throws Exception { @SuppressWarnings("RedundantCast") @Test - public void shouldOverrideDefaultIssuedAtLeeway() throws Exception { + public void shouldOverrideDefaultIssuedAtLeeway() { Algorithm algorithm = mock(Algorithm.class); JWTVerifier verifier = JWTVerifier.init(algorithm) .acceptLeeway(1234L) @@ -371,7 +370,7 @@ public void shouldOverrideDefaultIssuedAtLeeway() throws Exception { @SuppressWarnings("RedundantCast") @Test - public void shouldOverrideDefaultExpiresAtLeeway() throws Exception { + public void shouldOverrideDefaultExpiresAtLeeway() { Algorithm algorithm = mock(Algorithm.class); JWTVerifier verifier = JWTVerifier.init(algorithm) .acceptLeeway(1234L) @@ -386,7 +385,7 @@ public void shouldOverrideDefaultExpiresAtLeeway() throws Exception { @SuppressWarnings("RedundantCast") @Test - public void shouldOverrideDefaultNotBeforeLeeway() throws Exception { + public void shouldOverrideDefaultNotBeforeLeeway() { Algorithm algorithm = mock(Algorithm.class); JWTVerifier verifier = JWTVerifier.init(algorithm) .acceptLeeway(1234L) @@ -400,7 +399,7 @@ public void shouldOverrideDefaultNotBeforeLeeway() throws Exception { } @Test - public void shouldThrowOnNegativeCustomLeeway() throws Exception { + public void shouldThrowOnNegativeCustomLeeway() { exception.expect(IllegalArgumentException.class); exception.expectMessage("Leeway value can't be negative."); Algorithm algorithm = mock(Algorithm.class); @@ -410,7 +409,7 @@ public void shouldThrowOnNegativeCustomLeeway() throws Exception { // Expires At @Test - public void shouldValidateExpiresAtWithLeeway() throws Exception { + public void shouldValidateExpiresAtWithLeeway() { Clock clock = mock(Clock.class); when(clock.getToday()).thenReturn(new Date(DATE_TOKEN_MS_VALUE + 1000)); @@ -425,7 +424,7 @@ public void shouldValidateExpiresAtWithLeeway() throws Exception { } @Test - public void shouldValidateExpiresAtIfPresent() throws Exception { + public void shouldValidateExpiresAtIfPresent() { Clock clock = mock(Clock.class); when(clock.getToday()).thenReturn(new Date(DATE_TOKEN_MS_VALUE)); @@ -439,7 +438,7 @@ public void shouldValidateExpiresAtIfPresent() throws Exception { } @Test - public void shouldThrowOnInvalidExpiresAtIfPresent() throws Exception { + public void shouldThrowOnInvalidExpiresAtIfPresent() { exception.expect(TokenExpiredException.class); exception.expectMessage(startsWith("The Token has expired on")); Clock clock = mock(Clock.class); @@ -453,7 +452,7 @@ public void shouldThrowOnInvalidExpiresAtIfPresent() throws Exception { } @Test - public void shouldThrowOnNegativeExpiresAtLeeway() throws Exception { + public void shouldThrowOnNegativeExpiresAtLeeway() { exception.expect(IllegalArgumentException.class); exception.expectMessage("Leeway value can't be negative."); Algorithm algorithm = mock(Algorithm.class); @@ -463,7 +462,7 @@ public void shouldThrowOnNegativeExpiresAtLeeway() throws Exception { // Not before @Test - public void shouldValidateNotBeforeWithLeeway() throws Exception { + public void shouldValidateNotBeforeWithLeeway() { Clock clock = mock(Clock.class); when(clock.getToday()).thenReturn(new Date(DATE_TOKEN_MS_VALUE - 1000)); @@ -478,7 +477,7 @@ public void shouldValidateNotBeforeWithLeeway() throws Exception { } @Test - public void shouldThrowOnInvalidNotBeforeIfPresent() throws Exception { + public void shouldThrowOnInvalidNotBeforeIfPresent() { exception.expect(InvalidClaimException.class); exception.expectMessage(startsWith("The Token can't be used before")); Clock clock = mock(Clock.class); @@ -492,7 +491,7 @@ public void shouldThrowOnInvalidNotBeforeIfPresent() throws Exception { } @Test - public void shouldValidateNotBeforeIfPresent() throws Exception { + public void shouldValidateNotBeforeIfPresent() { Clock clock = mock(Clock.class); when(clock.getToday()).thenReturn(new Date(DATE_TOKEN_MS_VALUE)); @@ -506,7 +505,7 @@ public void shouldValidateNotBeforeIfPresent() throws Exception { } @Test - public void shouldThrowOnNegativeNotBeforeLeeway() throws Exception { + public void shouldThrowOnNegativeNotBeforeLeeway() { exception.expect(IllegalArgumentException.class); exception.expectMessage("Leeway value can't be negative."); Algorithm algorithm = mock(Algorithm.class); @@ -516,7 +515,7 @@ public void shouldThrowOnNegativeNotBeforeLeeway() throws Exception { // Issued At with future date @Test (expected = InvalidClaimException.class) - public void shouldThrowOnFutureIssuedAt() throws Exception { + public void shouldThrowOnFutureIssuedAt() { Clock clock = mock(Clock.class); when(clock.getToday()).thenReturn(new Date(DATE_TOKEN_MS_VALUE - 1000)); @@ -529,7 +528,7 @@ public void shouldThrowOnFutureIssuedAt() throws Exception { // Issued At with future date and ignore flag @Test - public void shouldSkipIssuedAtVerificationWhenFlagIsPassed() throws Exception { + public void shouldSkipIssuedAtVerificationWhenFlagIsPassed() { Clock clock = mock(Clock.class); when(clock.getToday()).thenReturn(new Date(DATE_TOKEN_MS_VALUE - 1000)); @@ -542,7 +541,7 @@ public void shouldSkipIssuedAtVerificationWhenFlagIsPassed() throws Exception { } @Test - public void shouldThrowOnInvalidIssuedAtIfPresent() throws Exception { + public void shouldThrowOnInvalidIssuedAtIfPresent() { exception.expect(InvalidClaimException.class); exception.expectMessage(startsWith("The Token can't be used before")); Clock clock = mock(Clock.class); @@ -556,7 +555,7 @@ public void shouldThrowOnInvalidIssuedAtIfPresent() throws Exception { } @Test - public void shouldOverrideAcceptIssuedAtWhenIgnoreIssuedAtFlagPassedAndSkipTheVerification() throws Exception { + public void shouldOverrideAcceptIssuedAtWhenIgnoreIssuedAtFlagPassedAndSkipTheVerification() { Clock clock = mock(Clock.class); when(clock.getToday()).thenReturn(new Date(DATE_TOKEN_MS_VALUE - 1000)); @@ -570,7 +569,7 @@ public void shouldOverrideAcceptIssuedAtWhenIgnoreIssuedAtFlagPassedAndSkipTheVe } @Test - public void shouldValidateIssuedAtIfPresent() throws Exception { + public void shouldValidateIssuedAtIfPresent() { Clock clock = mock(Clock.class); when(clock.getToday()).thenReturn(new Date(DATE_TOKEN_MS_VALUE)); @@ -584,7 +583,7 @@ public void shouldValidateIssuedAtIfPresent() throws Exception { } @Test - public void shouldThrowOnNegativeIssuedAtLeeway() throws Exception { + public void shouldThrowOnNegativeIssuedAtLeeway() { exception.expect(IllegalArgumentException.class); exception.expectMessage("Leeway value can't be negative."); Algorithm algorithm = mock(Algorithm.class); @@ -593,7 +592,7 @@ public void shouldThrowOnNegativeIssuedAtLeeway() throws Exception { } @Test - public void shouldValidateJWTId() throws Exception { + public void shouldValidateJWTId() { String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJqd3RfaWRfMTIzIn0.0kegfXUvwOYioP8PDaLMY1IlV8HOAzSVz3EGL7-jWF4"; DecodedJWT jwt = JWTVerifier.init(Algorithm.HMAC256("secret")) .withJWTId("jwt_id_123") @@ -604,7 +603,7 @@ public void shouldValidateJWTId() throws Exception { } @Test - public void shouldThrowOnInvalidJWTId() throws Exception { + public void shouldThrowOnInvalidJWTId() { exception.expect(InvalidClaimException.class); exception.expectMessage("The Claim 'jti' value doesn't match the required one."); String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJqd3RfaWRfMTIzIn0.0kegfXUvwOYioP8PDaLMY1IlV8HOAzSVz3EGL7-jWF4"; @@ -615,7 +614,7 @@ public void shouldThrowOnInvalidJWTId() throws Exception { } @Test - public void shouldRemoveClaimWhenPassingNull() throws Exception { + public void shouldRemoveClaimWhenPassingNull() { Algorithm algorithm = mock(Algorithm.class); JWTVerifier verifier = JWTVerifier.init(algorithm) .withIssuer("iss") @@ -627,7 +626,7 @@ public void shouldRemoveClaimWhenPassingNull() throws Exception { } @Test - public void shouldSkipClaimValidationsIfNoClaimsRequired() throws Exception { + public void shouldSkipClaimValidationsIfNoClaimsRequired() { String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.t-IDcSemACt8x4iTMCda8Yhe3iZaWbvV5XKSTbuAn0M"; DecodedJWT jwt = JWTVerifier.init(Algorithm.HMAC256("secret")) .build() diff --git a/lib/src/test/java/com/auth0/jwt/JsonMatcher.java b/lib/src/test/java/com/auth0/jwt/JsonMatcher.java index b09ab187..ec8bea79 100644 --- a/lib/src/test/java/com/auth0/jwt/JsonMatcher.java +++ b/lib/src/test/java/com/auth0/jwt/JsonMatcher.java @@ -10,7 +10,6 @@ import java.util.Map; public class JsonMatcher extends TypeSafeDiagnosingMatcher { - private final String entry; private final String key; private final Matcher matcher; @@ -137,4 +136,4 @@ private String mapToString(Map map) { sb.append("}"); return sb.toString(); } -} \ No newline at end of file +} diff --git a/lib/src/test/java/com/auth0/jwt/PemUtils.java b/lib/src/test/java/com/auth0/jwt/PemUtils.java index 5f026b0a..688f5e62 100644 --- a/lib/src/test/java/com/auth0/jwt/PemUtils.java +++ b/lib/src/test/java/com/auth0/jwt/PemUtils.java @@ -68,5 +68,4 @@ public static PrivateKey readPrivateKeyFromFile(String filepath, String algorith byte[] bytes = PemUtils.parsePEMFile(new File(filepath)); return PemUtils.getPrivateKey(bytes, algorithm); } - } diff --git a/lib/src/test/java/com/auth0/jwt/TokenUtilsTest.java b/lib/src/test/java/com/auth0/jwt/TokenUtilsTest.java index 4bea9b44..c4803b2a 100644 --- a/lib/src/test/java/com/auth0/jwt/TokenUtilsTest.java +++ b/lib/src/test/java/com/auth0/jwt/TokenUtilsTest.java @@ -14,7 +14,7 @@ public class TokenUtilsTest { public ExpectedException exception = ExpectedException.none(); @Test - public void shouldSplitToken() throws Exception { + public void shouldSplitToken() { String token = "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJhdXRoMCJ9.W1mx_Y0hbAMbPmfW9whT605AAcxB7REFuJiDAHk2Sdc"; String[] parts = TokenUtils.splitToken(token); @@ -26,7 +26,7 @@ public void shouldSplitToken() throws Exception { } @Test - public void shouldSplitTokenWithEmptySignature() throws Exception { + public void shouldSplitTokenWithEmptySignature() { String token = "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJhdXRoMCJ9."; String[] parts = TokenUtils.splitToken(token); @@ -38,7 +38,7 @@ public void shouldSplitTokenWithEmptySignature() throws Exception { } @Test - public void shouldThrowOnSplitTokenWithMoreThan3Parts() throws Exception { + public void shouldThrowOnSplitTokenWithMoreThan3Parts() { exception.expect(JWTDecodeException.class); exception.expectMessage("The token was expected to have 3 parts, but got 4."); String token = "this.has.four.parts"; @@ -46,7 +46,7 @@ public void shouldThrowOnSplitTokenWithMoreThan3Parts() throws Exception { } @Test - public void shouldThrowOnSplitTokenWithLessThan3Parts() throws Exception { + public void shouldThrowOnSplitTokenWithLessThan3Parts() { exception.expect(JWTDecodeException.class); exception.expectMessage("The token was expected to have 3 parts, but got 2."); String token = "two.parts"; diff --git a/lib/src/test/java/com/auth0/jwt/UserPojo.java b/lib/src/test/java/com/auth0/jwt/UserPojo.java index beb72b50..443cd9b9 100644 --- a/lib/src/test/java/com/auth0/jwt/UserPojo.java +++ b/lib/src/test/java/com/auth0/jwt/UserPojo.java @@ -39,4 +39,4 @@ public int getId() { public void setId(int id) { this.id = id; } -} \ No newline at end of file +} diff --git a/lib/src/test/java/com/auth0/jwt/algorithms/AlgorithmTest.java b/lib/src/test/java/com/auth0/jwt/algorithms/AlgorithmTest.java index f4f437f9..e76b8425 100644 --- a/lib/src/test/java/com/auth0/jwt/algorithms/AlgorithmTest.java +++ b/lib/src/test/java/com/auth0/jwt/algorithms/AlgorithmTest.java @@ -19,13 +19,11 @@ import static org.mockito.Mockito.when; public class AlgorithmTest { - @Rule public ExpectedException exception = ExpectedException.none(); - @Test - public void shouldThrowHMAC256InstanceWithNullSecretBytes() throws Exception { + public void shouldThrowHMAC256InstanceWithNullSecretBytes() { exception.expect(IllegalArgumentException.class); exception.expectMessage("The Secret cannot be null"); byte[] secret = null; @@ -33,7 +31,7 @@ public void shouldThrowHMAC256InstanceWithNullSecretBytes() throws Exception { } @Test - public void shouldThrowHMAC384InstanceWithNullSecretBytes() throws Exception { + public void shouldThrowHMAC384InstanceWithNullSecretBytes() { exception.expect(IllegalArgumentException.class); exception.expectMessage("The Secret cannot be null"); byte[] secret = null; @@ -41,7 +39,7 @@ public void shouldThrowHMAC384InstanceWithNullSecretBytes() throws Exception { } @Test - public void shouldThrowHMAC512InstanceWithNullSecretBytes() throws Exception { + public void shouldThrowHMAC512InstanceWithNullSecretBytes() { exception.expect(IllegalArgumentException.class); exception.expectMessage("The Secret cannot be null"); byte[] secret = null; @@ -49,7 +47,7 @@ public void shouldThrowHMAC512InstanceWithNullSecretBytes() throws Exception { } @Test - public void shouldThrowHMAC256InstanceWithNullSecret() throws Exception { + public void shouldThrowHMAC256InstanceWithNullSecret() { exception.expect(IllegalArgumentException.class); exception.expectMessage("The Secret cannot be null"); String secret = null; @@ -57,7 +55,7 @@ public void shouldThrowHMAC256InstanceWithNullSecret() throws Exception { } @Test - public void shouldThrowHMAC384InstanceWithNullSecret() throws Exception { + public void shouldThrowHMAC384InstanceWithNullSecret() { exception.expect(IllegalArgumentException.class); exception.expectMessage("The Secret cannot be null"); String secret = null; @@ -65,7 +63,7 @@ public void shouldThrowHMAC384InstanceWithNullSecret() throws Exception { } @Test - public void shouldThrowHMAC512InstanceWithNullSecret() throws Exception { + public void shouldThrowHMAC512InstanceWithNullSecret() { exception.expect(IllegalArgumentException.class); exception.expectMessage("The Secret cannot be null"); String secret = null; @@ -73,7 +71,7 @@ public void shouldThrowHMAC512InstanceWithNullSecret() throws Exception { } @Test - public void shouldThrowRSA256InstanceWithNullKey() throws Exception { + public void shouldThrowRSA256InstanceWithNullKey() { exception.expect(IllegalArgumentException.class); exception.expectMessage("Both provided Keys cannot be null."); RSAKey key = null; @@ -81,14 +79,14 @@ public void shouldThrowRSA256InstanceWithNullKey() throws Exception { } @Test - public void shouldThrowRSA256InstanceWithNullKeys() throws Exception { + public void shouldThrowRSA256InstanceWithNullKeys() { exception.expect(IllegalArgumentException.class); exception.expectMessage("Both provided Keys cannot be null."); Algorithm.RSA256(null, null); } @Test - public void shouldThrowRSA256InstanceWithNullKeyProvider() throws Exception { + public void shouldThrowRSA256InstanceWithNullKeyProvider() { exception.expect(IllegalArgumentException.class); exception.expectMessage("The Key Provider cannot be null."); RSAKeyProvider provider = null; @@ -96,7 +94,7 @@ public void shouldThrowRSA256InstanceWithNullKeyProvider() throws Exception { } @Test - public void shouldThrowRSA384InstanceWithNullKey() throws Exception { + public void shouldThrowRSA384InstanceWithNullKey() { exception.expect(IllegalArgumentException.class); exception.expectMessage("Both provided Keys cannot be null."); RSAKey key = null; @@ -104,14 +102,14 @@ public void shouldThrowRSA384InstanceWithNullKey() throws Exception { } @Test - public void shouldThrowRSA384InstanceWithNullKeys() throws Exception { + public void shouldThrowRSA384InstanceWithNullKeys() { exception.expect(IllegalArgumentException.class); exception.expectMessage("Both provided Keys cannot be null."); Algorithm.RSA384(null, null); } @Test - public void shouldThrowRSA384InstanceWithNullKeyProvider() throws Exception { + public void shouldThrowRSA384InstanceWithNullKeyProvider() { exception.expect(IllegalArgumentException.class); exception.expectMessage("The Key Provider cannot be null."); RSAKeyProvider provider = null; @@ -119,7 +117,7 @@ public void shouldThrowRSA384InstanceWithNullKeyProvider() throws Exception { } @Test - public void shouldThrowRSA512InstanceWithNullKey() throws Exception { + public void shouldThrowRSA512InstanceWithNullKey() { exception.expect(IllegalArgumentException.class); exception.expectMessage("Both provided Keys cannot be null."); RSAKey key = null; @@ -127,14 +125,14 @@ public void shouldThrowRSA512InstanceWithNullKey() throws Exception { } @Test - public void shouldThrowRSA512InstanceWithNullKeys() throws Exception { + public void shouldThrowRSA512InstanceWithNullKeys() { exception.expect(IllegalArgumentException.class); exception.expectMessage("Both provided Keys cannot be null."); Algorithm.RSA512(null, null); } @Test - public void shouldThrowRSA512InstanceWithNullKeyProvider() throws Exception { + public void shouldThrowRSA512InstanceWithNullKeyProvider() { exception.expect(IllegalArgumentException.class); exception.expectMessage("The Key Provider cannot be null."); RSAKeyProvider provider = null; @@ -142,7 +140,7 @@ public void shouldThrowRSA512InstanceWithNullKeyProvider() throws Exception { } @Test - public void shouldThrowECDSA256InstanceWithNullKey() throws Exception { + public void shouldThrowECDSA256InstanceWithNullKey() { exception.expect(IllegalArgumentException.class); exception.expectMessage("Both provided Keys cannot be null."); ECKey key = null; @@ -150,14 +148,14 @@ public void shouldThrowECDSA256InstanceWithNullKey() throws Exception { } @Test - public void shouldThrowECDSA256InstanceWithNullKeys() throws Exception { + public void shouldThrowECDSA256InstanceWithNullKeys() { exception.expect(IllegalArgumentException.class); exception.expectMessage("Both provided Keys cannot be null."); Algorithm.ECDSA256(null, null); } @Test - public void shouldThrowECDSA256InstanceWithNullKeyProvider() throws Exception { + public void shouldThrowECDSA256InstanceWithNullKeyProvider() { exception.expect(IllegalArgumentException.class); exception.expectMessage("The Key Provider cannot be null."); ECDSAKeyProvider provider = null; @@ -165,7 +163,7 @@ public void shouldThrowECDSA256InstanceWithNullKeyProvider() throws Exception { } @Test - public void shouldThrowECDSA384InstanceWithNullKey() throws Exception { + public void shouldThrowECDSA384InstanceWithNullKey() { exception.expect(IllegalArgumentException.class); exception.expectMessage("Both provided Keys cannot be null."); ECKey key = null; @@ -173,14 +171,14 @@ public void shouldThrowECDSA384InstanceWithNullKey() throws Exception { } @Test - public void shouldThrowECDSA384InstanceWithNullKeys() throws Exception { + public void shouldThrowECDSA384InstanceWithNullKeys() { exception.expect(IllegalArgumentException.class); exception.expectMessage("Both provided Keys cannot be null."); Algorithm.ECDSA384(null, null); } @Test - public void shouldThrowECDSA384InstanceWithNullKeyProvider() throws Exception { + public void shouldThrowECDSA384InstanceWithNullKeyProvider() { exception.expect(IllegalArgumentException.class); exception.expectMessage("The Key Provider cannot be null."); ECDSAKeyProvider provider = null; @@ -188,7 +186,7 @@ public void shouldThrowECDSA384InstanceWithNullKeyProvider() throws Exception { } @Test - public void shouldThrowECDSA512InstanceWithNullKey() throws Exception { + public void shouldThrowECDSA512InstanceWithNullKey() { exception.expect(IllegalArgumentException.class); exception.expectMessage("Both provided Keys cannot be null."); ECKey key = null; @@ -196,14 +194,14 @@ public void shouldThrowECDSA512InstanceWithNullKey() throws Exception { } @Test - public void shouldThrowECDSA512InstanceWithNullKeys() throws Exception { + public void shouldThrowECDSA512InstanceWithNullKeys() { exception.expect(IllegalArgumentException.class); exception.expectMessage("Both provided Keys cannot be null."); Algorithm.ECDSA512(null, null); } @Test - public void shouldThrowECDSA512InstanceWithNullKeyProvider() throws Exception { + public void shouldThrowECDSA512InstanceWithNullKeyProvider() { exception.expect(IllegalArgumentException.class); exception.expectMessage("The Key Provider cannot be null."); ECDSAKeyProvider provider = null; @@ -211,7 +209,7 @@ public void shouldThrowECDSA512InstanceWithNullKeyProvider() throws Exception { } @Test - public void shouldCreateHMAC256AlgorithmWithBytes() throws Exception { + public void shouldCreateHMAC256AlgorithmWithBytes() { Algorithm algorithm = Algorithm.HMAC256("secret".getBytes(StandardCharsets.UTF_8)); assertThat(algorithm, is(notNullValue())); @@ -221,7 +219,7 @@ public void shouldCreateHMAC256AlgorithmWithBytes() throws Exception { } @Test - public void shouldCreateHMAC384AlgorithmWithBytes() throws Exception { + public void shouldCreateHMAC384AlgorithmWithBytes() { Algorithm algorithm = Algorithm.HMAC384("secret".getBytes(StandardCharsets.UTF_8)); assertThat(algorithm, is(notNullValue())); @@ -231,7 +229,7 @@ public void shouldCreateHMAC384AlgorithmWithBytes() throws Exception { } @Test - public void shouldCreateHMAC512AlgorithmWithBytes() throws Exception { + public void shouldCreateHMAC512AlgorithmWithBytes() { Algorithm algorithm = Algorithm.HMAC512("secret".getBytes(StandardCharsets.UTF_8)); assertThat(algorithm, is(notNullValue())); @@ -241,7 +239,7 @@ public void shouldCreateHMAC512AlgorithmWithBytes() throws Exception { } @Test - public void shouldCreateHMAC256AlgorithmWithString() throws Exception { + public void shouldCreateHMAC256AlgorithmWithString() { Algorithm algorithm = Algorithm.HMAC256("secret"); assertThat(algorithm, is(notNullValue())); @@ -251,7 +249,7 @@ public void shouldCreateHMAC256AlgorithmWithString() throws Exception { } @Test - public void shouldCreateHMAC384AlgorithmWithString() throws Exception { + public void shouldCreateHMAC384AlgorithmWithString() { Algorithm algorithm = Algorithm.HMAC384("secret"); assertThat(algorithm, is(notNullValue())); @@ -261,7 +259,7 @@ public void shouldCreateHMAC384AlgorithmWithString() throws Exception { } @Test - public void shouldCreateHMAC512AlgorithmWithString() throws Exception { + public void shouldCreateHMAC512AlgorithmWithString() { Algorithm algorithm = Algorithm.HMAC512("secret"); assertThat(algorithm, is(notNullValue())); @@ -271,7 +269,7 @@ public void shouldCreateHMAC512AlgorithmWithString() throws Exception { } @Test - public void shouldCreateRSA256AlgorithmWithPublicKey() throws Exception { + public void shouldCreateRSA256AlgorithmWithPublicKey() { RSAKey key = mock(RSAKey.class, withSettings().extraInterfaces(RSAPublicKey.class)); Algorithm algorithm = Algorithm.RSA256(key); @@ -282,7 +280,7 @@ public void shouldCreateRSA256AlgorithmWithPublicKey() throws Exception { } @Test - public void shouldCreateRSA256AlgorithmWithPrivateKey() throws Exception { + public void shouldCreateRSA256AlgorithmWithPrivateKey() { RSAKey key = mock(RSAKey.class, withSettings().extraInterfaces(RSAPrivateKey.class)); Algorithm algorithm = Algorithm.RSA256(key); @@ -293,7 +291,7 @@ public void shouldCreateRSA256AlgorithmWithPrivateKey() throws Exception { } @Test - public void shouldCreateRSA256AlgorithmWithBothKeys() throws Exception { + public void shouldCreateRSA256AlgorithmWithBothKeys() { RSAPublicKey publicKey = mock(RSAPublicKey.class); RSAPrivateKey privateKey = mock(RSAPrivateKey.class); Algorithm algorithm = Algorithm.RSA256(publicKey, privateKey); @@ -305,7 +303,7 @@ public void shouldCreateRSA256AlgorithmWithBothKeys() throws Exception { } @Test - public void shouldCreateRSA256AlgorithmWithProvider() throws Exception { + public void shouldCreateRSA256AlgorithmWithProvider() { RSAKeyProvider provider = mock(RSAKeyProvider.class); Algorithm algorithm = Algorithm.RSA256(provider); @@ -316,7 +314,7 @@ public void shouldCreateRSA256AlgorithmWithProvider() throws Exception { } @Test - public void shouldCreateRSA384AlgorithmWithPublicKey() throws Exception { + public void shouldCreateRSA384AlgorithmWithPublicKey() { RSAKey key = mock(RSAKey.class, withSettings().extraInterfaces(RSAPublicKey.class)); Algorithm algorithm = Algorithm.RSA384(key); @@ -327,7 +325,7 @@ public void shouldCreateRSA384AlgorithmWithPublicKey() throws Exception { } @Test - public void shouldCreateRSA384AlgorithmWithPrivateKey() throws Exception { + public void shouldCreateRSA384AlgorithmWithPrivateKey() { RSAKey key = mock(RSAKey.class, withSettings().extraInterfaces(RSAPrivateKey.class)); Algorithm algorithm = Algorithm.RSA384(key); @@ -338,7 +336,7 @@ public void shouldCreateRSA384AlgorithmWithPrivateKey() throws Exception { } @Test - public void shouldCreateRSA384AlgorithmWithBothKeys() throws Exception { + public void shouldCreateRSA384AlgorithmWithBothKeys() { RSAPublicKey publicKey = mock(RSAPublicKey.class); RSAPrivateKey privateKey = mock(RSAPrivateKey.class); Algorithm algorithm = Algorithm.RSA384(publicKey, privateKey); @@ -350,7 +348,7 @@ public void shouldCreateRSA384AlgorithmWithBothKeys() throws Exception { } @Test - public void shouldCreateRSA384AlgorithmWithProvider() throws Exception { + public void shouldCreateRSA384AlgorithmWithProvider() { RSAKeyProvider provider = mock(RSAKeyProvider.class); Algorithm algorithm = Algorithm.RSA384(provider); @@ -361,7 +359,7 @@ public void shouldCreateRSA384AlgorithmWithProvider() throws Exception { } @Test - public void shouldCreateRSA512AlgorithmWithPublicKey() throws Exception { + public void shouldCreateRSA512AlgorithmWithPublicKey() { RSAKey key = mock(RSAKey.class, withSettings().extraInterfaces(RSAPublicKey.class)); Algorithm algorithm = Algorithm.RSA512(key); @@ -372,7 +370,7 @@ public void shouldCreateRSA512AlgorithmWithPublicKey() throws Exception { } @Test - public void shouldCreateRSA512AlgorithmWithPrivateKey() throws Exception { + public void shouldCreateRSA512AlgorithmWithPrivateKey() { RSAKey key = mock(RSAKey.class, withSettings().extraInterfaces(RSAPrivateKey.class)); Algorithm algorithm = Algorithm.RSA512(key); @@ -383,7 +381,7 @@ public void shouldCreateRSA512AlgorithmWithPrivateKey() throws Exception { } @Test - public void shouldCreateRSA512AlgorithmWithBothKeys() throws Exception { + public void shouldCreateRSA512AlgorithmWithBothKeys() { RSAPublicKey publicKey = mock(RSAPublicKey.class); RSAPrivateKey privateKey = mock(RSAPrivateKey.class); Algorithm algorithm = Algorithm.RSA512(publicKey, privateKey); @@ -395,7 +393,7 @@ public void shouldCreateRSA512AlgorithmWithBothKeys() throws Exception { } @Test - public void shouldCreateRSA512AlgorithmWithProvider() throws Exception { + public void shouldCreateRSA512AlgorithmWithProvider() { RSAKeyProvider provider = mock(RSAKeyProvider.class); Algorithm algorithm = Algorithm.RSA512(provider); @@ -406,7 +404,7 @@ public void shouldCreateRSA512AlgorithmWithProvider() throws Exception { } @Test - public void shouldCreateECDSA256AlgorithmWithPublicKey() throws Exception { + public void shouldCreateECDSA256AlgorithmWithPublicKey() { ECKey key = mock(ECKey.class, withSettings().extraInterfaces(ECPublicKey.class)); Algorithm algorithm = Algorithm.ECDSA256(key); @@ -417,7 +415,7 @@ public void shouldCreateECDSA256AlgorithmWithPublicKey() throws Exception { } @Test - public void shouldCreateECDSA256AlgorithmWithPrivateKey() throws Exception { + public void shouldCreateECDSA256AlgorithmWithPrivateKey() { ECKey key = mock(ECKey.class, withSettings().extraInterfaces(ECPrivateKey.class)); Algorithm algorithm = Algorithm.ECDSA256(key); @@ -428,7 +426,7 @@ public void shouldCreateECDSA256AlgorithmWithPrivateKey() throws Exception { } @Test - public void shouldCreateECDSA256AlgorithmWithBothKeys() throws Exception { + public void shouldCreateECDSA256AlgorithmWithBothKeys() { ECPublicKey publicKey = mock(ECPublicKey.class); ECPrivateKey privateKey = mock(ECPrivateKey.class); Algorithm algorithm = Algorithm.ECDSA256(publicKey, privateKey); @@ -440,7 +438,7 @@ public void shouldCreateECDSA256AlgorithmWithBothKeys() throws Exception { } @Test - public void shouldCreateECDSA256AlgorithmWithProvider() throws Exception { + public void shouldCreateECDSA256AlgorithmWithProvider() { ECDSAKeyProvider provider = mock(ECDSAKeyProvider.class); Algorithm algorithm = Algorithm.ECDSA256(provider); @@ -451,7 +449,7 @@ public void shouldCreateECDSA256AlgorithmWithProvider() throws Exception { } @Test - public void shouldCreateECDSA384AlgorithmWithPublicKey() throws Exception { + public void shouldCreateECDSA384AlgorithmWithPublicKey() { ECKey key = mock(ECKey.class, withSettings().extraInterfaces(ECPublicKey.class)); Algorithm algorithm = Algorithm.ECDSA384(key); @@ -462,7 +460,7 @@ public void shouldCreateECDSA384AlgorithmWithPublicKey() throws Exception { } @Test - public void shouldCreateECDSA384AlgorithmWithPrivateKey() throws Exception { + public void shouldCreateECDSA384AlgorithmWithPrivateKey() { ECKey key = mock(ECKey.class, withSettings().extraInterfaces(ECPrivateKey.class)); Algorithm algorithm = Algorithm.ECDSA384(key); @@ -473,7 +471,7 @@ public void shouldCreateECDSA384AlgorithmWithPrivateKey() throws Exception { } @Test - public void shouldCreateECDSA384AlgorithmWithBothKeys() throws Exception { + public void shouldCreateECDSA384AlgorithmWithBothKeys() { ECPublicKey publicKey = mock(ECPublicKey.class); ECPrivateKey privateKey = mock(ECPrivateKey.class); Algorithm algorithm = Algorithm.ECDSA384(publicKey, privateKey); @@ -485,7 +483,7 @@ public void shouldCreateECDSA384AlgorithmWithBothKeys() throws Exception { } @Test - public void shouldCreateECDSA384AlgorithmWithProvider() throws Exception { + public void shouldCreateECDSA384AlgorithmWithProvider() { ECDSAKeyProvider provider = mock(ECDSAKeyProvider.class); Algorithm algorithm = Algorithm.ECDSA384(provider); @@ -496,7 +494,7 @@ public void shouldCreateECDSA384AlgorithmWithProvider() throws Exception { } @Test - public void shouldCreateECDSA512AlgorithmWithPublicKey() throws Exception { + public void shouldCreateECDSA512AlgorithmWithPublicKey() { ECKey key = mock(ECKey.class, withSettings().extraInterfaces(ECPublicKey.class)); Algorithm algorithm = Algorithm.ECDSA512(key); @@ -507,7 +505,7 @@ public void shouldCreateECDSA512AlgorithmWithPublicKey() throws Exception { } @Test - public void shouldCreateECDSA512AlgorithmWithPrivateKey() throws Exception { + public void shouldCreateECDSA512AlgorithmWithPrivateKey() { ECKey key = mock(ECKey.class, withSettings().extraInterfaces(ECPrivateKey.class)); Algorithm algorithm = Algorithm.ECDSA512(key); @@ -518,7 +516,7 @@ public void shouldCreateECDSA512AlgorithmWithPrivateKey() throws Exception { } @Test - public void shouldCreateECDSA512AlgorithmWithBothKeys() throws Exception { + public void shouldCreateECDSA512AlgorithmWithBothKeys() { ECPublicKey publicKey = mock(ECPublicKey.class); ECPrivateKey privateKey = mock(ECPrivateKey.class); Algorithm algorithm = Algorithm.ECDSA512(publicKey, privateKey); @@ -530,7 +528,7 @@ public void shouldCreateECDSA512AlgorithmWithBothKeys() throws Exception { } @Test - public void shouldCreateECDSA512AlgorithmWithProvider() throws Exception { + public void shouldCreateECDSA512AlgorithmWithProvider() { ECDSAKeyProvider provider = mock(ECDSAKeyProvider.class); Algorithm algorithm = Algorithm.ECDSA512(provider); @@ -541,7 +539,7 @@ public void shouldCreateECDSA512AlgorithmWithProvider() throws Exception { } @Test - public void shouldCreateNoneAlgorithm() throws Exception { + public void shouldCreateNoneAlgorithm() { Algorithm algorithm = Algorithm.none(); assertThat(algorithm, is(notNullValue())); @@ -573,4 +571,4 @@ public void shouldForwardHeaderPayloadSignatureToSiblingSignMethodForBackwardsCo assertThat(sign, is(signature)); assertThat(contentCaptor.getValue(), is(bout.toByteArray())); } -} \ No newline at end of file +} diff --git a/lib/src/test/java/com/auth0/jwt/algorithms/CryptoTestHelper.java b/lib/src/test/java/com/auth0/jwt/algorithms/CryptoTestHelper.java index f977e42a..68733353 100644 --- a/lib/src/test/java/com/auth0/jwt/algorithms/CryptoTestHelper.java +++ b/lib/src/test/java/com/auth0/jwt/algorithms/CryptoTestHelper.java @@ -11,7 +11,6 @@ import org.apache.commons.codec.binary.Base64; public abstract class CryptoTestHelper { - private static final Pattern authHeaderPattern = Pattern.compile("^([\\w-]+)\\.([\\w-]+)\\.([\\w-]+)"); public static String asJWT(Algorithm algorithm, String header, String payload) { diff --git a/lib/src/test/java/com/auth0/jwt/algorithms/ECDSAAlgorithmTest.java b/lib/src/test/java/com/auth0/jwt/algorithms/ECDSAAlgorithmTest.java index b50832d1..1717d36a 100644 --- a/lib/src/test/java/com/auth0/jwt/algorithms/ECDSAAlgorithmTest.java +++ b/lib/src/test/java/com/auth0/jwt/algorithms/ECDSAAlgorithmTest.java @@ -36,7 +36,6 @@ @SuppressWarnings("deprecation") public class ECDSAAlgorithmTest { - private static final String PRIVATE_KEY_FILE_256 = "src/test/resources/ec256-key-private.pem"; private static final String PUBLIC_KEY_FILE_256 = "src/test/resources/ec256-key-public.pem"; private static final String INVALID_PUBLIC_KEY_FILE_256 = "src/test/resources/ec256-key-public-invalid.pem"; @@ -110,7 +109,7 @@ public void shouldPassECDSA256VerificationWithProvidedPublicKey() throws Excepti } @Test - public void shouldFailECDSA256VerificationWhenProvidedPublicKeyIsNull() throws Exception { + public void shouldFailECDSA256VerificationWhenProvidedPublicKeyIsNull() { exception.expect(SignatureVerificationException.class); exception.expectMessage("The Token's Signature resulted invalid when verified using the Algorithm: SHA256withECDSA"); exception.expectCause(isA(IllegalStateException.class)); @@ -235,7 +234,7 @@ public void shouldPassECDSA384VerificationWithProvidedPublicKey() throws Excepti } @Test - public void shouldFailECDSA384VerificationWhenProvidedPublicKeyIsNull() throws Exception { + public void shouldFailECDSA384VerificationWhenProvidedPublicKeyIsNull() { exception.expect(SignatureVerificationException.class); exception.expectMessage("The Token's Signature resulted invalid when verified using the Algorithm: SHA384withECDSA"); exception.expectCause(isA(IllegalStateException.class)); @@ -360,7 +359,7 @@ public void shouldPassECDSA512VerificationWithProvidedPublicKey() throws Excepti } @Test - public void shouldFailECDSA512VerificationWhenProvidedPublicKeyIsNull() throws Exception { + public void shouldFailECDSA512VerificationWhenProvidedPublicKeyIsNull() { exception.expect(SignatureVerificationException.class); exception.expectMessage("The Token's Signature resulted invalid when verified using the Algorithm: SHA512withECDSA"); exception.expectCause(isA(IllegalStateException.class)); @@ -514,8 +513,6 @@ public void shouldThrowOnVerifyWhenTheSignatureIsNotPrepared() throws Exception private static final String auth0IssPayload = "eyJpc3MiOiJhdXRoMCJ9"; private static final byte[] ES256HeaderBytes = ES256Header.getBytes(StandardCharsets.UTF_8); - private static final byte[] ES384HeaderBytes = ES384Header.getBytes(StandardCharsets.UTF_8); - private static final byte[] ES512HeaderBytes = ES512Header.getBytes(StandardCharsets.UTF_8); private static final byte[] auth0IssPayloadBytes = auth0IssPayload.getBytes(StandardCharsets.UTF_8); @@ -556,7 +553,7 @@ public void shouldDoECDSA256SigningWithProvidedPrivateKey() throws Exception { } @Test - public void shouldFailOnECDSA256SigningWhenProvidedPrivateKeyIsNull() throws Exception { + public void shouldFailOnECDSA256SigningWhenProvidedPrivateKeyIsNull() { exception.expect(SignatureGenerationException.class); exception.expectMessage("The Token's Signature couldn't be generated when signing using the Algorithm: SHA256withECDSA"); exception.expectCause(isA(IllegalStateException.class)); @@ -614,7 +611,7 @@ public void shouldDoECDSA384SigningWithProvidedPrivateKey() throws Exception { } @Test - public void shouldFailOnECDSA384SigningWhenProvidedPrivateKeyIsNull() throws Exception { + public void shouldFailOnECDSA384SigningWhenProvidedPrivateKeyIsNull() { exception.expect(SignatureGenerationException.class); exception.expectMessage("The Token's Signature couldn't be generated when signing using the Algorithm: SHA384withECDSA"); exception.expectCause(isA(IllegalStateException.class)); @@ -675,7 +672,7 @@ public void shouldDoECDSA512SigningWithProvidedPrivateKey() throws Exception { } @Test - public void shouldFailOnECDSA512SigningWhenProvidedPrivateKeyIsNull() throws Exception { + public void shouldFailOnECDSA512SigningWhenProvidedPrivateKeyIsNull() { exception.expect(SignatureGenerationException.class); exception.expectMessage("The Token's Signature couldn't be generated when signing using the Algorithm: SHA512withECDSA"); exception.expectCause(isA(IllegalStateException.class)); @@ -750,7 +747,7 @@ public void shouldThrowOnSignWhenTheSignatureIsNotPrepared() throws Exception { } @Test - public void shouldReturnNullSigningKeyIdIfCreatedWithDefaultProvider() throws Exception { + public void shouldReturnNullSigningKeyIdIfCreatedWithDefaultProvider() { ECPublicKey publicKey = mock(ECPublicKey.class); ECPrivateKey privateKey = mock(ECPrivateKey.class); ECDSAKeyProvider provider = ECDSAAlgorithm.providerForKeys(publicKey, privateKey); @@ -760,7 +757,7 @@ public void shouldReturnNullSigningKeyIdIfCreatedWithDefaultProvider() throws Ex } @Test - public void shouldReturnSigningKeyIdFromProvider() throws Exception { + public void shouldReturnSigningKeyIdFromProvider() { ECDSAKeyProvider provider = mock(ECDSAKeyProvider.class); when(provider.getPrivateKeyId()).thenReturn("keyId"); Algorithm algorithm = new ECDSAAlgorithm("some-alg", "some-algorithm", 32, provider); @@ -785,7 +782,7 @@ public void shouldThrowOnDERSignatureConversionIfDoesNotStartWithCorrectSequence public void shouldThrowOnDERSignatureConversionIfDoesNotHaveExpectedLength() throws Exception { ECDSAAlgorithm algorithm256 = (ECDSAAlgorithm) Algorithm.ECDSA256((ECPublicKey) readPublicKeyFromFile(PUBLIC_KEY_FILE_256, "EC"), (ECPrivateKey) readPrivateKeyFromFile(PRIVATE_KEY_FILE_256, "EC")); byte[] derSignature = createDERSignature(32, false, false); - int received = (int) derSignature[1]; + int received = derSignature[1]; received--; derSignature[1] = (byte) received; exception.expect(SignatureException.class); @@ -1187,5 +1184,4 @@ public void shouldFailOnECDSA256SigningWithDeprecatedMethodWhenProvidedPrivateKe Algorithm algorithm = Algorithm.ECDSA256(provider); algorithm.sign(new byte[0]); } - -} \ No newline at end of file +} diff --git a/lib/src/test/java/com/auth0/jwt/algorithms/ECDSABouncyCastleProviderTests.java b/lib/src/test/java/com/auth0/jwt/algorithms/ECDSABouncyCastleProviderTests.java index 452c0074..60e1457d 100644 --- a/lib/src/test/java/com/auth0/jwt/algorithms/ECDSABouncyCastleProviderTests.java +++ b/lib/src/test/java/com/auth0/jwt/algorithms/ECDSABouncyCastleProviderTests.java @@ -32,7 +32,6 @@ import static org.mockito.Mockito.when; public class ECDSABouncyCastleProviderTests { - private static final String PRIVATE_KEY_FILE_256 = "src/test/resources/ec256-key-private.pem"; private static final String PUBLIC_KEY_FILE_256 = "src/test/resources/ec256-key-public.pem"; private static final String INVALID_PUBLIC_KEY_FILE_256 = "src/test/resources/ec256-key-public-invalid.pem"; @@ -56,18 +55,18 @@ public class ECDSABouncyCastleProviderTests { //These tests add and use the BouncyCastle SecurityProvider to handle ECDSA algorithms @BeforeClass - public static void setUp() throws Exception { + public static void setUp() { //Set BC as the preferred bcProvider Security.insertProviderAt(bcProvider, 1); } @AfterClass - public static void tearDown() throws Exception { + public static void tearDown() { Security.removeProvider(bcProvider.getName()); } @Test - public void shouldPreferBouncyCastleProvider() throws Exception { + public void shouldPreferBouncyCastleProvider() { assertThat(Security.getProviders()[0], is(equalTo(bcProvider))); } @@ -124,7 +123,7 @@ public void shouldPassECDSA256VerificationWithProvidedPublicKey() throws Excepti } @Test - public void shouldFailECDSA256VerificationWhenProvidedPublicKeyIsNull() throws Exception { + public void shouldFailECDSA256VerificationWhenProvidedPublicKeyIsNull() { exception.expect(SignatureVerificationException.class); exception.expectMessage("The Token's Signature resulted invalid when verified using the Algorithm: SHA256withECDSA"); exception.expectCause(isA(IllegalStateException.class)); @@ -249,7 +248,7 @@ public void shouldPassECDSA384VerificationWithProvidedPublicKey() throws Excepti } @Test - public void shouldFailECDSA384VerificationWhenProvidedPublicKeyIsNull() throws Exception { + public void shouldFailECDSA384VerificationWhenProvidedPublicKeyIsNull() { exception.expect(SignatureVerificationException.class); exception.expectMessage("The Token's Signature resulted invalid when verified using the Algorithm: SHA384withECDSA"); exception.expectCause(isA(IllegalStateException.class)); @@ -374,7 +373,7 @@ public void shouldPassECDSA512VerificationWithProvidedPublicKey() throws Excepti } @Test - public void shouldFailECDSA512VerificationWhenProvidedPublicKeyIsNull() throws Exception { + public void shouldFailECDSA512VerificationWhenProvidedPublicKeyIsNull() { exception.expect(SignatureVerificationException.class); exception.expectMessage("The Token's Signature resulted invalid when verified using the Algorithm: SHA512withECDSA"); exception.expectCause(isA(IllegalStateException.class)); @@ -562,7 +561,7 @@ public void shouldDoECDSA256SigningWithProvidedPrivateKey() throws Exception { } @Test - public void shouldFailOnECDSA256SigningWhenProvidedPrivateKeyIsNull() throws Exception { + public void shouldFailOnECDSA256SigningWhenProvidedPrivateKeyIsNull() { exception.expect(SignatureGenerationException.class); exception.expectMessage("The Token's Signature couldn't be generated when signing using the Algorithm: SHA256withECDSA"); exception.expectCause(isA(IllegalStateException.class)); @@ -620,7 +619,7 @@ public void shouldDoECDSA384SigningWithProvidedPrivateKey() throws Exception { } @Test - public void shouldFailOnECDSA384SigningWhenProvidedPrivateKeyIsNull() throws Exception { + public void shouldFailOnECDSA384SigningWhenProvidedPrivateKeyIsNull() { exception.expect(SignatureGenerationException.class); exception.expectMessage("The Token's Signature couldn't be generated when signing using the Algorithm: SHA384withECDSA"); exception.expectCause(isA(IllegalStateException.class)); @@ -679,7 +678,7 @@ public void shouldDoECDSA512SigningWithProvidedPrivateKey() throws Exception { } @Test - public void shouldFailOnECDSA512SigningWhenProvidedPrivateKeyIsNull() throws Exception { + public void shouldFailOnECDSA512SigningWhenProvidedPrivateKeyIsNull() { exception.expect(SignatureGenerationException.class); exception.expectMessage("The Token's Signature couldn't be generated when signing using the Algorithm: SHA512withECDSA"); exception.expectCause(isA(IllegalStateException.class)); @@ -754,7 +753,7 @@ public void shouldThrowOnSignWhenTheSignatureIsNotPrepared() throws Exception { } @Test - public void shouldReturnNullSigningKeyIdIfCreatedWithDefaultProvider() throws Exception { + public void shouldReturnNullSigningKeyIdIfCreatedWithDefaultProvider() { ECPublicKey publicKey = mock(ECPublicKey.class); ECPrivateKey privateKey = mock(ECPrivateKey.class); ECDSAKeyProvider provider = ECDSAAlgorithm.providerForKeys(publicKey, privateKey); @@ -764,7 +763,7 @@ public void shouldReturnNullSigningKeyIdIfCreatedWithDefaultProvider() throws Ex } @Test - public void shouldReturnSigningKeyIdFromProvider() throws Exception { + public void shouldReturnSigningKeyIdFromProvider() { ECDSAKeyProvider provider = mock(ECDSAKeyProvider.class); when(provider.getPrivateKeyId()).thenReturn("keyId"); Algorithm algorithm = new ECDSAAlgorithm("some-alg", "some-algorithm", 32, provider); @@ -789,7 +788,7 @@ public void shouldThrowOnDERSignatureConversionIfDoesNotStartWithCorrectSequence public void shouldThrowOnDERSignatureConversionIfDoesNotHaveExpectedLength() throws Exception { ECDSAAlgorithm algorithm256 = (ECDSAAlgorithm) Algorithm.ECDSA256((ECPublicKey) readPublicKeyFromFile(PUBLIC_KEY_FILE_256, "EC"), (ECPrivateKey) readPrivateKeyFromFile(PRIVATE_KEY_FILE_256, "EC")); byte[] derSignature = createDERSignature(32, false, false); - int received = (int) derSignature[1]; + int received = derSignature[1]; received--; derSignature[1] = (byte) received; exception.expect(SignatureException.class); diff --git a/lib/src/test/java/com/auth0/jwt/algorithms/HMACAlgorithmTest.java b/lib/src/test/java/com/auth0/jwt/algorithms/HMACAlgorithmTest.java index b70c8633..f6f784c8 100644 --- a/lib/src/test/java/com/auth0/jwt/algorithms/HMACAlgorithmTest.java +++ b/lib/src/test/java/com/auth0/jwt/algorithms/HMACAlgorithmTest.java @@ -12,35 +12,32 @@ import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; -import java.util.Arrays; import static com.auth0.jwt.algorithms.CryptoTestHelper.asJWT; import static com.auth0.jwt.algorithms.CryptoTestHelper.assertSignaturePresent; import static com.auth0.jwt.algorithms.CryptoTestHelper.assertSignatureValue; import static org.hamcrest.Matchers.*; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class HMACAlgorithmTest { - @Rule public ExpectedException exception = ExpectedException.none(); // Verify @Test - public void shouldGetStringBytes() throws Exception { + public void shouldGetStringBytes() { String text = "abcdef123456!@#$%^"; - byte[] expectedBytes = text.getBytes("UTF-8"); - assertTrue(Arrays.equals(expectedBytes, HMACAlgorithm.getSecretBytes(text))); + byte[] expectedBytes = text.getBytes(StandardCharsets.UTF_8); + assertArrayEquals(expectedBytes, HMACAlgorithm.getSecretBytes(text)); } @Test - public void shouldPassHMAC256Verification() throws Exception { + public void shouldPassHMAC256Verification() { String jwt = "eyJhbGciOiJIUzI1NiIsImN0eSI6IkpXVCJ9.eyJpc3MiOiJhdXRoMCJ9.mZ0m_N1J4PgeqWmi903JuUoDRZDBPB7HwkS4nVyWH1M"; Algorithm algorithmString = Algorithm.HMAC256("secret"); Algorithm algorithmBytes = Algorithm.HMAC256("secret".getBytes(StandardCharsets.UTF_8)); @@ -50,7 +47,7 @@ public void shouldPassHMAC256Verification() throws Exception { } @Test - public void shouldFailHMAC256VerificationWithInvalidSecretString() throws Exception { + public void shouldFailHMAC256VerificationWithInvalidSecretString() { exception.expect(SignatureVerificationException.class); exception.expectMessage("The Token's Signature resulted invalid when verified using the Algorithm: HmacSHA256"); String jwt = "eyJhbGciOiJIUzI1NiIsImN0eSI6IkpXVCJ9.eyJpc3MiOiJhdXRoMCJ9.mZ0m_N1J4PgeqWmi903JuUoDRZDBPB7HwkS4nVyWH1M"; @@ -59,7 +56,7 @@ public void shouldFailHMAC256VerificationWithInvalidSecretString() throws Except } @Test - public void shouldFailHMAC256VerificationWithInvalidSecretBytes() throws Exception { + public void shouldFailHMAC256VerificationWithInvalidSecretBytes() { exception.expect(SignatureVerificationException.class); exception.expectMessage("The Token's Signature resulted invalid when verified using the Algorithm: HmacSHA256"); String jwt = "eyJhbGciOiJIUzI1NiIsImN0eSI6IkpXVCJ9.eyJpc3MiOiJhdXRoMCJ9.mZ0m_N1J4PgeqWmi903JuUoDRZDBPB7HwkS4nVyWH1M"; @@ -68,7 +65,7 @@ public void shouldFailHMAC256VerificationWithInvalidSecretBytes() throws Excepti } @Test - public void shouldPassHMAC384Verification() throws Exception { + public void shouldPassHMAC384Verification() { String jwt = "eyJhbGciOiJIUzM4NCIsImN0eSI6IkpXVCJ9.eyJpc3MiOiJhdXRoMCJ9.uztpK_wUMYJhrRv8SV-1LU4aPnwl-EM1q-wJnqgyb5DHoDteP6lN_gE1xnZJH5vw"; Algorithm algorithmString = Algorithm.HMAC384("secret"); Algorithm algorithmBytes = Algorithm.HMAC384("secret".getBytes(StandardCharsets.UTF_8)); @@ -78,7 +75,7 @@ public void shouldPassHMAC384Verification() throws Exception { } @Test - public void shouldFailHMAC384VerificationWithInvalidSecretString() throws Exception { + public void shouldFailHMAC384VerificationWithInvalidSecretString() { exception.expect(SignatureVerificationException.class); exception.expectMessage("The Token's Signature resulted invalid when verified using the Algorithm: HmacSHA384"); String jwt = "eyJhbGciOiJIUzM4NCIsImN0eSI6IkpXVCJ9.eyJpc3MiOiJhdXRoMCJ9.uztpK_wUMYJhrRv8SV-1LU4aPnwl-EM1q-wJnqgyb5DHoDteP6lN_gE1xnZJH5vw"; @@ -87,7 +84,7 @@ public void shouldFailHMAC384VerificationWithInvalidSecretString() throws Except } @Test - public void shouldFailHMAC384VerificationWithInvalidSecretBytes() throws Exception { + public void shouldFailHMAC384VerificationWithInvalidSecretBytes() { exception.expect(SignatureVerificationException.class); exception.expectMessage("The Token's Signature resulted invalid when verified using the Algorithm: HmacSHA384"); String jwt = "eyJhbGciOiJIUzM4NCIsImN0eSI6IkpXVCJ9.eyJpc3MiOiJhdXRoMCJ9.uztpK_wUMYJhrRv8SV-1LU4aPnwl-EM1q-wJnqgyb5DHoDteP6lN_gE1xnZJH5vw"; @@ -96,7 +93,7 @@ public void shouldFailHMAC384VerificationWithInvalidSecretBytes() throws Excepti } @Test - public void shouldPassHMAC512Verification() throws Exception { + public void shouldPassHMAC512Verification() { String jwt = "eyJhbGciOiJIUzUxMiIsImN0eSI6IkpXVCJ9.eyJpc3MiOiJhdXRoMCJ9.VUo2Z9SWDV-XcOc_Hr6Lff3vl7L9e5Vb8ThXpmGDFjHxe3Dr1ZBmUChYF-xVA7cAdX1P_D4ZCUcsv3IefpVaJw"; Algorithm algorithmString = Algorithm.HMAC512("secret"); Algorithm algorithmBytes = Algorithm.HMAC512("secret".getBytes(StandardCharsets.UTF_8)); @@ -106,7 +103,7 @@ public void shouldPassHMAC512Verification() throws Exception { } @Test - public void shouldFailHMAC512VerificationWithInvalidSecretString() throws Exception { + public void shouldFailHMAC512VerificationWithInvalidSecretString() { exception.expect(SignatureVerificationException.class); exception.expectMessage("The Token's Signature resulted invalid when verified using the Algorithm: HmacSHA512"); String jwt = "eyJhbGciOiJIUzUxMiIsImN0eSI6IkpXVCJ9.eyJpc3MiOiJhdXRoMCJ9.VUo2Z9SWDV-XcOc_Hr6Lff3vl7L9e5Vb8ThXpmGDFjHxe3Dr1ZBmUChYF-xVA7cAdX1P_D4ZCUcsv3IefpVaJw"; @@ -115,7 +112,7 @@ public void shouldFailHMAC512VerificationWithInvalidSecretString() throws Except } @Test - public void shouldFailHMAC512VerificationWithInvalidSecretBytes() throws Exception { + public void shouldFailHMAC512VerificationWithInvalidSecretBytes() { exception.expect(SignatureVerificationException.class); exception.expectMessage("The Token's Signature resulted invalid when verified using the Algorithm: HmacSHA512"); String jwt = "eyJhbGciOiJIUzUxMiIsImN0eSI6IkpXVCJ9.eyJpc3MiOiJhdXRoMCJ9.VUo2Z9SWDV-XcOc_Hr6Lff3vl7L9e5Vb8ThXpmGDFjHxe3Dr1ZBmUChYF-xVA7cAdX1P_D4ZCUcsv3IefpVaJw"; @@ -161,7 +158,7 @@ public void shouldThrowOnVerifyWhenTheSecretIsInvalid() throws Exception { private static final String auth0IssPayload = "eyJpc3MiOiJhdXRoMCJ9"; @Test - public void shouldDoHMAC256SigningWithBytes() throws Exception { + public void shouldDoHMAC256SigningWithBytes() { Algorithm algorithm = Algorithm.HMAC256("secret".getBytes(StandardCharsets.UTF_8)); String jwt = asJWT(algorithm, HS256Header, auth0IssPayload); @@ -173,7 +170,7 @@ public void shouldDoHMAC256SigningWithBytes() throws Exception { } @Test - public void shouldDoHMAC384SigningWithBytes() throws Exception { + public void shouldDoHMAC384SigningWithBytes() { Algorithm algorithm = Algorithm.HMAC384("secret".getBytes(StandardCharsets.UTF_8)); String jwt = asJWT(algorithm, HS384Header, auth0IssPayload); @@ -185,7 +182,7 @@ public void shouldDoHMAC384SigningWithBytes() throws Exception { } @Test - public void shouldDoHMAC512SigningWithBytes() throws Exception { + public void shouldDoHMAC512SigningWithBytes() { Algorithm algorithm = Algorithm.HMAC512("secret".getBytes(StandardCharsets.UTF_8)); String jwt = asJWT(algorithm, HS512Header, auth0IssPayload); @@ -197,7 +194,7 @@ public void shouldDoHMAC512SigningWithBytes() throws Exception { } @Test - public void shouldDoHMAC256SigningWithString() throws Exception { + public void shouldDoHMAC256SigningWithString() { Algorithm algorithm = Algorithm.HMAC256("secret"); String jwt = asJWT(algorithm, HS256Header, auth0IssPayload); @@ -209,7 +206,7 @@ public void shouldDoHMAC256SigningWithString() throws Exception { } @Test - public void shouldDoHMAC384SigningWithString() throws Exception { + public void shouldDoHMAC384SigningWithString() { Algorithm algorithm = Algorithm.HMAC384("secret"); String jwt = asJWT(algorithm, HS384Header, auth0IssPayload); @@ -221,7 +218,7 @@ public void shouldDoHMAC384SigningWithString() throws Exception { } @Test - public void shouldDoHMAC512SigningWithString() throws Exception { + public void shouldDoHMAC512SigningWithString() { Algorithm algorithm = Algorithm.HMAC512("secret"); String jwt = asJWT(algorithm ,HS512Header, auth0IssPayload); @@ -261,7 +258,7 @@ public void shouldThrowOnSignWhenTheSecretIsInvalid() throws Exception { } @Test - public void shouldReturnNullSigningKeyId() throws Exception { + public void shouldReturnNullSigningKeyId() { assertThat(Algorithm.HMAC256("secret").getSigningKeyId(), is(nullValue())); } @@ -279,5 +276,4 @@ public void shouldBeEqualSignatureMethodResults() throws Exception { assertThat(algorithm.sign(bout.toByteArray()), is(algorithm.sign(header, payload))); } - -} \ No newline at end of file +} diff --git a/lib/src/test/java/com/auth0/jwt/algorithms/NoneAlgorithmTest.java b/lib/src/test/java/com/auth0/jwt/algorithms/NoneAlgorithmTest.java index f6e72b84..f07ca5fa 100644 --- a/lib/src/test/java/com/auth0/jwt/algorithms/NoneAlgorithmTest.java +++ b/lib/src/test/java/com/auth0/jwt/algorithms/NoneAlgorithmTest.java @@ -12,19 +12,18 @@ import static org.junit.Assert.assertThat; public class NoneAlgorithmTest { - @Rule public ExpectedException exception = ExpectedException.none(); @Test - public void shouldPassNoneVerification() throws Exception { + public void shouldPassNoneVerification() { Algorithm algorithm = Algorithm.none(); String jwt = "eyJhbGciOiJub25lIiwiY3R5IjoiSldUIn0.eyJpc3MiOiJhdXRoMCJ9."; algorithm.verify(JWT.decode(jwt)); } @Test - public void shouldFailNoneVerificationWhenTokenHasTwoParts() throws Exception { + public void shouldFailNoneVerificationWhenTokenHasTwoParts() { exception.expect(JWTDecodeException.class); exception.expectMessage("The token was expected to have 3 parts, but got 2."); String jwt = "eyJhbGciOiJub25lIiwiY3R5IjoiSldUIn0.eyJpc3MiOiJhdXRoMCJ9"; @@ -33,7 +32,7 @@ public void shouldFailNoneVerificationWhenTokenHasTwoParts() throws Exception { } @Test - public void shouldFailNoneVerificationWhenSignatureIsPresent() throws Exception { + public void shouldFailNoneVerificationWhenSignatureIsPresent() { exception.expect(SignatureVerificationException.class); exception.expectMessage("The Token's Signature resulted invalid when verified using the Algorithm: none"); String jwt = "eyJhbGciOiJub25lIiwiY3R5IjoiSldUIn0.eyJpc3MiOiJhdXRoMCJ9.Ox-WRXRaGAuWt2KfPvWiGcCrPqZtbp_4OnQzZXaTfss"; @@ -42,7 +41,7 @@ public void shouldFailNoneVerificationWhenSignatureIsPresent() throws Exception } @Test - public void shouldReturnNullSigningKeyId() throws Exception { + public void shouldReturnNullSigningKeyId() { assertThat(Algorithm.none().getSigningKeyId(), is(nullValue())); } -} \ No newline at end of file +} diff --git a/lib/src/test/java/com/auth0/jwt/algorithms/RSAAlgorithmTest.java b/lib/src/test/java/com/auth0/jwt/algorithms/RSAAlgorithmTest.java index b24365e3..f44ae367 100644 --- a/lib/src/test/java/com/auth0/jwt/algorithms/RSAAlgorithmTest.java +++ b/lib/src/test/java/com/auth0/jwt/algorithms/RSAAlgorithmTest.java @@ -62,7 +62,7 @@ public void shouldPassRSA256VerificationWithProvidedPublicKey() throws Exception } @Test - public void shouldFailRSA256VerificationWhenProvidedPublicKeyIsNull() throws Exception { + public void shouldFailRSA256VerificationWhenProvidedPublicKeyIsNull() { exception.expect(SignatureVerificationException.class); exception.expectMessage("The Token's Signature resulted invalid when verified using the Algorithm: SHA256withRSA"); exception.expectCause(isA(IllegalStateException.class)); @@ -119,7 +119,7 @@ public void shouldPassRSA384VerificationWithProvidedPublicKey() throws Exception } @Test - public void shouldFailRSA384VerificationWhenProvidedPublicKeyIsNull() throws Exception { + public void shouldFailRSA384VerificationWhenProvidedPublicKeyIsNull() { exception.expect(SignatureVerificationException.class); exception.expectMessage("The Token's Signature resulted invalid when verified using the Algorithm: SHA384withRSA"); exception.expectCause(isA(IllegalStateException.class)); @@ -176,7 +176,7 @@ public void shouldPassRSA512VerificationWithProvidedPublicKey() throws Exception } @Test - public void shouldFailRSA512VerificationWhenProvidedPublicKeyIsNull() throws Exception { + public void shouldFailRSA512VerificationWhenProvidedPublicKeyIsNull() { exception.expect(SignatureVerificationException.class); exception.expectMessage("The Token's Signature resulted invalid when verified using the Algorithm: SHA512withRSA"); exception.expectCause(isA(IllegalStateException.class)); @@ -310,7 +310,7 @@ public void shouldDoRSA256SigningWithProvidedPrivateKey() throws Exception { } @Test - public void shouldFailOnRSA256SigningWhenProvidedPrivateKeyIsNull() throws Exception { + public void shouldFailOnRSA256SigningWhenProvidedPrivateKeyIsNull() { exception.expect(SignatureGenerationException.class); exception.expectMessage("The Token's Signature couldn't be generated when signing using the Algorithm: SHA256withRSA"); exception.expectCause(isA(IllegalStateException.class)); @@ -374,7 +374,7 @@ public void shouldDoRSA384SigningWithProvidedPrivateKey() throws Exception { } @Test - public void shouldFailOnRSA384SigningWhenProvidedPrivateKeyIsNull() throws Exception { + public void shouldFailOnRSA384SigningWhenProvidedPrivateKeyIsNull() { exception.expect(SignatureGenerationException.class); exception.expectMessage("The Token's Signature couldn't be generated when signing using the Algorithm: SHA384withRSA"); exception.expectCause(isA(IllegalStateException.class)); @@ -438,7 +438,7 @@ public void shouldDoRSA512SigningWithProvidedPrivateKey() throws Exception { } @Test - public void shouldFailOnRSA512SigningWhenProvidedPrivateKeyIsNull() throws Exception { + public void shouldFailOnRSA512SigningWhenProvidedPrivateKeyIsNull() { exception.expect(SignatureGenerationException.class); exception.expectMessage("The Token's Signature couldn't be generated when signing using the Algorithm: SHA512withRSA"); exception.expectCause(isA(IllegalStateException.class)); @@ -513,7 +513,7 @@ public void shouldThrowOnSignWhenTheSignatureIsNotPrepared() throws Exception { } @Test - public void shouldReturnNullSigningKeyIdIfCreatedWithDefaultProvider() throws Exception { + public void shouldReturnNullSigningKeyIdIfCreatedWithDefaultProvider() { RSAPublicKey publicKey = mock(RSAPublicKey.class); RSAPrivateKey privateKey = mock(RSAPrivateKey.class); RSAKeyProvider provider = RSAAlgorithm.providerForKeys(publicKey, privateKey); @@ -523,7 +523,7 @@ public void shouldReturnNullSigningKeyIdIfCreatedWithDefaultProvider() throws Ex } @Test - public void shouldReturnSigningKeyIdFromProvider() throws Exception { + public void shouldReturnSigningKeyIdFromProvider() { RSAKeyProvider provider = mock(RSAKeyProvider.class); when(provider.getPrivateKeyId()).thenReturn("keyId"); Algorithm algorithm = new RSAAlgorithm("some-alg", "some-algorithm", provider); @@ -553,11 +553,10 @@ public void shouldBeEqualSignatureMethodResults() throws Exception { * Test deprecated signing method error handling. * * @see {@linkplain #shouldFailOnRSA256SigningWhenProvidedPrivateKeyIsNull} - * @throws Exception expected exception */ @Test - public void shouldFailOnRSA256SigningWithDeprecatedMethodWhenProvidedPrivateKeyIsNull() throws Exception { + public void shouldFailOnRSA256SigningWithDeprecatedMethodWhenProvidedPrivateKeyIsNull() { exception.expect(SignatureGenerationException.class); exception.expectMessage("The Token's Signature couldn't be generated when signing using the Algorithm: SHA256withRSA"); exception.expectCause(isA(IllegalStateException.class)); diff --git a/lib/src/test/java/com/auth0/jwt/impl/BasicHeaderTest.java b/lib/src/test/java/com/auth0/jwt/impl/BasicHeaderTest.java index c2d89556..f7252c88 100644 --- a/lib/src/test/java/com/auth0/jwt/impl/BasicHeaderTest.java +++ b/lib/src/test/java/com/auth0/jwt/impl/BasicHeaderTest.java @@ -17,7 +17,6 @@ import static org.hamcrest.Matchers.*; public class BasicHeaderTest { - @Rule public ExpectedException exception = ExpectedException.none(); @@ -25,21 +24,21 @@ public class BasicHeaderTest { @SuppressWarnings("Convert2Diamond") @Test - public void shouldHaveUnmodifiableTreeWhenInstantiatedWithNonNullTree() throws Exception { + public void shouldHaveUnmodifiableTreeWhenInstantiatedWithNonNullTree() { exception.expect(UnsupportedOperationException.class); BasicHeader header = new BasicHeader(null, null, null, null, new HashMap(), objectReader); header.getTree().put("something", null); } @Test - public void shouldHaveUnmodifiableTreeWhenInstantiatedWithNullTree() throws Exception { + public void shouldHaveUnmodifiableTreeWhenInstantiatedWithNullTree() { exception.expect(UnsupportedOperationException.class); BasicHeader header = new BasicHeader(null, null, null, null, null, objectReader); header.getTree().put("something", null); } @Test - public void shouldHaveTree() throws Exception { + public void shouldHaveTree() { HashMap map = new HashMap<>(); JsonNode node = NullNode.getInstance(); map.put("key", node); @@ -50,7 +49,7 @@ public void shouldHaveTree() throws Exception { } @Test - public void shouldGetAlgorithm() throws Exception { + public void shouldGetAlgorithm() { BasicHeader header = new BasicHeader("HS256", null, null, null, null, objectReader); assertThat(header, is(notNullValue())); @@ -59,7 +58,7 @@ public void shouldGetAlgorithm() throws Exception { } @Test - public void shouldGetNullAlgorithmIfMissing() throws Exception { + public void shouldGetNullAlgorithmIfMissing() { BasicHeader header = new BasicHeader(null, null, null, null, null, objectReader); assertThat(header, is(notNullValue())); @@ -67,7 +66,7 @@ public void shouldGetNullAlgorithmIfMissing() throws Exception { } @Test - public void shouldGetType() throws Exception { + public void shouldGetType() { BasicHeader header = new BasicHeader(null, "jwt", null, null, null, objectReader); assertThat(header, is(notNullValue())); @@ -76,7 +75,7 @@ public void shouldGetType() throws Exception { } @Test - public void shouldGetNullTypeIfMissing() throws Exception { + public void shouldGetNullTypeIfMissing() { BasicHeader header = new BasicHeader(null, null, null, null, null, objectReader); assertThat(header, is(notNullValue())); @@ -84,7 +83,7 @@ public void shouldGetNullTypeIfMissing() throws Exception { } @Test - public void shouldGetContentType() throws Exception { + public void shouldGetContentType() { BasicHeader header = new BasicHeader(null, null, "content", null, null, objectReader); assertThat(header, is(notNullValue())); @@ -93,7 +92,7 @@ public void shouldGetContentType() throws Exception { } @Test - public void shouldGetNullContentTypeIfMissing() throws Exception { + public void shouldGetNullContentTypeIfMissing() { BasicHeader header = new BasicHeader(null, null, null, null, null, objectReader); assertThat(header, is(notNullValue())); @@ -101,7 +100,7 @@ public void shouldGetNullContentTypeIfMissing() throws Exception { } @Test - public void shouldGetKeyId() throws Exception { + public void shouldGetKeyId() { BasicHeader header = new BasicHeader(null, null, null, "key", null, objectReader); assertThat(header, is(notNullValue())); @@ -110,7 +109,7 @@ public void shouldGetKeyId() throws Exception { } @Test - public void shouldGetNullKeyIdIfMissing() throws Exception { + public void shouldGetNullKeyIdIfMissing() { BasicHeader header = new BasicHeader(null, null, null, null, null, objectReader); assertThat(header, is(notNullValue())); @@ -118,7 +117,7 @@ public void shouldGetNullKeyIdIfMissing() throws Exception { } @Test - public void shouldGetExtraClaim() throws Exception { + public void shouldGetExtraClaim() { Map tree = new HashMap<>(); tree.put("extraClaim", new TextNode("extraValue")); BasicHeader header = new BasicHeader(null, null, null, null, tree, objectReader); @@ -129,7 +128,7 @@ public void shouldGetExtraClaim() throws Exception { } @Test - public void shouldGetNotNullExtraClaimIfMissing() throws Exception { + public void shouldGetNotNullExtraClaimIfMissing() { Map tree = new HashMap<>(); BasicHeader header = new BasicHeader(null, null, null, null, tree, objectReader); @@ -137,4 +136,4 @@ public void shouldGetNotNullExtraClaimIfMissing() throws Exception { assertThat(header.getHeaderClaim("missing"), is(notNullValue())); assertThat(header.getHeaderClaim("missing"), is(instanceOf(NullClaim.class))); } -} \ No newline at end of file +} diff --git a/lib/src/test/java/com/auth0/jwt/impl/ClaimsHolderTest.java b/lib/src/test/java/com/auth0/jwt/impl/ClaimsHolderTest.java index 87cc9800..b2f97ff6 100644 --- a/lib/src/test/java/com/auth0/jwt/impl/ClaimsHolderTest.java +++ b/lib/src/test/java/com/auth0/jwt/impl/ClaimsHolderTest.java @@ -13,7 +13,7 @@ public class ClaimsHolderTest { @SuppressWarnings("RedundantCast") @Test - public void shouldGetClaims() throws Exception { + public void shouldGetClaims() { HashMap claims = new HashMap<>(); claims.put("iss", "auth0"); ClaimsHolder holder = new ClaimsHolder(claims); @@ -24,10 +24,10 @@ public void shouldGetClaims() throws Exception { } @Test - public void shouldGetNotNullClaims() throws Exception { + public void shouldGetNotNullClaims() { ClaimsHolder holder = new ClaimsHolder(null); assertThat(holder, is(notNullValue())); assertThat(holder.getClaims(), is(notNullValue())); assertThat(holder.getClaims(), is(instanceOf(Map.class))); } -} \ No newline at end of file +} diff --git a/lib/src/test/java/com/auth0/jwt/impl/HeaderDeserializerTest.java b/lib/src/test/java/com/auth0/jwt/impl/HeaderDeserializerTest.java index dffcab50..655a8c3c 100644 --- a/lib/src/test/java/com/auth0/jwt/impl/HeaderDeserializerTest.java +++ b/lib/src/test/java/com/auth0/jwt/impl/HeaderDeserializerTest.java @@ -30,14 +30,13 @@ import static org.mockito.Mockito.when; public class HeaderDeserializerTest { - @Rule public ExpectedException exception = ExpectedException.none(); private HeaderDeserializer deserializer; private ObjectReader objectReader = new ObjectMapper().reader(); @Before - public void setUp() throws Exception { + public void setUp() { deserializer = new HeaderDeserializer(objectReader); } @@ -88,7 +87,7 @@ public void shouldNotRemoveKnownPublicClaimsFromTree() throws Exception { } @Test - public void shouldGetNullStringWhenParsingNullNode() throws Exception { + public void shouldGetNullStringWhenParsingNullNode() { Map tree = new HashMap<>(); NullNode node = NullNode.getInstance(); tree.put("key", node); @@ -98,7 +97,7 @@ public void shouldGetNullStringWhenParsingNullNode() throws Exception { } @Test - public void shouldGetNullStringWhenParsingNull() throws Exception { + public void shouldGetNullStringWhenParsingNull() { Map tree = new HashMap<>(); tree.put("key", null); @@ -107,7 +106,7 @@ public void shouldGetNullStringWhenParsingNull() throws Exception { } @Test - public void shouldGetStringWhenParsingTextNode() throws Exception { + public void shouldGetStringWhenParsingTextNode() { Map tree = new HashMap<>(); TextNode node = new TextNode("something here"); tree.put("key", node); @@ -116,4 +115,4 @@ public void shouldGetStringWhenParsingTextNode() throws Exception { assertThat(text, is(notNullValue())); assertThat(text, is("something here")); } -} \ No newline at end of file +} diff --git a/lib/src/test/java/com/auth0/jwt/impl/JWTParserTest.java b/lib/src/test/java/com/auth0/jwt/impl/JWTParserTest.java index 4f97b2bd..1592bf8b 100644 --- a/lib/src/test/java/com/auth0/jwt/impl/JWTParserTest.java +++ b/lib/src/test/java/com/auth0/jwt/impl/JWTParserTest.java @@ -21,18 +21,17 @@ import static org.mockito.Mockito.when; public class JWTParserTest { - @Rule public ExpectedException exception = ExpectedException.none(); private JWTParser parser; @Before - public void setUp() throws Exception { + public void setUp() { parser = new JWTParser(); } @Test - public void shouldGetDefaultObjectMapper() throws Exception { + public void shouldGetDefaultObjectMapper() { ObjectMapper mapper = getDefaultObjectMapper(); assertThat(mapper, is(notNullValue())); assertThat(mapper, is(instanceOf(ObjectMapper.class))); @@ -40,7 +39,7 @@ public void shouldGetDefaultObjectMapper() throws Exception { } @Test - public void shouldAddDeserializers() throws Exception { + public void shouldAddDeserializers() { ObjectMapper mapper = mock(ObjectMapper.class); new JWTParser(mapper); verify(mapper).registerModule(any(Module.class)); @@ -58,7 +57,7 @@ public void shouldParsePayload() throws Exception { } @Test - public void shouldThrowOnInvalidPayload() throws Exception { + public void shouldThrowOnInvalidPayload() { String jsonPayload = "{{"; exception.expect(JWTDecodeException.class); exception.expectMessage(String.format("The string '%s' doesn't have a valid JSON format.", jsonPayload)); @@ -78,7 +77,7 @@ public void shouldParseHeader() throws Exception { } @Test - public void shouldThrowOnInvalidHeader() throws Exception { + public void shouldThrowOnInvalidHeader() { String jsonHeader = "}}"; exception.expect(JWTDecodeException.class); exception.expectMessage(String.format("The string '%s' doesn't have a valid JSON format.", jsonHeader)); @@ -87,28 +86,28 @@ public void shouldThrowOnInvalidHeader() throws Exception { } @Test - public void shouldThrowWhenConvertingHeaderIfNullJson() throws Exception { + public void shouldThrowWhenConvertingHeaderIfNullJson() { exception.expect(JWTDecodeException.class); exception.expectMessage("The string 'null' doesn't have a valid JSON format."); parser.parseHeader(null); } @Test - public void shouldThrowWhenConvertingHeaderFromInvalidJson() throws Exception { + public void shouldThrowWhenConvertingHeaderFromInvalidJson() { exception.expect(JWTDecodeException.class); exception.expectMessage("The string '}{' doesn't have a valid JSON format."); parser.parseHeader("}{"); } @Test - public void shouldThrowWhenConvertingPayloadIfNullJson() throws Exception { + public void shouldThrowWhenConvertingPayloadIfNullJson() { exception.expect(JWTDecodeException.class); exception.expectMessage("The string 'null' doesn't have a valid JSON format."); parser.parsePayload(null); } @Test - public void shouldThrowWhenConvertingPayloadFromInvalidJson() throws Exception { + public void shouldThrowWhenConvertingPayloadFromInvalidJson() { exception.expect(JWTDecodeException.class); exception.expectMessage("The string '}{' doesn't have a valid JSON format."); parser.parsePayload("}{"); diff --git a/lib/src/test/java/com/auth0/jwt/impl/JsonNodeClaimTest.java b/lib/src/test/java/com/auth0/jwt/impl/JsonNodeClaimTest.java index 1a82fcec..712f2b0e 100644 --- a/lib/src/test/java/com/auth0/jwt/impl/JsonNodeClaimTest.java +++ b/lib/src/test/java/com/auth0/jwt/impl/JsonNodeClaimTest.java @@ -23,7 +23,6 @@ import java.util.*; import static com.auth0.jwt.impl.JWTParser.getDefaultObjectMapper; -import static com.auth0.jwt.impl.JsonNodeClaim.claimFromNode; import static org.hamcrest.Matchers.*; import static org.hamcrest.core.IsNull.notNullValue; import static org.hamcrest.core.IsNull.nullValue; @@ -31,7 +30,6 @@ import static org.mockito.Mockito.*; public class JsonNodeClaimTest { - private ObjectMapper mapper; private ObjectReader objectReader; @@ -39,13 +37,13 @@ public class JsonNodeClaimTest { public ExpectedException exception = ExpectedException.none(); @Before - public void setUp() throws Exception { + public void setUp() { mapper = getDefaultObjectMapper(); objectReader = mapper.reader(); } @Test - public void shouldGetBooleanValue() throws Exception { + public void shouldGetBooleanValue() { JsonNode value = mapper.valueToTree(true); Claim claim = claimFromNode(value); @@ -58,7 +56,7 @@ private Claim claimFromNode(JsonNode value) { } @Test - public void shouldGetNullBooleanIfNotBooleanValue() throws Exception { + public void shouldGetNullBooleanIfNotBooleanValue() { JsonNode objectValue = mapper.valueToTree(new Object()); assertThat(claimFromNode(objectValue).asBoolean(), is(nullValue())); JsonNode stringValue = mapper.valueToTree("boolean"); @@ -66,7 +64,7 @@ public void shouldGetNullBooleanIfNotBooleanValue() throws Exception { } @Test - public void shouldGetIntValue() throws Exception { + public void shouldGetIntValue() { JsonNode value = mapper.valueToTree(123); Claim claim = claimFromNode(value); @@ -75,7 +73,7 @@ public void shouldGetIntValue() throws Exception { } @Test - public void shouldGetNullIntIfNotIntValue() throws Exception { + public void shouldGetNullIntIfNotIntValue() { JsonNode objectValue = mapper.valueToTree(new Object()); assertThat(claimFromNode(objectValue).asInt(), is(nullValue())); JsonNode stringValue = mapper.valueToTree("123"); @@ -83,7 +81,7 @@ public void shouldGetNullIntIfNotIntValue() throws Exception { } @Test - public void shouldGetLongValue() throws Exception { + public void shouldGetLongValue() { JsonNode value = mapper.valueToTree(Long.MAX_VALUE); Claim claim = claimFromNode(value); @@ -92,7 +90,7 @@ public void shouldGetLongValue() throws Exception { } @Test - public void shouldGetNullLongIfNotIntValue() throws Exception { + public void shouldGetNullLongIfNotIntValue() { JsonNode objectValue = mapper.valueToTree(new Object()); assertThat(claimFromNode(objectValue).asLong(), is(nullValue())); JsonNode stringValue = mapper.valueToTree("" + Long.MAX_VALUE); @@ -100,7 +98,7 @@ public void shouldGetNullLongIfNotIntValue() throws Exception { } @Test - public void shouldGetDoubleValue() throws Exception { + public void shouldGetDoubleValue() { JsonNode value = mapper.valueToTree(1.5); Claim claim = claimFromNode(value); @@ -109,7 +107,7 @@ public void shouldGetDoubleValue() throws Exception { } @Test - public void shouldGetNullDoubleIfNotDoubleValue() throws Exception { + public void shouldGetNullDoubleIfNotDoubleValue() { JsonNode objectValue = mapper.valueToTree(new Object()); assertThat(claimFromNode(objectValue).asDouble(), is(nullValue())); JsonNode stringValue = mapper.valueToTree("123.23"); @@ -117,7 +115,7 @@ public void shouldGetNullDoubleIfNotDoubleValue() throws Exception { } @Test - public void shouldGetDateValue() throws Exception { + public void shouldGetDateValue() { JsonNode value = mapper.valueToTree(1476824844L); Claim claim = claimFromNode(value); @@ -126,7 +124,7 @@ public void shouldGetDateValue() throws Exception { } @Test - public void shouldGetNullDateIfNotDateValue() throws Exception { + public void shouldGetNullDateIfNotDateValue() { JsonNode objectValue = mapper.valueToTree(new Object()); assertThat(claimFromNode(objectValue).asDate(), is(nullValue())); JsonNode stringValue = mapper.valueToTree("1476824844"); @@ -134,7 +132,7 @@ public void shouldGetNullDateIfNotDateValue() throws Exception { } @Test - public void shouldGetStringValue() throws Exception { + public void shouldGetStringValue() { JsonNode value = mapper.valueToTree("string"); Claim claim = claimFromNode(value); @@ -143,7 +141,7 @@ public void shouldGetStringValue() throws Exception { } @Test - public void shouldGetNullStringIfNotStringValue() throws Exception { + public void shouldGetNullStringIfNotStringValue() { JsonNode objectValue = mapper.valueToTree(new Object()); assertThat(claimFromNode(objectValue).asString(), is(nullValue())); JsonNode intValue = mapper.valueToTree(12345); @@ -151,7 +149,7 @@ public void shouldGetNullStringIfNotStringValue() throws Exception { } @Test - public void shouldGetArrayValueOfCustomClass() throws Exception { + public void shouldGetArrayValueOfCustomClass() { JsonNode value = mapper.valueToTree(new UserPojo[]{new UserPojo("George", 1), new UserPojo("Mark", 2)}); Claim claim = claimFromNode(value); @@ -160,7 +158,7 @@ public void shouldGetArrayValueOfCustomClass() throws Exception { } @Test - public void shouldGetArrayValue() throws Exception { + public void shouldGetArrayValue() { JsonNode value = mapper.valueToTree(new String[]{"string1", "string2"}); Claim claim = claimFromNode(value); @@ -169,7 +167,7 @@ public void shouldGetArrayValue() throws Exception { } @Test - public void shouldGetNullArrayIfNullValue() throws Exception { + public void shouldGetNullArrayIfNullValue() { JsonNode value = mapper.valueToTree(null); Claim claim = claimFromNode(value); @@ -177,7 +175,7 @@ public void shouldGetNullArrayIfNullValue() throws Exception { } @Test - public void shouldGetNullArrayIfNonArrayValue() throws Exception { + public void shouldGetNullArrayIfNonArrayValue() { JsonNode value = mapper.valueToTree(1); Claim claim = claimFromNode(value); @@ -185,7 +183,7 @@ public void shouldGetNullArrayIfNonArrayValue() throws Exception { } @Test - public void shouldThrowIfArrayClassMismatch() throws Exception { + public void shouldThrowIfArrayClassMismatch() { JsonNode value = mapper.valueToTree(new String[]{"keys", "values"}); Claim claim = claimFromNode(value); @@ -194,7 +192,7 @@ public void shouldThrowIfArrayClassMismatch() throws Exception { } @Test - public void shouldGetListValueOfCustomClass() throws Exception { + public void shouldGetListValueOfCustomClass() { JsonNode value = mapper.valueToTree(Arrays.asList(new UserPojo("George", 1), new UserPojo("Mark", 2))); Claim claim = claimFromNode(value); @@ -203,7 +201,7 @@ public void shouldGetListValueOfCustomClass() throws Exception { } @Test - public void shouldGetListValue() throws Exception { + public void shouldGetListValue() { JsonNode value = mapper.valueToTree(Arrays.asList("string1", "string2")); Claim claim = claimFromNode(value); @@ -212,7 +210,7 @@ public void shouldGetListValue() throws Exception { } @Test - public void shouldGetNullListIfNullValue() throws Exception { + public void shouldGetNullListIfNullValue() { JsonNode value = mapper.valueToTree(null); Claim claim = claimFromNode(value); @@ -220,7 +218,7 @@ public void shouldGetNullListIfNullValue() throws Exception { } @Test - public void shouldGetNullListIfNonArrayValue() throws Exception { + public void shouldGetNullListIfNonArrayValue() { JsonNode value = mapper.valueToTree(1); Claim claim = claimFromNode(value); @@ -228,7 +226,7 @@ public void shouldGetNullListIfNonArrayValue() throws Exception { } @Test - public void shouldThrowIfListClassMismatch() throws Exception { + public void shouldThrowIfListClassMismatch() { JsonNode value = mapper.valueToTree(new String[]{"keys", "values"}); Claim claim = claimFromNode(value); @@ -237,7 +235,7 @@ public void shouldThrowIfListClassMismatch() throws Exception { } @Test - public void shouldGetNullMapIfNullValue() throws Exception { + public void shouldGetNullMapIfNullValue() { JsonNode value = mapper.valueToTree(null); Claim claim = claimFromNode(value); @@ -245,7 +243,7 @@ public void shouldGetNullMapIfNullValue() throws Exception { } @Test - public void shouldGetNullMapIfNonArrayValue() throws Exception { + public void shouldGetNullMapIfNonArrayValue() { JsonNode value = mapper.valueToTree(1); Claim claim = claimFromNode(value); @@ -253,7 +251,7 @@ public void shouldGetNullMapIfNonArrayValue() throws Exception { } @Test - public void shouldGetMapValue() throws Exception { + public void shouldGetMapValue() { Map map = new HashMap<>(); map.put("text", "extraValue"); map.put("number", 12); @@ -292,7 +290,7 @@ public void shouldThrowIfAnExtraordinaryExceptionHappensWhenParsingAsGenericMap( } @Test - public void shouldGetCustomClassValue() throws Exception { + public void shouldGetCustomClassValue() { JsonNode value = mapper.valueToTree(new UserPojo("john", 123)); Claim claim = claimFromNode(value); @@ -302,7 +300,7 @@ public void shouldGetCustomClassValue() throws Exception { } @Test - public void shouldThrowIfCustomClassMismatch() throws Exception { + public void shouldThrowIfCustomClassMismatch() { JsonNode value = mapper.valueToTree(new UserPojo("john", 123)); Claim claim = claimFromNode(value); @@ -312,7 +310,7 @@ public void shouldThrowIfCustomClassMismatch() throws Exception { @SuppressWarnings({"unchecked", "RedundantCast"}) @Test - public void shouldGetAsMapValue() throws Exception { + public void shouldGetAsMapValue() { JsonNode value = mapper.valueToTree(Collections.singletonMap("key", new UserPojo("john", 123))); Claim claim = claimFromNode(value); @@ -323,7 +321,7 @@ public void shouldGetAsMapValue() throws Exception { } @Test - public void shouldReturnBaseClaimWhenParsingMissingNode() throws Exception { + public void shouldReturnBaseClaimWhenParsingMissingNode() { JsonNode value = MissingNode.getInstance(); Claim claim = claimFromNode(value); @@ -333,7 +331,7 @@ public void shouldReturnBaseClaimWhenParsingMissingNode() throws Exception { } @Test - public void shouldReturnBaseClaimWhenParsingNullNode() throws Exception { + public void shouldReturnBaseClaimWhenParsingNullNode() { JsonNode value = NullNode.getInstance(); Claim claim = claimFromNode(value); @@ -343,7 +341,7 @@ public void shouldReturnBaseClaimWhenParsingNullNode() throws Exception { } @Test - public void shouldReturnBaseClaimWhenParsingNullValue() throws Exception { + public void shouldReturnBaseClaimWhenParsingNullValue() { JsonNode value = mapper.valueToTree(null); Claim claim = claimFromNode(value); @@ -353,7 +351,7 @@ public void shouldReturnBaseClaimWhenParsingNullValue() throws Exception { } @Test - public void shouldReturnNonNullClaimWhenParsingObject() throws Exception { + public void shouldReturnNonNullClaimWhenParsingObject() { JsonNode value = mapper.valueToTree(new Object()); Claim claim = claimFromNode(value); @@ -363,7 +361,7 @@ public void shouldReturnNonNullClaimWhenParsingObject() throws Exception { } @Test - public void shouldReturnNonNullClaimWhenParsingArray() throws Exception { + public void shouldReturnNonNullClaimWhenParsingArray() { JsonNode value = mapper.valueToTree(new String[]{}); Claim claim = claimFromNode(value); @@ -373,7 +371,7 @@ public void shouldReturnNonNullClaimWhenParsingArray() throws Exception { } @Test - public void shouldReturnNonNullClaimWhenParsingList() throws Exception { + public void shouldReturnNonNullClaimWhenParsingList() { JsonNode value = mapper.valueToTree(new ArrayList()); Claim claim = claimFromNode(value); @@ -383,7 +381,7 @@ public void shouldReturnNonNullClaimWhenParsingList() throws Exception { } @Test - public void shouldReturnNonNullClaimWhenParsingStringValue() throws Exception { + public void shouldReturnNonNullClaimWhenParsingStringValue() { JsonNode value = mapper.valueToTree(""); Claim claim = claimFromNode(value); @@ -393,7 +391,7 @@ public void shouldReturnNonNullClaimWhenParsingStringValue() throws Exception { } @Test - public void shouldReturnNonNullClaimWhenParsingIntValue() throws Exception { + public void shouldReturnNonNullClaimWhenParsingIntValue() { JsonNode value = mapper.valueToTree(Integer.MAX_VALUE); Claim claim = claimFromNode(value); @@ -403,7 +401,7 @@ public void shouldReturnNonNullClaimWhenParsingIntValue() throws Exception { } @Test - public void shouldReturnNonNullClaimWhenParsingDoubleValue() throws Exception { + public void shouldReturnNonNullClaimWhenParsingDoubleValue() { JsonNode value = mapper.valueToTree(Double.MAX_VALUE); Claim claim = claimFromNode(value); @@ -413,7 +411,7 @@ public void shouldReturnNonNullClaimWhenParsingDoubleValue() throws Exception { } @Test - public void shouldReturnNonNullClaimWhenParsingDateValue() throws Exception { + public void shouldReturnNonNullClaimWhenParsingDateValue() { JsonNode value = mapper.valueToTree(new Date()); Claim claim = claimFromNode(value); @@ -423,7 +421,7 @@ public void shouldReturnNonNullClaimWhenParsingDateValue() throws Exception { } @Test - public void shouldReturnNonNullClaimWhenParsingBooleanValue() throws Exception { + public void shouldReturnNonNullClaimWhenParsingBooleanValue() { JsonNode value = mapper.valueToTree(Boolean.TRUE); Claim claim = claimFromNode(value); @@ -431,4 +429,4 @@ public void shouldReturnNonNullClaimWhenParsingBooleanValue() throws Exception { assertThat(claim, is(instanceOf(JsonNodeClaim.class))); assertThat(claim.isNull(), is(false)); } -} \ No newline at end of file +} diff --git a/lib/src/test/java/com/auth0/jwt/impl/NullClaimTest.java b/lib/src/test/java/com/auth0/jwt/impl/NullClaimTest.java index d8ddd516..9c1b49c5 100644 --- a/lib/src/test/java/com/auth0/jwt/impl/NullClaimTest.java +++ b/lib/src/test/java/com/auth0/jwt/impl/NullClaimTest.java @@ -11,63 +11,62 @@ public class NullClaimTest { private NullClaim claim; @Before - public void setUp() throws Exception { + public void setUp() { claim = new NullClaim(); } @Test - public void shouldBeNull() throws Exception { + public void shouldBeNull() { assertThat(claim.isNull(), is(true)); } @Test - public void shouldGetAsBoolean() throws Exception { + public void shouldGetAsBoolean() { assertThat(claim.asBoolean(), is(nullValue())); } @Test - public void shouldGetAsInt() throws Exception { + public void shouldGetAsInt() { assertThat(claim.asInt(), is(nullValue())); } @Test - public void shouldGetAsLong() throws Exception { + public void shouldGetAsLong() { assertThat(claim.asLong(), is(nullValue())); } @Test - public void shouldGetAsDouble() throws Exception { + public void shouldGetAsDouble() { assertThat(claim.asDouble(), is(nullValue())); } @Test - public void shouldGetAsString() throws Exception { + public void shouldGetAsString() { assertThat(claim.asString(), is(nullValue())); } @Test - public void shouldGetAsDate() throws Exception { + public void shouldGetAsDate() { assertThat(claim.asDate(), is(nullValue())); } @Test - public void shouldGetAsArray() throws Exception { + public void shouldGetAsArray() { assertThat(claim.asArray(Object.class), is(nullValue())); } @Test - public void shouldGetAsList() throws Exception { + public void shouldGetAsList() { assertThat(claim.asList(Object.class), is(nullValue())); } @Test - public void shouldGetAsMap() throws Exception { + public void shouldGetAsMap() { assertThat(claim.asMap(), is(nullValue())); } @Test - public void shouldGetAsCustomClass() throws Exception { + public void shouldGetAsCustomClass() { assertThat(claim.as(Object.class), is(nullValue())); } - -} \ No newline at end of file +} 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 9a82878e..fe9ce45d 100644 --- a/lib/src/test/java/com/auth0/jwt/impl/PayloadDeserializerTest.java +++ b/lib/src/test/java/com/auth0/jwt/impl/PayloadDeserializerTest.java @@ -35,7 +35,7 @@ public class PayloadDeserializerTest { private PayloadDeserializer deserializer; @Before - public void setUp() throws Exception { + public void setUp() { deserializer = new PayloadDeserializer(new ObjectMapper().reader()); } @@ -109,7 +109,7 @@ public void shouldNotRemoveKnownPublicClaimsFromTree() throws Exception { } @Test - public void shouldGetStringArrayWhenParsingArrayNode() throws Exception { + public void shouldGetStringArrayWhenParsingArrayNode() { Map tree = new HashMap<>(); List subNodes = new ArrayList<>(); TextNode textNode1 = new TextNode("one"); @@ -126,7 +126,7 @@ public void shouldGetStringArrayWhenParsingArrayNode() throws Exception { } @Test - public void shouldGetStringArrayWhenParsingTextNode() throws Exception { + public void shouldGetStringArrayWhenParsingTextNode() { Map tree = new HashMap<>(); TextNode textNode = new TextNode("something"); tree.put("key", textNode); @@ -138,7 +138,7 @@ public void shouldGetStringArrayWhenParsingTextNode() throws Exception { } @Test - public void shouldGetEmptyStringArrayWhenParsingEmptyTextNode() throws Exception { + public void shouldGetEmptyStringArrayWhenParsingEmptyTextNode() { Map tree = new HashMap<>(); TextNode textNode = new TextNode(""); tree.put("key", textNode); @@ -149,7 +149,7 @@ public void shouldGetEmptyStringArrayWhenParsingEmptyTextNode() throws Exception } @Test - public void shouldGetNullArrayWhenParsingNullNode() throws Exception { + public void shouldGetNullArrayWhenParsingNullNode() { Map tree = new HashMap<>(); NullNode node = NullNode.getInstance(); tree.put("key", node); @@ -159,7 +159,7 @@ public void shouldGetNullArrayWhenParsingNullNode() throws Exception { } @Test - public void shouldGetNullArrayWhenParsingNullNodeValue() throws Exception { + public void shouldGetNullArrayWhenParsingNullNodeValue() { Map tree = new HashMap<>(); tree.put("key", null); @@ -168,7 +168,7 @@ public void shouldGetNullArrayWhenParsingNullNodeValue() throws Exception { } @Test - public void shouldGetNullArrayWhenParsingNonArrayOrTextNode() throws Exception { + public void shouldGetNullArrayWhenParsingNonArrayOrTextNode() { Map tree = new HashMap<>(); IntNode node = new IntNode(456789); tree.put("key", node); @@ -179,7 +179,7 @@ public void shouldGetNullArrayWhenParsingNonArrayOrTextNode() throws Exception { @Test - public void shouldGetNullDateWhenParsingNullNode() throws Exception { + public void shouldGetNullDateWhenParsingNullNode() { Map tree = new HashMap<>(); NullNode node = NullNode.getInstance(); tree.put("key", node); @@ -189,7 +189,7 @@ public void shouldGetNullDateWhenParsingNullNode() throws Exception { } @Test - public void shouldGetNullDateWhenParsingNull() throws Exception { + public void shouldGetNullDateWhenParsingNull() { Map tree = new HashMap<>(); tree.put("key", null); @@ -198,7 +198,7 @@ public void shouldGetNullDateWhenParsingNull() throws Exception { } @Test - public void shouldThrowWhenParsingNonNumericNode() throws Exception { + public void shouldThrowWhenParsingNonNumericNode() { exception.expect(JWTDecodeException.class); exception.expectMessage("The claim 'key' contained a non-numeric date value."); @@ -210,7 +210,7 @@ public void shouldThrowWhenParsingNonNumericNode() throws Exception { } @Test - public void shouldGetDateWhenParsingNumericNode() throws Exception { + public void shouldGetDateWhenParsingNumericNode() { Map tree = new HashMap<>(); long seconds = 1478627949 / 1000; LongNode node = new LongNode(seconds); @@ -222,7 +222,7 @@ public void shouldGetDateWhenParsingNumericNode() throws Exception { } @Test - public void shouldGetLargeDateWhenParsingNumericNode() throws Exception { + public void shouldGetLargeDateWhenParsingNumericNode() { Map tree = new HashMap<>(); long seconds = Integer.MAX_VALUE + 10000L; LongNode node = new LongNode(seconds); @@ -235,7 +235,7 @@ public void shouldGetLargeDateWhenParsingNumericNode() throws Exception { } @Test - public void shouldGetNullStringWhenParsingNullNode() throws Exception { + public void shouldGetNullStringWhenParsingNullNode() { Map tree = new HashMap<>(); NullNode node = NullNode.getInstance(); tree.put("key", node); @@ -245,7 +245,7 @@ public void shouldGetNullStringWhenParsingNullNode() throws Exception { } @Test - public void shouldGetNullStringWhenParsingNull() throws Exception { + public void shouldGetNullStringWhenParsingNull() { Map tree = new HashMap<>(); tree.put("key", null); @@ -254,7 +254,7 @@ public void shouldGetNullStringWhenParsingNull() throws Exception { } @Test - public void shouldGetStringWhenParsingTextNode() throws Exception { + public void shouldGetStringWhenParsingTextNode() { Map tree = new HashMap<>(); TextNode node = new TextNode("something here"); tree.put("key", node); @@ -263,5 +263,4 @@ public void shouldGetStringWhenParsingTextNode() throws Exception { assertThat(text, is(notNullValue())); assertThat(text, is("something here")); } - -} \ No newline at end of file +} diff --git a/lib/src/test/java/com/auth0/jwt/impl/PayloadImplTest.java b/lib/src/test/java/com/auth0/jwt/impl/PayloadImplTest.java index 56654427..1960b6e5 100644 --- a/lib/src/test/java/com/auth0/jwt/impl/PayloadImplTest.java +++ b/lib/src/test/java/com/auth0/jwt/impl/PayloadImplTest.java @@ -23,7 +23,6 @@ import static org.hamcrest.Matchers.*; public class PayloadImplTest { - @Rule public ExpectedException exception = ExpectedException.none(); @@ -36,7 +35,7 @@ public class PayloadImplTest { private ObjectReader objectReader; @Before - public void setUp() throws Exception { + public void setUp() { mapper = getDefaultObjectMapper(); objectReader = mapper.reader(); @@ -50,40 +49,40 @@ public void setUp() throws Exception { @SuppressWarnings("Convert2Diamond") @Test - public void shouldHaveUnmodifiableTree() throws Exception { + public void shouldHaveUnmodifiableTree() { exception.expect(UnsupportedOperationException.class); PayloadImpl payload = new PayloadImpl(null, null, null, null, null, null, null, new HashMap(), objectReader); payload.getTree().put("something", null); } @Test - public void shouldGetIssuer() throws Exception { + public void shouldGetIssuer() { assertThat(payload, is(notNullValue())); assertThat(payload.getIssuer(), is("issuer")); } @Test - public void shouldGetNullIssuerIfMissing() throws Exception { + public void shouldGetNullIssuerIfMissing() { PayloadImpl payload = new PayloadImpl(null, null, null, null, null, null, null, null, objectReader); assertThat(payload, is(notNullValue())); assertThat(payload.getIssuer(), is(nullValue())); } @Test - public void shouldGetSubject() throws Exception { + public void shouldGetSubject() { assertThat(payload, is(notNullValue())); assertThat(payload.getSubject(), is("subject")); } @Test - public void shouldGetNullSubjectIfMissing() throws Exception { + public void shouldGetNullSubjectIfMissing() { PayloadImpl payload = new PayloadImpl(null, null, null, null, null, null, null, null, objectReader); assertThat(payload, is(notNullValue())); assertThat(payload.getSubject(), is(nullValue())); } @Test - public void shouldGetAudience() throws Exception { + public void shouldGetAudience() { assertThat(payload, is(notNullValue())); assertThat(payload.getAudience(), is(IsCollectionWithSize.hasSize(1))); @@ -91,73 +90,73 @@ public void shouldGetAudience() throws Exception { } @Test - public void shouldGetNullAudienceIfMissing() throws Exception { + public void shouldGetNullAudienceIfMissing() { PayloadImpl payload = new PayloadImpl(null, null, null, null, null, null, null, null, objectReader); assertThat(payload, is(notNullValue())); assertThat(payload.getAudience(), is(nullValue())); } @Test - public void shouldGetExpiresAt() throws Exception { + public void shouldGetExpiresAt() { assertThat(payload, is(notNullValue())); assertThat(payload.getExpiresAt(), is(expiresAt)); } @Test - public void shouldGetNullExpiresAtIfMissing() throws Exception { + public void shouldGetNullExpiresAtIfMissing() { PayloadImpl payload = new PayloadImpl(null, null, null, null, null, null, null, null, objectReader); assertThat(payload, is(notNullValue())); assertThat(payload.getExpiresAt(), is(nullValue())); } @Test - public void shouldGetNotBefore() throws Exception { + public void shouldGetNotBefore() { assertThat(payload, is(notNullValue())); assertThat(payload.getNotBefore(), is(notBefore)); } @Test - public void shouldGetNullNotBeforeIfMissing() throws Exception { + public void shouldGetNullNotBeforeIfMissing() { PayloadImpl payload = new PayloadImpl(null, null, null, null, null, null, null, null, objectReader); assertThat(payload, is(notNullValue())); assertThat(payload.getNotBefore(), is(nullValue())); } @Test - public void shouldGetIssuedAt() throws Exception { + public void shouldGetIssuedAt() { assertThat(payload, is(notNullValue())); assertThat(payload.getIssuedAt(), is(issuedAt)); } @Test - public void shouldGetNullIssuedAtIfMissing() throws Exception { + public void shouldGetNullIssuedAtIfMissing() { PayloadImpl payload = new PayloadImpl(null, null, null, null, null, null, null, null, objectReader); assertThat(payload, is(notNullValue())); assertThat(payload.getIssuedAt(), is(nullValue())); } @Test - public void shouldGetJWTId() throws Exception { + public void shouldGetJWTId() { assertThat(payload, is(notNullValue())); assertThat(payload.getId(), is("jwtId")); } @Test - public void shouldGetNullJWTIdIfMissing() throws Exception { + public void shouldGetNullJWTIdIfMissing() { PayloadImpl payload = new PayloadImpl(null, null, null, null, null, null, null, null, objectReader); assertThat(payload, is(notNullValue())); assertThat(payload.getId(), is(nullValue())); } @Test - public void shouldGetExtraClaim() throws Exception { + public void shouldGetExtraClaim() { assertThat(payload, is(notNullValue())); assertThat(payload.getClaim("extraClaim"), is(instanceOf(JsonNodeClaim.class))); assertThat(payload.getClaim("extraClaim").asString(), is("extraValue")); } @Test - public void shouldGetNotNullExtraClaimIfMissing() throws Exception { + public void shouldGetNotNullExtraClaimIfMissing() { PayloadImpl payload = new PayloadImpl(null, null, null, null, null, null, null, null, objectReader); assertThat(payload, is(notNullValue())); assertThat(payload.getClaim("missing"), is(notNullValue())); @@ -165,7 +164,7 @@ public void shouldGetNotNullExtraClaimIfMissing() throws Exception { } @Test - public void shouldGetClaims() throws Exception { + public void shouldGetClaims() { Map tree = new HashMap<>(); tree.put("extraClaim", new TextNode("extraValue")); tree.put("sub", new TextNode("auth0")); @@ -179,11 +178,11 @@ public void shouldGetClaims() throws Exception { } @Test - public void shouldNotAllowToModifyClaimsMap() throws Exception { + public void shouldNotAllowToModifyClaimsMap() { assertThat(payload, is(notNullValue())); Map claims = payload.getClaims(); assertThat(claims, is(notNullValue())); exception.expect(UnsupportedOperationException.class); claims.put("name", null); } -} \ No newline at end of file +} diff --git a/lib/src/test/java/com/auth0/jwt/impl/PayloadSerializerTest.java b/lib/src/test/java/com/auth0/jwt/impl/PayloadSerializerTest.java index ebdd5864..44da5e54 100644 --- a/lib/src/test/java/com/auth0/jwt/impl/PayloadSerializerTest.java +++ b/lib/src/test/java/com/auth0/jwt/impl/PayloadSerializerTest.java @@ -18,7 +18,6 @@ import static org.junit.Assert.assertThat; public class PayloadSerializerTest { - private StringWriter writer; private PayloadSerializer serializer; private JsonGenerator jsonGenerator; @@ -120,7 +119,7 @@ public void shouldSerializeCustomDateInSeconds() throws Exception { public void shouldSerializeDatesUsingLong() throws Exception { long secs = Integer.MAX_VALUE + 10000L; Date date = new Date(secs * 1000L); - Map claims = new HashMap(); + Map claims = new HashMap<>(); claims.put("iat", date); claims.put("nbf", date); claims.put("exp", date); @@ -219,5 +218,4 @@ private ClaimsHolder holderFor(String key, Object value) { map.put(key, value); return new ClaimsHolder(map); } - -} \ No newline at end of file +} From 6210713fa520685aaa1b448bb398f57eff18ad38 Mon Sep 17 00:00:00 2001 From: Jim Anderson Date: Thu, 5 Mar 2020 15:44:51 -0600 Subject: [PATCH 10/14] Updated date time APIs (#392) * Update to new Date/Time APIs * Update to support Instant claims nested in maps and lists * Add back Date APIs * add back date to custom list and map claims * add Date-based claims back to Verifier * Update README.md Co-Authored-By: Luciano Balmaceda * Add comments for future deprecations, rename serialization method name * Updated tests per review feedback * Remove unnecessary throws from tests, fix up from rebase * remove duplicated code * review feedback - simplify serialization Co-authored-by: Luciano Balmaceda --- README.md | 12 +- .../main/java/com/auth0/jwt/ClockImpl.java | 6 + .../main/java/com/auth0/jwt/JWTCreator.java | 65 ++++- .../main/java/com/auth0/jwt/JWTDecoder.java | 16 ++ .../main/java/com/auth0/jwt/JWTVerifier.java | 40 ++-- .../com/auth0/jwt/impl/JsonNodeClaim.java | 9 +- .../java/com/auth0/jwt/impl/NullClaim.java | 6 + .../auth0/jwt/impl/PayloadDeserializer.java | 12 +- .../java/com/auth0/jwt/impl/PayloadImpl.java | 30 ++- .../com/auth0/jwt/impl/PayloadSerializer.java | 49 +++- .../java/com/auth0/jwt/interfaces/Claim.java | 10 + .../java/com/auth0/jwt/interfaces/Clock.java | 11 +- .../com/auth0/jwt/interfaces/Payload.java | 25 ++ .../auth0/jwt/interfaces/Verification.java | 12 + .../java/com/auth0/jwt/ClockImplTest.java | 10 +- .../java/com/auth0/jwt/JWTCreatorTest.java | 93 +++++-- .../java/com/auth0/jwt/JWTDecoderTest.java | 51 +++- lib/src/test/java/com/auth0/jwt/JWTTest.java | 84 ++++++- .../java/com/auth0/jwt/JWTVerifierTest.java | 226 +++++++++++++++--- .../com/auth0/jwt/impl/JsonNodeClaimTest.java | 5 + .../com/auth0/jwt/impl/NullClaimTest.java | 5 + .../jwt/impl/PayloadDeserializerTest.java | 36 +-- .../com/auth0/jwt/impl/PayloadImplTest.java | 30 ++- .../auth0/jwt/impl/PayloadSerializerTest.java | 32 +-- 24 files changed, 711 insertions(+), 164 deletions(-) diff --git a/README.md b/README.md index b63599a2..f9dd39f3 100644 --- a/README.md +++ b/README.md @@ -200,7 +200,7 @@ JWTVerifier verifier = JWT.require(algorithm) .build(); ``` -You can also specify a custom value for a given Date claim and override the default one for only that claim. +You can also specify a custom value for a given DateNumber claim and override the default one for only that claim. ```java JWTVerifier verifier = JWT.require(algorithm) @@ -319,7 +319,7 @@ List audience = jwt.getAudience(); Returns the Expiration Time value or null if it's not defined in the Payload. ```java -Date expiresAt = jwt.getExpiresAt(); +Instant expiresAt = jwt.getExpiresAt(); ``` #### Not Before ("nbf") @@ -327,7 +327,7 @@ Date expiresAt = jwt.getExpiresAt(); Returns the Not Before value or null if it's not defined in the Payload. ```java -Date notBefore = jwt.getNotBefore(); +Instant notBefore = jwt.getNotBefore(); ``` #### Issued At ("iat") @@ -335,7 +335,7 @@ Date notBefore = jwt.getNotBefore(); Returns the Issued At value or null if it's not defined in the Payload. ```java -Date issuedAt = jwt.getIssuedAt(); +Instant issuedAt = jwt.getIssuedAt(); ``` #### JWT ID ("jti") @@ -380,7 +380,7 @@ JWTVerifier verifier = JWT.require(algorithm) DecodedJWT jwt = verifier.verify("my.jwt.token"); ``` -> Currently supported classes for custom JWT Claim creation and verification are: Boolean, Integer, Double, String, Date and Arrays of type String and Integer. +> Currently supported classes for custom JWT Claim creation and verification are: Boolean, Integer, Double, String, Instant (for NumericDate) and Arrays of type String and Integer. ### Claim Class @@ -392,7 +392,7 @@ The Claim class is a wrapper for the Claim values. It allows you to get the Clai * **asDouble()**: Returns the Double value or null if it can't be converted. * **asLong()**: Returns the Long value or null if it can't be converted. * **asString()**: Returns the String value or null if it can't be converted. -* **asDate()**: Returns the Date value or null if it can't be converted. This must be a NumericDate (Unix Epoch/Timestamp). Note that the [JWT Standard](https://tools.ietf.org/html/rfc7519#section-2) specified that all the *NumericDate* values must be in seconds. +* **asInstant()**: Returns the Instant value or null if it can't be converted. This must be a NumericDate (Unix Epoch/Timestamp). Note that the [JWT Standard](https://tools.ietf.org/html/rfc7519#section-2) specified that all the *NumericDate* values must be in seconds. #### Custom Classes and Collections To obtain a Claim as a Collection you'll need to provide the **Class Type** of the contents to convert from. diff --git a/lib/src/main/java/com/auth0/jwt/ClockImpl.java b/lib/src/main/java/com/auth0/jwt/ClockImpl.java index 274d4417..4c0f1871 100644 --- a/lib/src/main/java/com/auth0/jwt/ClockImpl.java +++ b/lib/src/main/java/com/auth0/jwt/ClockImpl.java @@ -2,6 +2,7 @@ import com.auth0.jwt.interfaces.Clock; +import java.time.Instant; import java.util.Date; final class ClockImpl implements Clock { @@ -10,6 +11,11 @@ final class ClockImpl implements Clock { } + @Override + public Instant getNow() { + return Instant.now(); + } + @Override public Date getToday() { return new Date(); diff --git a/lib/src/main/java/com/auth0/jwt/JWTCreator.java b/lib/src/main/java/com/auth0/jwt/JWTCreator.java index 81c649de..625715bd 100644 --- a/lib/src/main/java/com/auth0/jwt/JWTCreator.java +++ b/lib/src/main/java/com/auth0/jwt/JWTCreator.java @@ -13,6 +13,7 @@ import org.apache.commons.codec.binary.Base64; import java.nio.charset.StandardCharsets; +import java.time.Instant; import java.util.Date; import java.util.HashMap; import java.util.List; @@ -140,33 +141,66 @@ public Builder withAudience(String... audience) { * @param expiresAt the Expires At value. * @return this same Builder instance. */ - public Builder withExpiresAt(Date expiresAt) { + public Builder withExpiresAt(Instant expiresAt) { addClaim(PublicClaims.EXPIRES_AT, expiresAt); return this; } + /** + * Add a specific Expires At ("exp") claim to the Payload. + * + * @param expiresAt the Expires At value. + * @return this same Builder instance. + */ + // TODO - Deprecate this method in favor of withExpiresAtInstant + public Builder withExpiresAt(Date expiresAt) { + return withExpiresAt(expiresAt.toInstant()); + } + /** * Add a specific Not Before ("nbf") claim to the Payload. * * @param notBefore the Not Before value. * @return this same Builder instance. */ - public Builder withNotBefore(Date notBefore) { + public Builder withNotBefore(Instant notBefore) { addClaim(PublicClaims.NOT_BEFORE, notBefore); return this; } + /** + * Add a specific Not Before ("nbf") claim to the Payload. + * + * @param notBefore the Not Before value. + * @return this same Builder instance. + */ + // TODO - Deprecate this method in favor of withNotBeforeInstant + public Builder withNotBefore(Date notBefore) { + return withNotBefore(notBefore.toInstant()); + } + /** * Add a specific Issued At ("iat") claim to the Payload. * * @param issuedAt the Issued At value. * @return this same Builder instance. */ - public Builder withIssuedAt(Date issuedAt) { + public Builder withIssuedAt(Instant issuedAt) { addClaim(PublicClaims.ISSUED_AT, issuedAt); return this; } + /** + * Add a specific Issued At ("iat") claim to the Payload. + * + * @param issuedAt the Issued At value. + * @return this same Builder instance. + */ + // TODO - Deprecate this method in favor of withIssuedAtInstant + public Builder withIssuedAt(Date issuedAt) { + return withIssuedAt(issuedAt.toInstant()); + } + /** * Add a specific JWT Id ("jti") claim to the Payload. * @@ -256,12 +290,25 @@ public Builder withClaim(String name, String value) throws IllegalArgumentExcept * @return this same Builder instance. * @throws IllegalArgumentException if the name is null. */ - public Builder withClaim(String name, Date value) throws IllegalArgumentException { + public Builder withClaim(String name, Instant value) throws IllegalArgumentException { assertNonNull(name); addClaim(name, value); return this; } + /** + * Add a custom Claim value. + * + * @param name the Claim's name. + * @param value the Claim's value. + * @return this same Builder instance. + * @throws IllegalArgumentException if the name is null. + */ + // TODO - Deprecate this method in favor of withClaim(String name, Instant value) + public Builder withClaim(String name, Date value) throws IllegalArgumentException { + return withClaim(name, value.toInstant()); + } + /** * Add a custom Array Claim with the given items. * @@ -309,7 +356,7 @@ public Builder withArrayClaim(String name, Long[] items) throws IllegalArgumentE * * Accepted nested types are {@linkplain Map} and {@linkplain List} with basic types * {@linkplain Boolean}, {@linkplain Integer}, {@linkplain Long}, {@linkplain Double}, - * {@linkplain String} and {@linkplain Date}. {@linkplain Map}s cannot contain null keys or values. + * {@linkplain String}, {@linkplain Instant}, and {@linkplain Date}. {@linkplain Map}s cannot contain null keys or values. * {@linkplain List}s can contain null elements. * * @param name the Claim's name. @@ -321,7 +368,7 @@ public Builder withClaim(String name, Map map) throws IllegalArgument assertNonNull(name); // validate map contents if(!validateClaim(map)) { - throw new IllegalArgumentException("Expected map containing Map, List, Boolean, Integer, Long, Double, String and Date"); + throw new IllegalArgumentException("Expected map containing Map, List, Boolean, Integer, Long, Double, String and Instant"); } addClaim(name, map); return this; @@ -332,7 +379,7 @@ public Builder withClaim(String name, Map map) throws IllegalArgument * * Accepted nested types are {@linkplain Map} and {@linkplain List} with basic types * {@linkplain Boolean}, {@linkplain Integer}, {@linkplain Long}, {@linkplain Double}, - * {@linkplain String} and {@linkplain Date}. {@linkplain Map}s cannot contain null keys or values. + * {@linkplain String}, {@linkplain Instant}, and {@linkplain Date}. {@linkplain Map}s cannot contain null keys or values. * {@linkplain List}s can contain null elements. * * @param name the Claim's name. @@ -344,7 +391,7 @@ public Builder withClaim(String name, List list) throws IllegalArgumentExcept assertNonNull(name); // validate list contents if(!validateClaim(list)) { - throw new IllegalArgumentException("Expected list containing Map, List, Boolean, Integer, Long, Double, String and Date"); + throw new IllegalArgumentException("Expected list containing Map, List, Boolean, Integer, Long, Double, String and Instant"); } addClaim(name, list); return this; @@ -391,7 +438,7 @@ private static boolean isBasicType(Object value) { if(c.isArray()) { return c == Integer[].class || c == Long[].class || c == String[].class; } - return c == String.class || c == Integer.class || c == Long.class || c == Double.class || c == Date.class || c == Boolean.class; + return c == String.class || c == Integer.class || c == Long.class || c == Double.class || c == Date.class || c == Instant.class || c == Boolean.class; } /** diff --git a/lib/src/main/java/com/auth0/jwt/JWTDecoder.java b/lib/src/main/java/com/auth0/jwt/JWTDecoder.java index ffaf25ec..2b35a9ab 100644 --- a/lib/src/main/java/com/auth0/jwt/JWTDecoder.java +++ b/lib/src/main/java/com/auth0/jwt/JWTDecoder.java @@ -10,6 +10,7 @@ import org.apache.commons.codec.binary.StringUtils; import java.io.Serializable; +import java.time.Instant; import java.util.Date; import java.util.List; import java.util.Map; @@ -83,6 +84,21 @@ public List getAudience() { return payload.getAudience(); } + @Override + public Instant getExpiresAtInstant() { + return payload.getExpiresAtInstant(); + } + + @Override + public Instant getNotBeforeInstant() { + return payload.getNotBeforeInstant(); + } + + @Override + public Instant getIssuedAtInstant() { + return payload.getIssuedAtInstant(); + } + @Override public Date getExpiresAt() { return payload.getExpiresAt(); diff --git a/lib/src/main/java/com/auth0/jwt/JWTVerifier.java b/lib/src/main/java/com/auth0/jwt/JWTVerifier.java index 7b0449d5..deca7b68 100644 --- a/lib/src/main/java/com/auth0/jwt/JWTVerifier.java +++ b/lib/src/main/java/com/auth0/jwt/JWTVerifier.java @@ -9,6 +9,8 @@ import com.auth0.jwt.interfaces.DecodedJWT; import com.auth0.jwt.interfaces.Verification; +import java.time.Duration; +import java.time.Instant; import java.util.*; /** @@ -148,6 +150,13 @@ public Verification withClaim(String name, String value) throws IllegalArgumentE return this; } + @Override + public Verification withClaim(String name, Instant value) throws IllegalArgumentException { + assertNonNull(name); + requireClaim(name, value); + return this; + } + @Override public Verification withClaim(String name, Date value) throws IllegalArgumentException { assertNonNull(name); @@ -270,13 +279,13 @@ private void verifyClaims(DecodedJWT jwt, Map claims) throws Tok assertValidAudienceClaim(jwt.getAudience(), (List) entry.getValue()); break; case PublicClaims.EXPIRES_AT: - assertValidDateClaim(jwt.getExpiresAt(), (Long) entry.getValue(), true); + assertValidInstantClaim(jwt.getExpiresAtInstant(), (Long) entry.getValue(), true); break; case PublicClaims.ISSUED_AT: - assertValidDateClaim(jwt.getIssuedAt(), (Long) entry.getValue(), false); + assertValidInstantClaim(jwt.getIssuedAtInstant(), (Long) entry.getValue(), false); break; case PublicClaims.NOT_BEFORE: - assertValidDateClaim(jwt.getNotBefore(), (Long) entry.getValue(), false); + assertValidInstantClaim(jwt.getNotBeforeInstant(), (Long) entry.getValue(), false); break; case PublicClaims.ISSUER: assertValidIssuerClaim(jwt.getIssuer(), (List) entry.getValue()); @@ -308,6 +317,8 @@ private void assertValidClaim(Claim claim, String claimName, Object value) { isValid = value.equals(claim.asDouble()); } else if (value instanceof Date) { isValid = value.equals(claim.asDate()); + } else if (value instanceof Instant) { + isValid = value.equals(claim.asInstant()); } else if (value instanceof Object[]) { List claimArr = Arrays.asList(claim.as(Object[].class)); List valueArr = Arrays.asList((Object[]) value); @@ -325,27 +336,24 @@ private void assertValidStringClaim(String claimName, String value, String expec } } - private void assertValidDateClaim(Date date, long leeway, boolean shouldBeFuture) { - Date today = clock.getToday(); - today.setTime(today.getTime() / 1000 * 1000); // truncate millis + private void assertValidInstantClaim(Instant claimVal, long leeway, boolean shouldBeFuture) { + Instant today = clock.getNow(); if (shouldBeFuture) { - assertDateIsFuture(date, leeway, today); + assertInstantIsFuture(claimVal, leeway, today); } else { - assertDateIsPast(date, leeway, today); + assertInstantIsPast(claimVal, leeway, today); } } - private void assertDateIsFuture(Date date, long leeway, Date today) { - today.setTime(today.getTime() - leeway * 1000); - if (date != null && today.after(date)) { - throw new TokenExpiredException(String.format("The Token has expired on %s.", date)); + private void assertInstantIsFuture(Instant claimVal, long leeway, Instant now) { + if (claimVal != null && now.minus(Duration.ofSeconds(leeway)).isAfter(claimVal)) { + throw new TokenExpiredException(String.format("The Token has expired on %s.", claimVal)); } } - private void assertDateIsPast(Date date, long leeway, Date today) { - today.setTime(today.getTime() + leeway * 1000); - if (date != null && today.before(date)) { - throw new InvalidClaimException(String.format("The Token can't be used before %s.", date)); + private void assertInstantIsPast(Instant claimVal, long leeway, Instant now) { + if (claimVal != null && now.plus(Duration.ofSeconds(leeway)).isBefore(claimVal)) { + throw new InvalidClaimException(String.format("The Token can't be used before %s.", claimVal)); } } diff --git a/lib/src/main/java/com/auth0/jwt/impl/JsonNodeClaim.java b/lib/src/main/java/com/auth0/jwt/impl/JsonNodeClaim.java index 8238091c..0bf6f6c2 100644 --- a/lib/src/main/java/com/auth0/jwt/impl/JsonNodeClaim.java +++ b/lib/src/main/java/com/auth0/jwt/impl/JsonNodeClaim.java @@ -10,6 +10,7 @@ import java.io.IOException; import java.lang.reflect.Array; +import java.time.Instant; import java.util.ArrayList; import java.util.Date; import java.util.List; @@ -55,11 +56,17 @@ public String asString() { @Override public Date asDate() { + Instant instant = asInstant(); + return (instant != null) ? Date.from(instant) : null; + } + + @Override + public Instant asInstant() { if (!data.canConvertToLong()) { return null; } long seconds = data.asLong(); - return new Date(seconds * 1000); + return Instant.ofEpochSecond(seconds); } @Override diff --git a/lib/src/main/java/com/auth0/jwt/impl/NullClaim.java b/lib/src/main/java/com/auth0/jwt/impl/NullClaim.java index 93bedbb1..fbb12726 100644 --- a/lib/src/main/java/com/auth0/jwt/impl/NullClaim.java +++ b/lib/src/main/java/com/auth0/jwt/impl/NullClaim.java @@ -3,6 +3,7 @@ import com.auth0.jwt.exceptions.JWTDecodeException; import com.auth0.jwt.interfaces.Claim; +import java.time.Instant; import java.util.Date; import java.util.List; import java.util.Map; @@ -41,6 +42,11 @@ public String asString() { return null; } + @Override + public Instant asInstant() { + return null; + } + @Override public Date asDate() { return null; 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 901a9385..2a332958 100644 --- a/lib/src/main/java/com/auth0/jwt/impl/PayloadDeserializer.java +++ b/lib/src/main/java/com/auth0/jwt/impl/PayloadDeserializer.java @@ -11,6 +11,7 @@ import com.fasterxml.jackson.databind.deser.std.StdDeserializer; import java.io.IOException; +import java.time.Instant; import java.util.*; class PayloadDeserializer extends StdDeserializer { @@ -37,9 +38,9 @@ public Payload deserialize(JsonParser p, DeserializationContext ctxt) throws IOE String issuer = getString(tree, PublicClaims.ISSUER); String subject = getString(tree, PublicClaims.SUBJECT); List audience = getStringOrArray(tree, PublicClaims.AUDIENCE); - Date expiresAt = getDateFromSeconds(tree, PublicClaims.EXPIRES_AT); - Date notBefore = getDateFromSeconds(tree, PublicClaims.NOT_BEFORE); - Date issuedAt = getDateFromSeconds(tree, PublicClaims.ISSUED_AT); + Instant expiresAt = getInstantFromSeconds(tree, PublicClaims.EXPIRES_AT); + Instant notBefore = getInstantFromSeconds(tree, PublicClaims.NOT_BEFORE); + Instant issuedAt = getInstantFromSeconds(tree, PublicClaims.ISSUED_AT); String jwtId = getString(tree, PublicClaims.JWT_ID); return new PayloadImpl(issuer, subject, audience, expiresAt, notBefore, issuedAt, jwtId, tree, objectReader); @@ -65,7 +66,7 @@ List getStringOrArray(Map tree, String claimName) thro return list; } - Date getDateFromSeconds(Map tree, String claimName) { + Instant getInstantFromSeconds(Map tree, String claimName) { JsonNode node = tree.get(claimName); if (node == null || node.isNull()) { return null; @@ -73,8 +74,7 @@ Date getDateFromSeconds(Map tree, String claimName) { if (!node.canConvertToLong()) { throw new JWTDecodeException(String.format("The claim '%s' contained a non-numeric date value.", claimName)); } - final long ms = node.asLong() * 1000; - return new Date(ms); + return Instant.ofEpochSecond(node.asLong()); } String getString(Map tree, String claimName) { diff --git a/lib/src/main/java/com/auth0/jwt/impl/PayloadImpl.java b/lib/src/main/java/com/auth0/jwt/impl/PayloadImpl.java index 38f7906d..bd7156dc 100644 --- a/lib/src/main/java/com/auth0/jwt/impl/PayloadImpl.java +++ b/lib/src/main/java/com/auth0/jwt/impl/PayloadImpl.java @@ -6,6 +6,7 @@ import com.fasterxml.jackson.databind.ObjectReader; import java.io.Serializable; +import java.time.Instant; import java.util.*; import static com.auth0.jwt.impl.JsonNodeClaim.extractClaim; @@ -19,14 +20,14 @@ class PayloadImpl implements Payload, Serializable { private final String issuer; private final String subject; private final List audience; - private final Date expiresAt; - private final Date notBefore; - private final Date issuedAt; + private final Instant expiresAt; + private final Instant notBefore; + private final Instant issuedAt; private final String jwtId; private final Map tree; private final ObjectReader objectReader; - PayloadImpl(String issuer, String subject, List audience, Date expiresAt, Date notBefore, Date issuedAt, String jwtId, Map tree, ObjectReader objectReader) { + PayloadImpl(String issuer, String subject, List audience, Instant expiresAt, Instant notBefore, Instant issuedAt, String jwtId, Map tree, ObjectReader objectReader) { this.issuer = issuer; this.subject = subject; this.audience = audience; @@ -58,20 +59,35 @@ public List getAudience() { } @Override - public Date getExpiresAt() { + public Instant getExpiresAtInstant() { return expiresAt; } @Override - public Date getNotBefore() { + public Instant getNotBeforeInstant() { return notBefore; } @Override - public Date getIssuedAt() { + public Instant getIssuedAtInstant() { return issuedAt; } + @Override + public Date getExpiresAt() { + return (expiresAt != null) ? Date.from(expiresAt) : null; + } + + @Override + public Date getIssuedAt() { + return (issuedAt != null) ? Date.from(issuedAt) : null; + } + + @Override + public Date getNotBefore() { + return (notBefore != null) ? Date.from(notBefore) : null; + } + @Override public String getId() { return jwtId; diff --git a/lib/src/main/java/com/auth0/jwt/impl/PayloadSerializer.java b/lib/src/main/java/com/auth0/jwt/impl/PayloadSerializer.java index ea9f95f1..69ada53b 100644 --- a/lib/src/main/java/com/auth0/jwt/impl/PayloadSerializer.java +++ b/lib/src/main/java/com/auth0/jwt/impl/PayloadSerializer.java @@ -5,7 +5,8 @@ import com.fasterxml.jackson.databind.ser.std.StdSerializer; import java.io.IOException; -import java.util.Date; +import java.time.Instant; +import java.util.List; import java.util.Map; public class PayloadSerializer extends StdSerializer { @@ -42,18 +43,50 @@ public void serialize(ClaimsHolder holder, JsonGenerator gen, SerializerProvider } } else { gen.writeFieldName(e.getKey()); - if (e.getValue() instanceof Date) { // true for EXPIRES_AT, ISSUED_AT, NOT_BEFORE - gen.writeNumber(dateToSeconds((Date) e.getValue())); - } else { - gen.writeObject(e.getValue()); - } + handleSerialization(e.getValue(), gen); } } gen.writeEndObject(); } - private long dateToSeconds(Date date) { - return date.getTime() / 1000; + /** + * Serializes {@linkplain Instant} to epoch second values, traversing maps and lists as needed. + * @param value the object to serialize + * @param gen the JsonGenerator to use for JSON serialization + * @throws IOException + */ + private void handleSerialization(Object value, JsonGenerator gen) throws IOException { + if (value instanceof Instant) { // EXPIRES_AT, ISSUED_AT, NOT_BEFORE, custom Instant claims + gen.writeNumber(instantToSeconds((Instant) value)); + } else if (value instanceof Map) { + serializeMap((Map) value, gen); + } else if (value instanceof List) { + serializeList((List) value, gen); + } else { + gen.writeObject(value); + } + } + + private void serializeMap(Map map, JsonGenerator gen) throws IOException { + gen.writeStartObject(); + for (Map.Entry entry : map.entrySet()) { + gen.writeFieldName((String) entry.getKey()); + Object value = entry.getValue(); + handleSerialization(value, gen); + } + gen.writeEndObject(); + } + + private void serializeList(List list, JsonGenerator gen) throws IOException { + gen.writeStartArray(); + for (Object entry : list) { + handleSerialization(entry, gen); + } + gen.writeEndArray(); + } + + private long instantToSeconds(Instant instant) { + return instant.getEpochSecond(); } } diff --git a/lib/src/main/java/com/auth0/jwt/interfaces/Claim.java b/lib/src/main/java/com/auth0/jwt/interfaces/Claim.java index cde08e4d..d6bd7372 100644 --- a/lib/src/main/java/com/auth0/jwt/interfaces/Claim.java +++ b/lib/src/main/java/com/auth0/jwt/interfaces/Claim.java @@ -2,6 +2,7 @@ import com.auth0.jwt.exceptions.JWTDecodeException; +import java.time.Instant; import java.util.Date; import java.util.List; import java.util.Map; @@ -57,12 +58,21 @@ public interface Claim { */ String asString(); + /** + * Get this Claim as an Instant. + * If the value can't be converted to an Instant, null will be returned. + * + * @return the value as a Instant or null. + */ + Instant asInstant(); + /** * Get this Claim as a Date. * If the value can't be converted to a Date, null will be returned. * * @return the value as a Date or null. */ + // TODO - Deprecate this method in favor of asInstant() Date asDate(); /** diff --git a/lib/src/main/java/com/auth0/jwt/interfaces/Clock.java b/lib/src/main/java/com/auth0/jwt/interfaces/Clock.java index 454de9be..21e6d3f8 100644 --- a/lib/src/main/java/com/auth0/jwt/interfaces/Clock.java +++ b/lib/src/main/java/com/auth0/jwt/interfaces/Clock.java @@ -1,5 +1,6 @@ package com.auth0.jwt.interfaces; +import java.time.Instant; import java.util.Date; /** @@ -7,9 +8,17 @@ */ public interface Clock { /** - * Returns a new Date representing Today's time. + * Returns a new Instant representing the current time. * + * @return the current time. + */ + Instant getNow(); + + /** + * Returns a new Date representing Today's time. + * @return a new Date representing Today's time. */ + // TODO - Deprecate this method in favor of getNow() Date getToday(); } diff --git a/lib/src/main/java/com/auth0/jwt/interfaces/Payload.java b/lib/src/main/java/com/auth0/jwt/interfaces/Payload.java index 89779936..9b2fddf1 100644 --- a/lib/src/main/java/com/auth0/jwt/interfaces/Payload.java +++ b/lib/src/main/java/com/auth0/jwt/interfaces/Payload.java @@ -1,5 +1,6 @@ package com.auth0.jwt.interfaces; +import java.time.Instant; import java.util.Date; import java.util.List; import java.util.Map; @@ -34,6 +35,28 @@ public interface Payload { * * @return the Expiration Time value or null. */ + Instant getExpiresAtInstant(); + + /** + * Get the value of the "nbf" claim, or null if it's not available. + * + * @return the Not Before value or null. + */ + Instant getNotBeforeInstant(); + + /** + * Get the value of the "iat" claim, or null if it's not available. + * + * @return the Issued At value or null. + */ + Instant getIssuedAtInstant(); + + /** + * Get the value of the "exp" claim, or null if it's not available. + * + * @return the Expiration Time value or null. + */ + // TODO - deprecate in favor of getExpiresAtInstant Date getExpiresAt(); /** @@ -41,6 +64,7 @@ public interface Payload { * * @return the Not Before value or null. */ + // TODO - deprecate in favor of getNotBeforeInstant Date getNotBefore(); /** @@ -48,6 +72,7 @@ public interface Payload { * * @return the Issued At value or null. */ + // TODO - deprecate in favor of getIssuedAtInstant Date getIssuedAt(); /** diff --git a/lib/src/main/java/com/auth0/jwt/interfaces/Verification.java b/lib/src/main/java/com/auth0/jwt/interfaces/Verification.java index 511e59b7..024a33f3 100644 --- a/lib/src/main/java/com/auth0/jwt/interfaces/Verification.java +++ b/lib/src/main/java/com/auth0/jwt/interfaces/Verification.java @@ -2,6 +2,7 @@ import com.auth0.jwt.JWTVerifier; +import java.time.Instant; import java.util.Date; /** @@ -138,6 +139,17 @@ public interface Verification { * @return this same Verification instance. * @throws IllegalArgumentException if the name is null. */ + Verification withClaim(String name, Instant value) throws IllegalArgumentException; + + /** + * Require a specific Claim value. + * + * @param name the Claim's name. + * @param value the Claim's value. + * @return this same Verification instance. + * @throws IllegalArgumentException if the name is null. + */ + // TODO deprecate this in favor of withClaim(String name, Instant value) Verification withClaim(String name, Date value) throws IllegalArgumentException; /** diff --git a/lib/src/test/java/com/auth0/jwt/ClockImplTest.java b/lib/src/test/java/com/auth0/jwt/ClockImplTest.java index 21a9f11b..fb548d14 100644 --- a/lib/src/test/java/com/auth0/jwt/ClockImplTest.java +++ b/lib/src/test/java/com/auth0/jwt/ClockImplTest.java @@ -3,11 +3,12 @@ import com.auth0.jwt.interfaces.Clock; import org.junit.Test; +import java.time.Instant; import java.util.Date; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; -import static org.junit.Assert.*; +import static org.junit.Assert.assertThat; public class ClockImplTest { @@ -18,4 +19,11 @@ public void shouldGetToday() { assertThat(clockToday, is(notNullValue())); } + @Test + public void shouldGetNow() throws Exception{ + Clock clock = new ClockImpl(); + Instant clockToday = clock.getNow(); + assertThat(clockToday, is(notNullValue())); + } + } \ No newline at end of file diff --git a/lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java b/lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java index a23d8638..fc9cd3e2 100644 --- a/lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java +++ b/lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java @@ -5,7 +5,6 @@ import com.auth0.jwt.interfaces.ECDSAKeyProvider; import com.auth0.jwt.interfaces.RSAKeyProvider; import com.fasterxml.jackson.databind.ObjectMapper; - import org.apache.commons.codec.binary.Base64; import org.junit.Rule; import org.junit.Test; @@ -14,13 +13,8 @@ import java.nio.charset.StandardCharsets; import java.security.interfaces.ECPrivateKey; import java.security.interfaces.RSAPrivateKey; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.time.Instant; +import java.util.*; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; @@ -234,7 +228,17 @@ public void shouldAddAudience() { } @Test - public void shouldAddExpiresAt() { + public void shouldAddExpiresAtAsInstant() { + String signed = JWTCreator.init() + .withExpiresAt(Instant.ofEpochMilli(1477592000)) + .sign(Algorithm.HMAC256("secret")); + + assertThat(signed, is(notNullValue())); + assertThat(TokenUtils.splitToken(signed)[1], is("eyJleHAiOjE0Nzc1OTJ9")); + } + + @Test + public void shouldAddExpiresAtAsDate() { String signed = JWTCreator.init() .withExpiresAt(new Date(1477592000)) .sign(Algorithm.HMAC256("secret")); @@ -244,7 +248,17 @@ public void shouldAddExpiresAt() { } @Test - public void shouldAddNotBefore() { + public void shouldAddNotBeforeAsInstant() { + String signed = JWTCreator.init() + .withNotBefore(Instant.ofEpochMilli(1477592000)) + .sign(Algorithm.HMAC256("secret")); + + assertThat(signed, is(notNullValue())); + assertThat(TokenUtils.splitToken(signed)[1], is("eyJuYmYiOjE0Nzc1OTJ9")); + } + + @Test + public void shouldAddNotBeforeAsDate() { String signed = JWTCreator.init() .withNotBefore(new Date(1477592000)) .sign(Algorithm.HMAC256("secret")); @@ -254,7 +268,17 @@ public void shouldAddNotBefore() { } @Test - public void shouldAddIssuedAt() { + public void shouldAddIssuedAtAsInstant() { + String signed = JWTCreator.init() + .withIssuedAt(Instant.ofEpochMilli(1477592000)) + .sign(Algorithm.HMAC256("secret")); + + assertThat(signed, is(notNullValue())); + assertThat(TokenUtils.splitToken(signed)[1], is("eyJpYXQiOjE0Nzc1OTJ9")); + } + + @Test + public void shouldAddIssuedAtAsDate() { String signed = JWTCreator.init() .withIssuedAt(new Date(1477592000)) .sign(Algorithm.HMAC256("secret")); @@ -390,6 +414,18 @@ public void shouldAcceptCustomClaimOfTypeBoolean() { assertThat(parts[1], is("eyJuYW1lIjp0cnVlfQ")); } + @Test + public void shouldAcceptCustomClaimOfTypeInstant() { + Instant instant = Instant.ofEpochMilli(1478891521000L); + String jwt = JWTCreator.init() + .withClaim("name", instant) + .sign(Algorithm.HMAC256("secret")); + + assertThat(jwt, is(notNullValue())); + String[] parts = jwt.split("\\."); + assertThat(parts[1], is("eyJuYW1lIjoxNDc4ODkxNTIxfQ")); + } + @Test public void shouldAcceptCustomClaimOfTypeDate() { Date date = new Date(1478891521000L); @@ -472,6 +508,7 @@ public void shouldAcceptCustomMapClaimOfBasicObjectTypes() throws Exception { data.put("long", Long.MAX_VALUE); data.put("double", 123.456d); data.put("date", new Date(123L)); + data.put("instant", Instant.ofEpochSecond(123L)); data.put("boolean", true); // array types @@ -479,11 +516,12 @@ public void shouldAcceptCustomMapClaimOfBasicObjectTypes() throws Exception { data.put("longArray", new Long[]{Long.MAX_VALUE, Long.MIN_VALUE}); data.put("stringArray", new String[]{"string"}); - data.put("list", Arrays.asList("a", "b", "c")); + data.put("list", Arrays.asList("a", "b", "c", Instant.ofEpochSecond(41L))); Map sub = new HashMap<>(); sub.put("subKey", "subValue"); - + sub.put("subDate", new Date(567L)); + sub.put("subInstant", Instant.ofEpochSecond(567L)); data.put("map", sub); String jwt = JWTCreator.init() @@ -502,6 +540,7 @@ public void shouldAcceptCustomMapClaimOfBasicObjectTypes() throws Exception { assertThat(map.get("long"), is(Long.MAX_VALUE)); assertThat(map.get("double"), is(123.456d)); assertThat(map.get("date"), is(123)); + assertThat(map.get("instant"), is(123)); assertThat(map.get("boolean"), is(true)); // array types @@ -510,9 +549,13 @@ public void shouldAcceptCustomMapClaimOfBasicObjectTypes() throws Exception { assertThat(map.get("stringArray"), is(Arrays.asList("string"))); // list - assertThat(map.get("list"), is(Arrays.asList("a", "b", "c"))); - assertThat(map.get("map"), is(sub)); + assertThat(map.get("list"), is(Arrays.asList("a", "b", "c", 41))); + // nested map + Map nested = (Map) map.get("map"); + assertThat(nested.get("subKey"), is("subValue")); + assertThat(nested.get("subDate"), is(567)); + assertThat(nested.get("subInstant"), is(567)); } @SuppressWarnings("unchecked") @@ -526,6 +569,7 @@ public void shouldAcceptCustomListClaimOfBasicObjectTypes() throws Exception { data.add(Long.MAX_VALUE); data.add(123.456d); data.add(new Date(123L)); + data.add(Instant.ofEpochSecond(123L)); data.add(true); // array types @@ -537,6 +581,8 @@ public void shouldAcceptCustomListClaimOfBasicObjectTypes() throws Exception { Map sub = new HashMap<>(); sub.put("subKey", "subValue"); + sub.put("subDate", new Date(567L)); + sub.put("subInstant", Instant.ofEpochSecond(567L)); data.add(sub); @@ -556,17 +602,22 @@ public void shouldAcceptCustomListClaimOfBasicObjectTypes() throws Exception { assertThat(list.get(2), is(Long.MAX_VALUE)); assertThat(list.get(3), is(123.456d)); assertThat(list.get(4), is(123)); - assertThat(list.get(5), is(true)); + assertThat(list.get(5), is(123)); + assertThat(list.get(6), is(true)); // array types - assertThat(list.get(6), is(Arrays.asList(3, 5))); - assertThat(list.get(7), is(Arrays.asList(Long.MAX_VALUE, Long.MIN_VALUE))); - assertThat(list.get(8), is(Arrays.asList("string"))); + assertThat(list.get(7), is(Arrays.asList(new Integer[]{3, 5}))); + assertThat(list.get(8), is(Arrays.asList(new Long[]{Long.MAX_VALUE, Long.MIN_VALUE}))); + assertThat(list.get(9), is(Arrays.asList(new String[]{"string"}))); // list - assertThat(list.get(9), is(Arrays.asList("a", "b", "c"))); - assertThat(list.get(10), is(sub)); + assertThat(list.get(10), is(Arrays.asList("a", "b", "c"))); + // nested map + Map nested = (Map) list.get(11); + assertThat(nested.get("subKey"), is("subValue")); + assertThat(nested.get("subDate"), is(567)); + assertThat(nested.get("subInstant"), is(567)); } @Test diff --git a/lib/src/test/java/com/auth0/jwt/JWTDecoderTest.java b/lib/src/test/java/com/auth0/jwt/JWTDecoderTest.java index ce55c3bf..0623ee2a 100644 --- a/lib/src/test/java/com/auth0/jwt/JWTDecoderTest.java +++ b/lib/src/test/java/com/auth0/jwt/JWTDecoderTest.java @@ -14,6 +14,7 @@ import java.io.*; import java.nio.charset.StandardCharsets; +import java.time.Instant; import java.util.Date; import java.util.Map; @@ -25,7 +26,7 @@ public class JWTDecoderTest { public ExpectedException exception = ExpectedException.none(); @Test - public void getSubject() throws Exception { + public void getSubject() { DecodedJWT jwt = JWT.decode("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ"); assertThat(jwt.getSubject(), is(notNullValue())); assertThat(jwt.getSubject(), is("1234567890")); @@ -160,6 +161,39 @@ public void shouldGetIssuedAt() { assertThat(jwt.getIssuedAt(), is(equalTo(expectedDate))); } + @Test + public void shouldGetExpirationTimeAsInstant() { + DecodedJWT jwt = JWT.decode("eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE0NzY3MjcwODZ9.L9dcPHEDQew2u9MkDCORFkfDGcSOsgoPqNY-LUMLEHg"); + assertThat(jwt, is(notNullValue())); + assertThat(jwt.getExpiresAtInstant(), is(instanceOf(Instant.class))); + long ms = 1476727086L * 1000; + Instant expectedInstant = Instant.ofEpochMilli(ms); + assertThat(jwt.getExpiresAtInstant(), is(notNullValue())); + assertThat(jwt.getExpiresAtInstant(), is(equalTo(expectedInstant))); + } + + @Test + public void shouldGetNotBeforeAsInstant() { + DecodedJWT jwt = JWT.decode("eyJhbGciOiJIUzI1NiJ9.eyJuYmYiOjE0NzY3MjcwODZ9.tkpD3iCPQPVqjnjpDVp2bJMBAgpVCG9ZjlBuMitass0"); + assertThat(jwt, is(notNullValue())); + assertThat(jwt.getNotBeforeInstant(), is(instanceOf(Instant.class))); + long ms = 1476727086L * 1000; + Instant expectedInstant = Instant.ofEpochMilli(ms); + assertThat(jwt.getNotBeforeInstant(), is(notNullValue())); + assertThat(jwt.getNotBeforeInstant(), is(equalTo(expectedInstant))); + } + + @Test + public void shouldGetIssuedAtAsInstant() { + DecodedJWT jwt = JWT.decode("eyJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE0NzY3MjcwODZ9.KPjGoW665E8V5_27Jugab8qSTxLk2cgquhPCBfAP0_w"); + assertThat(jwt, is(notNullValue())); + assertThat(jwt.getIssuedAtInstant(), is(instanceOf(Instant.class))); + long ms = 1476727086L * 1000; + Instant expectedInstant = Instant.ofEpochMilli(ms); + assertThat(jwt.getIssuedAtInstant(), is(notNullValue())); + assertThat(jwt.getIssuedAtInstant(), is(equalTo(expectedInstant))); + } + @Test public void shouldGetId() { DecodedJWT jwt = JWT.decode("eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxMjM0NTY3ODkwIn0.m3zgEfVUFOd-CvL3xG5BuOWLzb0zMQZCqiVNQQOPOvA"); @@ -247,6 +281,15 @@ public void shouldGetCustomClaimOfTypeDate() { Assert.assertThat(jwt.getClaim("name").asDate().getTime(), is(date.getTime())); } + @Test + public void shouldGetCustomClaimOfTypeInstant() { + String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoxNDc4ODkxNTIxfQ.mhioumeok8fghQEhTKF3QtQAksSvZ_9wIhJmgZLhJ6c"; + Instant instant = Instant.ofEpochMilli(1478891521000L); + DecodedJWT jwt = JWT.decode(token); + Assert.assertThat(jwt, is(notNullValue())); + Assert.assertThat(jwt.getClaim("name").asInstant().getEpochSecond(), is(instant.getEpochSecond())); + } + @Test public void shouldGetCustomArrayClaimOfTypeString() { String token = "eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjpbInRleHQiLCIxMjMiLCJ0cnVlIl19.lxM8EcmK1uSZRAPd0HUhXGZJdauRmZmLjoeqz4J9yAA"; @@ -306,12 +349,12 @@ public void shouldSerializeAndDeserialize() throws Exception { assertThat(originalJwt.getAlgorithm(), is(equalTo(deserializedJwt.getAlgorithm()))); assertThat(originalJwt.getAudience(), is(equalTo(deserializedJwt.getAudience()))); assertThat(originalJwt.getContentType(), is(equalTo(deserializedJwt.getContentType()))); - assertThat(originalJwt.getExpiresAt(), is(equalTo(deserializedJwt.getExpiresAt()))); + assertThat(originalJwt.getExpiresAtInstant(), is(equalTo(deserializedJwt.getExpiresAtInstant()))); assertThat(originalJwt.getId(), is(equalTo(deserializedJwt.getId()))); - assertThat(originalJwt.getIssuedAt(), is(equalTo(deserializedJwt.getIssuedAt()))); + assertThat(originalJwt.getIssuedAtInstant(), is(equalTo(deserializedJwt.getIssuedAtInstant()))); assertThat(originalJwt.getIssuer(), is(equalTo(deserializedJwt.getIssuer()))); assertThat(originalJwt.getKeyId(), is(equalTo(deserializedJwt.getKeyId()))); - assertThat(originalJwt.getNotBefore(), is(equalTo(deserializedJwt.getNotBefore()))); + assertThat(originalJwt.getNotBeforeInstant(), is(equalTo(deserializedJwt.getNotBeforeInstant()))); assertThat(originalJwt.getSubject(), is(equalTo(deserializedJwt.getSubject()))); assertThat(originalJwt.getType(), is(equalTo(deserializedJwt.getType()))); assertThat(originalJwt.getClaims().get("extraClaim").asString(), diff --git a/lib/src/test/java/com/auth0/jwt/JWTTest.java b/lib/src/test/java/com/auth0/jwt/JWTTest.java index c90c01ad..a05b05ee 100644 --- a/lib/src/test/java/com/auth0/jwt/JWTTest.java +++ b/lib/src/test/java/com/auth0/jwt/JWTTest.java @@ -13,6 +13,7 @@ import java.nio.charset.StandardCharsets; import java.security.interfaces.ECKey; import java.security.interfaces.RSAKey; +import java.time.Instant; import java.util.Date; import static org.hamcrest.Matchers.*; @@ -267,10 +268,11 @@ public void shouldGetStringAudience() { } @Test - public void shouldGetExpirationTime() { - Date expectedDate = new Date(1477592 * 1000); + public void shouldGetExpirationTime() throws Exception { + Instant expectedInstant = Instant.ofEpochSecond(1477592); Clock clock = mock(Clock.class); - when(clock.getToday()).thenReturn(expectedDate); + // implementation uses getNow instead of getToday, so need to mock that call + when(clock.getNow()).thenReturn(expectedInstant); String token = "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE0Nzc1OTJ9.x_ZjkPkKYUV5tdvc0l8go6D_z2kez1MQcOxokXrDc3k"; JWTVerifier.BaseVerification verification = (JWTVerifier.BaseVerification) JWT.require(Algorithm.HMAC256("secret")); @@ -281,14 +283,15 @@ public void shouldGetExpirationTime() { assertThat(jwt, is(notNullValue())); assertThat(jwt.getExpiresAt(), is(instanceOf(Date.class))); assertThat(jwt.getExpiresAt(), is(notNullValue())); - assertThat(jwt.getExpiresAt(), is(equalTo(expectedDate))); + assertThat(jwt.getExpiresAt(), is(equalTo(Date.from(expectedInstant)))); } @Test - public void shouldGetNotBefore() { - Date expectedDate = new Date(1477592 * 1000); + public void shouldGetNotBefore() throws Exception { + Instant expectedInstant = Instant.ofEpochSecond(1477592); Clock clock = mock(Clock.class); - when(clock.getToday()).thenReturn(expectedDate); + // implementation uses getNow instead of getToday, so need to mock that call + when(clock.getNow()).thenReturn(expectedInstant); String token = "eyJhbGciOiJIUzI1NiJ9.eyJuYmYiOjE0Nzc1OTJ9.mWYSOPoNXstjKbZkKrqgkwPOQWEx3F3gMm6PMcfuJd8"; JWTVerifier.BaseVerification verification = (JWTVerifier.BaseVerification) JWT.require(Algorithm.HMAC256("secret")); @@ -299,14 +302,15 @@ public void shouldGetNotBefore() { assertThat(jwt, is(notNullValue())); assertThat(jwt.getNotBefore(), is(instanceOf(Date.class))); assertThat(jwt.getNotBefore(), is(notNullValue())); - assertThat(jwt.getNotBefore(), is(equalTo(expectedDate))); + assertThat(jwt.getNotBefore(), is(equalTo(Date.from(expectedInstant)))); } @Test - public void shouldGetIssuedAt() { - Date expectedDate = new Date(1477592 * 1000); + public void shouldGetIssuedAt() throws Exception { + Instant expectedInstant = Instant.ofEpochSecond(1477592); Clock clock = mock(Clock.class); - when(clock.getToday()).thenReturn(expectedDate); + // implementation uses getNow instead of getToday, so need to mock that call + when(clock.getNow()).thenReturn(expectedInstant); String token = "eyJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE0Nzc1OTJ9.5o1CKlLFjKKcddZzoarQ37pq7qZqNPav3sdZ_bsZaD4"; JWTVerifier.BaseVerification verification = (JWTVerifier.BaseVerification) JWT.require(Algorithm.HMAC256("secret")); @@ -317,11 +321,65 @@ public void shouldGetIssuedAt() { assertThat(jwt, is(notNullValue())); assertThat(jwt.getIssuedAt(), is(instanceOf(Date.class))); assertThat(jwt.getIssuedAt(), is(notNullValue())); - assertThat(jwt.getIssuedAt(), is(equalTo(expectedDate))); + assertThat(jwt.getIssuedAt(), is(equalTo(Date.from(expectedInstant)))); } @Test - public void shouldGetId() { + public void shouldGetExpirationTimeAsInstant() throws Exception { + Instant expectedDate = Instant.ofEpochMilli(1477592 * 1000); + Clock clock = mock(Clock.class); + when(clock.getNow()).thenReturn(expectedDate); + + String token = "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE0Nzc1OTJ9.x_ZjkPkKYUV5tdvc0l8go6D_z2kez1MQcOxokXrDc3k"; + JWTVerifier.BaseVerification verification = (JWTVerifier.BaseVerification) JWT.require(Algorithm.HMAC256("secret")); + DecodedJWT jwt = verification + .build(clock) + .verify(token); + + assertThat(jwt, is(notNullValue())); + assertThat(jwt.getExpiresAtInstant(), is(instanceOf(Instant.class))); + assertThat(jwt.getExpiresAtInstant(), is(notNullValue())); + assertThat(jwt.getExpiresAtInstant(), is(equalTo(expectedDate))); + } + + @Test + public void shouldGetNotBeforeAsInstant() throws Exception { + Instant expectedDate = Instant.ofEpochMilli(1477592 * 1000); + Clock clock = mock(Clock.class); + when(clock.getNow()).thenReturn(expectedDate); + + String token = "eyJhbGciOiJIUzI1NiJ9.eyJuYmYiOjE0Nzc1OTJ9.mWYSOPoNXstjKbZkKrqgkwPOQWEx3F3gMm6PMcfuJd8"; + JWTVerifier.BaseVerification verification = (JWTVerifier.BaseVerification) JWT.require(Algorithm.HMAC256("secret")); + DecodedJWT jwt = verification + .build(clock) + .verify(token); + + assertThat(jwt, is(notNullValue())); + assertThat(jwt.getNotBeforeInstant(), is(instanceOf(Instant.class))); + assertThat(jwt.getNotBeforeInstant(), is(notNullValue())); + assertThat(jwt.getNotBeforeInstant(), is(equalTo(expectedDate))); + } + + @Test + public void shouldGetIssuedAtAsInstant() throws Exception { + Instant expectedDate = Instant.ofEpochMilli(1477592 * 1000); + Clock clock = mock(Clock.class); + when(clock.getNow()).thenReturn(expectedDate); + + String token = "eyJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE0Nzc1OTJ9.5o1CKlLFjKKcddZzoarQ37pq7qZqNPav3sdZ_bsZaD4"; + JWTVerifier.BaseVerification verification = (JWTVerifier.BaseVerification) JWT.require(Algorithm.HMAC256("secret")); + DecodedJWT jwt = verification + .build(clock) + .verify(token); + + assertThat(jwt, is(notNullValue())); + assertThat(jwt.getIssuedAtInstant(), is(instanceOf(Instant.class))); + assertThat(jwt.getIssuedAtInstant(), is(notNullValue())); + assertThat(jwt.getIssuedAtInstant(), is(equalTo(expectedDate))); + } + + @Test + public void shouldGetId() throws Exception { String token = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxMjM0NTY3ODkwIn0.m3zgEfVUFOd-CvL3xG5BuOWLzb0zMQZCqiVNQQOPOvA"; JWTVerifier.BaseVerification verification = (JWTVerifier.BaseVerification) JWT.require(Algorithm.HMAC256("secret")); DecodedJWT jwt = verification diff --git a/lib/src/test/java/com/auth0/jwt/JWTVerifierTest.java b/lib/src/test/java/com/auth0/jwt/JWTVerifierTest.java index f77cf22a..cb4a2279 100644 --- a/lib/src/test/java/com/auth0/jwt/JWTVerifierTest.java +++ b/lib/src/test/java/com/auth0/jwt/JWTVerifierTest.java @@ -10,6 +10,7 @@ import org.junit.Test; import org.junit.rules.ExpectedException; +import java.time.Instant; import java.util.Date; import java.util.HashMap; import java.util.Map; @@ -213,7 +214,6 @@ public void shouldThrowOnInvalidCustomClaimValueOfTypeBoolean() { .verify(token); } - @Test public void shouldThrowOnInvalidCustomClaimValueOfTypeDate() { exception.expect(InvalidClaimException.class); @@ -225,6 +225,17 @@ public void shouldThrowOnInvalidCustomClaimValueOfTypeDate() { .verify(token); } + @Test + public void shouldThrowOnInvalidCustomClaimValueOfTypeInstant() { + exception.expect(InvalidClaimException.class); + exception.expectMessage("The Claim 'name' value doesn't match the required one."); + String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjpbInNvbWV0aGluZyJdfQ.3ENLez6tU_fG0SVFrGmISltZPiXLSHaz_dyn-XFTEGQ"; + JWTVerifier.init(Algorithm.HMAC256("secret")) + .withClaim("name", Instant.now()) + .build() + .verify(token); + } + @Test public void shouldThrowOnInvalidCustomClaimValue() { exception.expect(InvalidClaimException.class); @@ -294,9 +305,9 @@ public void shouldValidateCustomClaimOfTypeBoolean() { @Test public void shouldValidateCustomClaimOfTypeDate() { String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoxNDc4ODkxNTIxfQ.mhioumeok8fghQEhTKF3QtQAksSvZ_9wIhJmgZLhJ6c"; - Date date = new Date(1478891521000L); + Instant instant = Instant.ofEpochMilli(1478891521000L); DecodedJWT jwt = JWTVerifier.init(Algorithm.HMAC256("secret")) - .withClaim("name", date) + .withClaim("name", instant) .build() .verify(token); @@ -409,9 +420,9 @@ public void shouldThrowOnNegativeCustomLeeway() { // Expires At @Test - public void shouldValidateExpiresAtWithLeeway() { + public void shouldValidateExpiresDateAtWithLeeway() { Clock clock = mock(Clock.class); - when(clock.getToday()).thenReturn(new Date(DATE_TOKEN_MS_VALUE + 1000)); + when(clock.getNow()).thenReturn(Instant.ofEpochMilli(DATE_TOKEN_MS_VALUE + 1000)); String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0Nzc1OTJ9.isvT0Pqx0yjnZk53mUFSeYFJLDs-Ls9IsNAm86gIdZo"; JWTVerifier.BaseVerification verification = (JWTVerifier.BaseVerification) JWTVerifier.init(Algorithm.HMAC256("secret")) @@ -423,10 +434,40 @@ public void shouldValidateExpiresAtWithLeeway() { assertThat(jwt, is(notNullValue())); } + @Test - public void shouldValidateExpiresAtIfPresent() { + public void shouldValidateExpiresInstantAtWithLeeway() { Clock clock = mock(Clock.class); - when(clock.getToday()).thenReturn(new Date(DATE_TOKEN_MS_VALUE)); + when(clock.getNow()).thenReturn(Instant.ofEpochMilli(DATE_TOKEN_MS_VALUE + 1000)); + + String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0Nzc1OTJ9.isvT0Pqx0yjnZk53mUFSeYFJLDs-Ls9IsNAm86gIdZo"; + JWTVerifier.BaseVerification verification = (JWTVerifier.BaseVerification) JWTVerifier.init(Algorithm.HMAC256("secret")) + .acceptExpiresAt(2); + DecodedJWT jwt = verification + .build(clock) + .verify(token); + + assertThat(jwt, is(notNullValue())); + } + + @Test + public void shouldValidateExpiresAtDateIfPresent() { + Clock clock = mock(Clock.class); + when(clock.getNow()).thenReturn(Instant.ofEpochMilli(DATE_TOKEN_MS_VALUE)); + + String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0Nzc1OTJ9.isvT0Pqx0yjnZk53mUFSeYFJLDs-Ls9IsNAm86gIdZo"; + JWTVerifier.BaseVerification verification = (JWTVerifier.BaseVerification) JWTVerifier.init(Algorithm.HMAC256("secret")); + DecodedJWT jwt = verification + .build(clock) + .verify(token); + + assertThat(jwt, is(notNullValue())); + } + + @Test + public void shouldValidateExpiresAtInstantIfPresent() { + Clock clock = mock(Clock.class); + when(clock.getNow()).thenReturn(Instant.ofEpochMilli(DATE_TOKEN_MS_VALUE)); String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0Nzc1OTJ9.isvT0Pqx0yjnZk53mUFSeYFJLDs-Ls9IsNAm86gIdZo"; JWTVerifier.BaseVerification verification = (JWTVerifier.BaseVerification) JWTVerifier.init(Algorithm.HMAC256("secret")); @@ -438,11 +479,25 @@ public void shouldValidateExpiresAtIfPresent() { } @Test - public void shouldThrowOnInvalidExpiresAtIfPresent() { + public void shouldThrowOnInvalidExpiresAtDateIfPresent() { + exception.expect(TokenExpiredException.class); + exception.expectMessage(startsWith("The Token has expired on")); + Clock clock = mock(Clock.class); + when(clock.getNow()).thenReturn(Instant.ofEpochMilli(DATE_TOKEN_MS_VALUE + 1000)); + + String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0Nzc1OTJ9.isvT0Pqx0yjnZk53mUFSeYFJLDs-Ls9IsNAm86gIdZo"; + JWTVerifier.BaseVerification verification = (JWTVerifier.BaseVerification) JWTVerifier.init(Algorithm.HMAC256("secret")); + verification + .build(clock) + .verify(token); + } + + @Test + public void shouldThrowOnInvalidExpiresAtInstantIfPresent() { exception.expect(TokenExpiredException.class); exception.expectMessage(startsWith("The Token has expired on")); Clock clock = mock(Clock.class); - when(clock.getToday()).thenReturn(new Date(DATE_TOKEN_MS_VALUE + 1000)); + when(clock.getNow()).thenReturn(Instant.ofEpochMilli(DATE_TOKEN_MS_VALUE + 1000)); String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0Nzc1OTJ9.isvT0Pqx0yjnZk53mUFSeYFJLDs-Ls9IsNAm86gIdZo"; JWTVerifier.BaseVerification verification = (JWTVerifier.BaseVerification) JWTVerifier.init(Algorithm.HMAC256("secret")); @@ -462,9 +517,9 @@ public void shouldThrowOnNegativeExpiresAtLeeway() { // Not before @Test - public void shouldValidateNotBeforeWithLeeway() { + public void shouldValidateNotBeforeDateWithLeeway() { Clock clock = mock(Clock.class); - when(clock.getToday()).thenReturn(new Date(DATE_TOKEN_MS_VALUE - 1000)); + when(clock.getNow()).thenReturn(Instant.ofEpochMilli(DATE_TOKEN_MS_VALUE - 1000)); String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE0Nzc1OTJ9.wq4ZmnSF2VOxcQBxPLfeh1J2Ozy1Tj5iUaERm3FKaw8"; JWTVerifier.BaseVerification verification = (JWTVerifier.BaseVerification) JWTVerifier.init(Algorithm.HMAC256("secret")) @@ -476,12 +531,42 @@ public void shouldValidateNotBeforeWithLeeway() { assertThat(jwt, is(notNullValue())); } + + @Test + public void shouldValidateNotBeforeInstantWithLeeway() { + Clock clock = mock(Clock.class); + when(clock.getNow()).thenReturn(Instant.ofEpochMilli(DATE_TOKEN_MS_VALUE - 1000)); + + String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE0Nzc1OTJ9.wq4ZmnSF2VOxcQBxPLfeh1J2Ozy1Tj5iUaERm3FKaw8"; + JWTVerifier.BaseVerification verification = (JWTVerifier.BaseVerification) JWTVerifier.init(Algorithm.HMAC256("secret")) + .acceptNotBefore(2); + DecodedJWT jwt = verification + .build(clock) + .verify(token); + + assertThat(jwt, is(notNullValue())); + } + + @Test + public void shouldThrowOnInvalidNotBeforeDateIfPresent() { + exception.expect(InvalidClaimException.class); + exception.expectMessage(startsWith("The Token can't be used before")); + Clock clock = mock(Clock.class); + when(clock.getNow()).thenReturn(Instant.ofEpochMilli(DATE_TOKEN_MS_VALUE - 1000)); + + String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE0Nzc1OTJ9.wq4ZmnSF2VOxcQBxPLfeh1J2Ozy1Tj5iUaERm3FKaw8"; + JWTVerifier.BaseVerification verification = (JWTVerifier.BaseVerification) JWTVerifier.init(Algorithm.HMAC256("secret")); + verification + .build(clock) + .verify(token); + } + @Test - public void shouldThrowOnInvalidNotBeforeIfPresent() { + public void shouldThrowOnInvalidNotBeforeInstantIfPresent() { exception.expect(InvalidClaimException.class); exception.expectMessage(startsWith("The Token can't be used before")); Clock clock = mock(Clock.class); - when(clock.getToday()).thenReturn(new Date(DATE_TOKEN_MS_VALUE - 1000)); + when(clock.getNow()).thenReturn(Instant.ofEpochMilli(DATE_TOKEN_MS_VALUE - 1000)); String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE0Nzc1OTJ9.wq4ZmnSF2VOxcQBxPLfeh1J2Ozy1Tj5iUaERm3FKaw8"; JWTVerifier.BaseVerification verification = (JWTVerifier.BaseVerification) JWTVerifier.init(Algorithm.HMAC256("secret")); @@ -491,9 +576,23 @@ public void shouldThrowOnInvalidNotBeforeIfPresent() { } @Test - public void shouldValidateNotBeforeIfPresent() { + public void shouldValidateNotBeforeDateIfPresent() { Clock clock = mock(Clock.class); - when(clock.getToday()).thenReturn(new Date(DATE_TOKEN_MS_VALUE)); + when(clock.getNow()).thenReturn(Instant.ofEpochMilli(DATE_TOKEN_MS_VALUE)); + + String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0Nzc1OTJ9.isvT0Pqx0yjnZk53mUFSeYFJLDs-Ls9IsNAm86gIdZo"; + JWTVerifier.BaseVerification verification = (JWTVerifier.BaseVerification) JWTVerifier.init(Algorithm.HMAC256("secret")); + DecodedJWT jwt = verification + .build(clock) + .verify(token); + + assertThat(jwt, is(notNullValue())); + } + + @Test + public void shouldValidateNotBeforeInstantIfPresent() { + Clock clock = mock(Clock.class); + when(clock.getNow()).thenReturn(Instant.ofEpochMilli(DATE_TOKEN_MS_VALUE)); String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0Nzc1OTJ9.isvT0Pqx0yjnZk53mUFSeYFJLDs-Ls9IsNAm86gIdZo"; JWTVerifier.BaseVerification verification = (JWTVerifier.BaseVerification) JWTVerifier.init(Algorithm.HMAC256("secret")); @@ -513,11 +612,29 @@ public void shouldThrowOnNegativeNotBeforeLeeway() { .acceptNotBefore(-1); } -// Issued At with future date - @Test (expected = InvalidClaimException.class) - public void shouldThrowOnFutureIssuedAt() { + // Issued At with future date + @Test + public void shouldThrowOnFutureIssuedAtDate() { + exception.expect(InvalidClaimException.class); + exception.expectMessage("The Token can't be used before 1970-01-18T02:26:32Z."); + Clock clock = mock(Clock.class); - when(clock.getToday()).thenReturn(new Date(DATE_TOKEN_MS_VALUE - 1000)); + when(clock.getNow()).thenReturn(Instant.ofEpochMilli(DATE_TOKEN_MS_VALUE - 1000)); + + String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE0Nzc1OTJ9.CWq-6pUXl1bFg81vqOUZbZrheO2kUBd2Xr3FUZmvudE"; + JWTVerifier.BaseVerification verification = (JWTVerifier.BaseVerification) JWTVerifier.init(Algorithm.HMAC256("secret")); + + DecodedJWT jwt = verification.build(clock).verify(token); + assertThat(jwt, is(notNullValue())); + } + + @Test + public void shouldThrowOnFutureIssuedAtInstant() { + exception.expect(InvalidClaimException.class); + exception.expectMessage("The Token can't be used before 1970-01-18T02:26:32Z."); + + Clock clock = mock(Clock.class); + when(clock.getNow()).thenReturn(Instant.ofEpochMilli(DATE_TOKEN_MS_VALUE - 1000)); String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE0Nzc1OTJ9.CWq-6pUXl1bFg81vqOUZbZrheO2kUBd2Xr3FUZmvudE"; JWTVerifier.BaseVerification verification = (JWTVerifier.BaseVerification) JWTVerifier.init(Algorithm.HMAC256("secret")); @@ -528,9 +645,22 @@ public void shouldThrowOnFutureIssuedAt() { // Issued At with future date and ignore flag @Test - public void shouldSkipIssuedAtVerificationWhenFlagIsPassed() { + public void shouldSkipIssuedAtDateVerificationWhenFlagIsPassed() { + Clock clock = mock(Clock.class); + when(clock.getNow()).thenReturn(Instant.ofEpochMilli(DATE_TOKEN_MS_VALUE - 1000)); + + String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE0Nzc1OTJ9.CWq-6pUXl1bFg81vqOUZbZrheO2kUBd2Xr3FUZmvudE"; + JWTVerifier.BaseVerification verification = (JWTVerifier.BaseVerification) JWTVerifier.init(Algorithm.HMAC256("secret")); + verification.ignoreIssuedAt(); + + DecodedJWT jwt = verification.build(clock).verify(token); + assertThat(jwt, is(notNullValue())); + } + + @Test + public void shouldSkipIssuedAtInstantVerificationWhenFlagIsPassed() { Clock clock = mock(Clock.class); - when(clock.getToday()).thenReturn(new Date(DATE_TOKEN_MS_VALUE - 1000)); + when(clock.getNow()).thenReturn(Instant.ofEpochMilli(DATE_TOKEN_MS_VALUE - 1000)); String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE0Nzc1OTJ9.CWq-6pUXl1bFg81vqOUZbZrheO2kUBd2Xr3FUZmvudE"; JWTVerifier.BaseVerification verification = (JWTVerifier.BaseVerification) JWTVerifier.init(Algorithm.HMAC256("secret")); @@ -541,11 +671,11 @@ public void shouldSkipIssuedAtVerificationWhenFlagIsPassed() { } @Test - public void shouldThrowOnInvalidIssuedAtIfPresent() { + public void shouldThrowOnInvalidIssuedAtDateIfPresent() { exception.expect(InvalidClaimException.class); exception.expectMessage(startsWith("The Token can't be used before")); Clock clock = mock(Clock.class); - when(clock.getToday()).thenReturn(new Date(DATE_TOKEN_MS_VALUE - 1000)); + when(clock.getNow()).thenReturn(Instant.ofEpochMilli(DATE_TOKEN_MS_VALUE - 1000)); String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE0Nzc1OTJ9.0WJky9eLN7kuxLyZlmbcXRL3Wy8hLoNCEk5CCl2M4lo"; JWTVerifier.BaseVerification verification = (JWTVerifier.BaseVerification) JWTVerifier.init(Algorithm.HMAC256("secret")); @@ -555,9 +685,23 @@ public void shouldThrowOnInvalidIssuedAtIfPresent() { } @Test - public void shouldOverrideAcceptIssuedAtWhenIgnoreIssuedAtFlagPassedAndSkipTheVerification() { + public void shouldThrowOnInvalidIssuedAtInstantIfPresent() { + exception.expect(InvalidClaimException.class); + exception.expectMessage(startsWith("The Token can't be used before")); Clock clock = mock(Clock.class); - when(clock.getToday()).thenReturn(new Date(DATE_TOKEN_MS_VALUE - 1000)); + when(clock.getNow()).thenReturn(Instant.ofEpochMilli(DATE_TOKEN_MS_VALUE - 1000)); + + String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE0Nzc1OTJ9.0WJky9eLN7kuxLyZlmbcXRL3Wy8hLoNCEk5CCl2M4lo"; + JWTVerifier.BaseVerification verification = (JWTVerifier.BaseVerification) JWTVerifier.init(Algorithm.HMAC256("secret")); + verification + .build(clock) + .verify(token); + } + + @Test + public void shouldOverrideAcceptIssuedAtWhenIgnoreIssuedAtDateFlagPassedAndSkipTheVerification() { + Clock clock = mock(Clock.class); + when(clock.getNow()).thenReturn(Instant.ofEpochMilli(DATE_TOKEN_MS_VALUE - 1000)); String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE0Nzc1OTJ9.0WJky9eLN7kuxLyZlmbcXRL3Wy8hLoNCEk5CCl2M4lo"; JWTVerifier.BaseVerification verification = (JWTVerifier.BaseVerification) JWTVerifier.init(Algorithm.HMAC256("secret")); @@ -569,9 +713,37 @@ public void shouldOverrideAcceptIssuedAtWhenIgnoreIssuedAtFlagPassedAndSkipTheVe } @Test - public void shouldValidateIssuedAtIfPresent() { + public void shouldOverrideAcceptIssuedAtWhenIgnoreIssuedAtInstantFlagPassedAndSkipTheVerification() { + Clock clock = mock(Clock.class); + when(clock.getNow()).thenReturn(Instant.ofEpochMilli(DATE_TOKEN_MS_VALUE - 1000)); + + String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE0Nzc1OTJ9.0WJky9eLN7kuxLyZlmbcXRL3Wy8hLoNCEk5CCl2M4lo"; + JWTVerifier.BaseVerification verification = (JWTVerifier.BaseVerification) JWTVerifier.init(Algorithm.HMAC256("secret")); + DecodedJWT jwt = verification.acceptIssuedAt(20).ignoreIssuedAt() + .build() + .verify(token); + + assertThat(jwt, is(notNullValue())); + } + + @Test + public void shouldValidateIssuedAtDateIfPresent() { + Clock clock = mock(Clock.class); + when(clock.getNow()).thenReturn(Instant.ofEpochMilli(DATE_TOKEN_MS_VALUE)); + + String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE0Nzc1OTJ9.0WJky9eLN7kuxLyZlmbcXRL3Wy8hLoNCEk5CCl2M4lo"; + JWTVerifier.BaseVerification verification = (JWTVerifier.BaseVerification) JWTVerifier.init(Algorithm.HMAC256("secret")); + DecodedJWT jwt = verification + .build(clock) + .verify(token); + + assertThat(jwt, is(notNullValue())); + } + + @Test + public void shouldValidateIssuedAtInstantIfPresent() { Clock clock = mock(Clock.class); - when(clock.getToday()).thenReturn(new Date(DATE_TOKEN_MS_VALUE)); + when(clock.getNow()).thenReturn(Instant.ofEpochMilli(DATE_TOKEN_MS_VALUE)); String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE0Nzc1OTJ9.0WJky9eLN7kuxLyZlmbcXRL3Wy8hLoNCEk5CCl2M4lo"; JWTVerifier.BaseVerification verification = (JWTVerifier.BaseVerification) JWTVerifier.init(Algorithm.HMAC256("secret")); diff --git a/lib/src/test/java/com/auth0/jwt/impl/JsonNodeClaimTest.java b/lib/src/test/java/com/auth0/jwt/impl/JsonNodeClaimTest.java index 712f2b0e..da369fe2 100644 --- a/lib/src/test/java/com/auth0/jwt/impl/JsonNodeClaimTest.java +++ b/lib/src/test/java/com/auth0/jwt/impl/JsonNodeClaimTest.java @@ -20,6 +20,7 @@ import org.mockito.ArgumentMatchers; import java.io.IOException; +import java.time.Instant; import java.util.*; import static com.auth0.jwt.impl.JWTParser.getDefaultObjectMapper; @@ -121,14 +122,18 @@ public void shouldGetDateValue() { assertThat(claim.asDate(), is(notNullValue())); assertThat(claim.asDate(), is(new Date(1476824844L * 1000))); + assertThat(claim.asInstant(), is(notNullValue())); + assertThat(claim.asInstant(), is(Instant.ofEpochSecond(1476824844L))); } @Test public void shouldGetNullDateIfNotDateValue() { JsonNode objectValue = mapper.valueToTree(new Object()); assertThat(claimFromNode(objectValue).asDate(), is(nullValue())); + assertThat(claimFromNode(objectValue).asInstant(), is(nullValue())); JsonNode stringValue = mapper.valueToTree("1476824844"); assertThat(claimFromNode(stringValue).asDate(), is(nullValue())); + assertThat(claimFromNode(stringValue).asInstant(), is(nullValue())); } @Test diff --git a/lib/src/test/java/com/auth0/jwt/impl/NullClaimTest.java b/lib/src/test/java/com/auth0/jwt/impl/NullClaimTest.java index 9c1b49c5..14815d8c 100644 --- a/lib/src/test/java/com/auth0/jwt/impl/NullClaimTest.java +++ b/lib/src/test/java/com/auth0/jwt/impl/NullClaimTest.java @@ -50,6 +50,11 @@ public void shouldGetAsDate() { assertThat(claim.asDate(), is(nullValue())); } + @Test + public void shouldGetAsInstant() { + assertThat(claim.asInstant(), is(nullValue())); + } + @Test public void shouldGetAsArray() { assertThat(claim.asArray(Object.class), is(nullValue())); 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 fe9ce45d..76c85b63 100644 --- a/lib/src/test/java/com/auth0/jwt/impl/PayloadDeserializerTest.java +++ b/lib/src/test/java/com/auth0/jwt/impl/PayloadDeserializerTest.java @@ -19,6 +19,7 @@ import org.junit.rules.ExpectedException; import java.io.StringReader; +import java.time.Instant; import java.util.*; import static org.hamcrest.Matchers.*; @@ -96,6 +97,9 @@ public void shouldNotRemoveKnownPublicClaimsFromTree() throws Exception { assertThat(payload.getIssuedAt().getTime(), is(10101010L * 1000)); assertThat(payload.getExpiresAt().getTime(), is(11111111L * 1000)); assertThat(payload.getNotBefore().getTime(), is(10101011L * 1000)); + assertThat(payload.getIssuedAtInstant().toEpochMilli(), is(10101010L * 1000)); + assertThat(payload.getExpiresAtInstant().toEpochMilli(), is(11111111L * 1000)); + assertThat(payload.getNotBeforeInstant().toEpochMilli(), is(10101011L * 1000)); assertThat(payload.getId(), is("idid")); assertThat(payload.getClaim("roles").asString(), is("admin")); @@ -179,22 +183,22 @@ public void shouldGetNullArrayWhenParsingNonArrayOrTextNode() { @Test - public void shouldGetNullDateWhenParsingNullNode() { + public void shouldGetNullInstantWhenParsingNullNode() { Map tree = new HashMap<>(); NullNode node = NullNode.getInstance(); tree.put("key", node); - Date date = deserializer.getDateFromSeconds(tree, "key"); - assertThat(date, is(nullValue())); + Instant instant = deserializer.getInstantFromSeconds(tree, "key"); + assertThat(instant, is(nullValue())); } @Test - public void shouldGetNullDateWhenParsingNull() { + public void shouldGetNullInstantWhenParsingNull() { Map tree = new HashMap<>(); tree.put("key", null); - Date date = deserializer.getDateFromSeconds(tree, "key"); - assertThat(date, is(nullValue())); + Instant instant = deserializer.getInstantFromSeconds(tree, "key"); + assertThat(instant, is(nullValue())); } @Test @@ -206,32 +210,32 @@ public void shouldThrowWhenParsingNonNumericNode() { TextNode node = new TextNode("123456789"); tree.put("key", node); - deserializer.getDateFromSeconds(tree, "key"); + deserializer.getInstantFromSeconds(tree, "key"); } @Test - public void shouldGetDateWhenParsingNumericNode() { + public void shouldGetInstantWhenParsingNumericNode() { Map tree = new HashMap<>(); long seconds = 1478627949 / 1000; LongNode node = new LongNode(seconds); tree.put("key", node); - Date date = deserializer.getDateFromSeconds(tree, "key"); - assertThat(date, is(notNullValue())); - assertThat(date.getTime(), is(seconds * 1000)); + Instant instant = deserializer.getInstantFromSeconds(tree, "key"); + assertThat(instant, is(notNullValue())); + assertThat(instant.toEpochMilli(), is(seconds * 1000)); } @Test - public void shouldGetLargeDateWhenParsingNumericNode() { + public void shouldGetLargeInstantWhenParsingNumericNode() { Map tree = new HashMap<>(); long seconds = Integer.MAX_VALUE + 10000L; LongNode node = new LongNode(seconds); tree.put("key", node); - Date date = deserializer.getDateFromSeconds(tree, "key"); - assertThat(date, is(notNullValue())); - assertThat(date.getTime(), is(seconds * 1000)); - assertThat(date.getTime(), is(2147493647L * 1000)); + Instant instant = deserializer.getInstantFromSeconds(tree, "key"); + assertThat(instant, is(notNullValue())); + assertThat(instant.toEpochMilli(), is(seconds * 1000)); + assertThat(instant.toEpochMilli(), is(2147493647L * 1000)); } @Test diff --git a/lib/src/test/java/com/auth0/jwt/impl/PayloadImplTest.java b/lib/src/test/java/com/auth0/jwt/impl/PayloadImplTest.java index 1960b6e5..11726971 100644 --- a/lib/src/test/java/com/auth0/jwt/impl/PayloadImplTest.java +++ b/lib/src/test/java/com/auth0/jwt/impl/PayloadImplTest.java @@ -11,10 +11,10 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; -import org.mockito.Mockito; +import java.sql.Date; +import java.time.Instant; import java.util.Collections; -import java.util.Date; import java.util.HashMap; import java.util.Map; @@ -27,9 +27,9 @@ public class PayloadImplTest { public ExpectedException exception = ExpectedException.none(); private PayloadImpl payload; - private Date expiresAt; - private Date notBefore; - private Date issuedAt; + private Instant expiresAt; + private Instant notBefore; + private Instant issuedAt; private ObjectMapper mapper; private ObjectReader objectReader; @@ -38,10 +38,10 @@ public class PayloadImplTest { public void setUp() { mapper = getDefaultObjectMapper(); objectReader = mapper.reader(); - - expiresAt = Mockito.mock(Date.class); - notBefore = Mockito.mock(Date.class); - issuedAt = Mockito.mock(Date.class); + + expiresAt = Instant.now(); + notBefore = Instant.now(); + issuedAt = Instant.now(); Map tree = new HashMap<>(); tree.put("extraClaim", new TextNode("extraValue")); payload = new PayloadImpl("issuer", "subject", Collections.singletonList("audience"), expiresAt, notBefore, issuedAt, "jwtId", tree, objectReader); @@ -99,7 +99,8 @@ public void shouldGetNullAudienceIfMissing() { @Test public void shouldGetExpiresAt() { assertThat(payload, is(notNullValue())); - assertThat(payload.getExpiresAt(), is(expiresAt)); + assertThat(payload.getExpiresAt(), is(Date.from(expiresAt))); + assertThat(payload.getExpiresAtInstant(), is(expiresAt)); } @Test @@ -107,12 +108,14 @@ public void shouldGetNullExpiresAtIfMissing() { PayloadImpl payload = new PayloadImpl(null, null, null, null, null, null, null, null, objectReader); assertThat(payload, is(notNullValue())); assertThat(payload.getExpiresAt(), is(nullValue())); + assertThat(payload.getExpiresAtInstant(), is(nullValue())); } @Test public void shouldGetNotBefore() { assertThat(payload, is(notNullValue())); - assertThat(payload.getNotBefore(), is(notBefore)); + assertThat(payload.getNotBefore(), is(Date.from(notBefore))); + assertThat(payload.getNotBeforeInstant(), is(notBefore)); } @Test @@ -120,12 +123,14 @@ public void shouldGetNullNotBeforeIfMissing() { PayloadImpl payload = new PayloadImpl(null, null, null, null, null, null, null, null, objectReader); assertThat(payload, is(notNullValue())); assertThat(payload.getNotBefore(), is(nullValue())); + assertThat(payload.getNotBeforeInstant(), is(nullValue())); } @Test public void shouldGetIssuedAt() { assertThat(payload, is(notNullValue())); - assertThat(payload.getIssuedAt(), is(issuedAt)); + assertThat(payload.getIssuedAt(), is(Date.from(issuedAt))); + assertThat(payload.getIssuedAtInstant(), is(issuedAt)); } @Test @@ -133,6 +138,7 @@ public void shouldGetNullIssuedAtIfMissing() { PayloadImpl payload = new PayloadImpl(null, null, null, null, null, null, null, null, objectReader); assertThat(payload, is(notNullValue())); assertThat(payload.getIssuedAt(), is(nullValue())); + assertThat(payload.getIssuedAtInstant(), is(nullValue())); } @Test diff --git a/lib/src/test/java/com/auth0/jwt/impl/PayloadSerializerTest.java b/lib/src/test/java/com/auth0/jwt/impl/PayloadSerializerTest.java index 44da5e54..12395c34 100644 --- a/lib/src/test/java/com/auth0/jwt/impl/PayloadSerializerTest.java +++ b/lib/src/test/java/com/auth0/jwt/impl/PayloadSerializerTest.java @@ -9,8 +9,8 @@ import org.junit.Test; import java.io.StringWriter; +import java.time.Instant; import java.util.Arrays; -import java.util.Date; import java.util.HashMap; import java.util.Map; @@ -80,8 +80,8 @@ public void shouldSkipSerializationOnEmptyAudience() throws Exception { } @Test - public void shouldSerializeNotBeforeDateInSeconds() throws Exception { - ClaimsHolder holder = holderFor("nbf", new Date(1478874000)); + public void shouldSerializeNotBeforeInstantInSeconds() throws Exception { + ClaimsHolder holder = holderFor("nbf", Instant.ofEpochMilli(1478874000)); serializer.serialize(holder, jsonGenerator, serializerProvider); jsonGenerator.flush(); @@ -89,8 +89,8 @@ public void shouldSerializeNotBeforeDateInSeconds() throws Exception { } @Test - public void shouldSerializeIssuedAtDateInSeconds() throws Exception { - ClaimsHolder holder = holderFor("iat", new Date(1478874000)); + public void shouldSerializeIssuedAtInstantInSeconds() throws Exception { + ClaimsHolder holder = holderFor("iat", Instant.ofEpochMilli(1478874000)); serializer.serialize(holder, jsonGenerator, serializerProvider); jsonGenerator.flush(); @@ -98,8 +98,8 @@ public void shouldSerializeIssuedAtDateInSeconds() throws Exception { } @Test - public void shouldSerializeExpiresAtDateInSeconds() throws Exception { - ClaimsHolder holder = holderFor("exp", new Date(1478874000)); + public void shouldSerializeExpiresAtInstantInSeconds() throws Exception { + ClaimsHolder holder = holderFor("exp", Instant.ofEpochMilli(1478874000)); serializer.serialize(holder, jsonGenerator, serializerProvider); jsonGenerator.flush(); @@ -107,8 +107,8 @@ public void shouldSerializeExpiresAtDateInSeconds() throws Exception { } @Test - public void shouldSerializeCustomDateInSeconds() throws Exception { - ClaimsHolder holder = holderFor("birthdate", new Date(1478874000)); + public void shouldSerializeCustomInstantInSeconds() throws Exception { + ClaimsHolder holder = holderFor("birthdate", Instant.ofEpochMilli(1478874000)); serializer.serialize(holder, jsonGenerator, serializerProvider); jsonGenerator.flush(); @@ -116,14 +116,14 @@ public void shouldSerializeCustomDateInSeconds() throws Exception { } @Test - public void shouldSerializeDatesUsingLong() throws Exception { + public void shouldSerializeInstantsUsingLong() throws Exception { long secs = Integer.MAX_VALUE + 10000L; - Date date = new Date(secs * 1000L); - Map claims = new HashMap<>(); - claims.put("iat", date); - claims.put("nbf", date); - claims.put("exp", date); - claims.put("ctm", date); + Instant instant = Instant.ofEpochSecond(secs); + Map claims = new HashMap(); + claims.put("iat", instant); + claims.put("nbf", instant); + claims.put("exp", instant); + claims.put("ctm", instant); ClaimsHolder holder = new ClaimsHolder(claims); serializer.serialize(holder, jsonGenerator, serializerProvider); jsonGenerator.flush(); From 37b70f6df1bf1544fd276391cdc9c1284ed1eef8 Mon Sep 17 00:00:00 2001 From: Jim Anderson Date: Thu, 5 Mar 2020 16:39:20 -0600 Subject: [PATCH 11/14] Use java.util.Base64 for encoding and decoding (#401) * Use Java Base64 encoding and decoding * Throw on invalid Base64 encoded tokens --- lib/build.gradle | 1 - .../main/java/com/auth0/jwt/JWTCreator.java | 12 ++--- .../main/java/com/auth0/jwt/JWTDecoder.java | 12 ++--- .../auth0/jwt/algorithms/ECDSAAlgorithm.java | 7 ++- .../auth0/jwt/algorithms/HMACAlgorithm.java | 7 ++- .../auth0/jwt/algorithms/NoneAlgorithm.java | 14 ++++-- .../auth0/jwt/algorithms/RSAAlgorithm.java | 7 ++- .../java/com/auth0/jwt/JWTCreatorTest.java | 30 ++++++------- .../java/com/auth0/jwt/JWTDecoderTest.java | 24 ++++++++-- lib/src/test/java/com/auth0/jwt/JWTTest.java | 20 ++++----- .../jwt/algorithms/CryptoTestHelper.java | 11 +++-- .../jwt/algorithms/ECDSAAlgorithmTest.java | 45 ++++++++++++------- .../ECDSABouncyCastleProviderTests.java | 37 +++++++-------- .../jwt/algorithms/HMACAlgorithmTest.java | 16 ++++++- .../jwt/algorithms/NoneAlgorithmTest.java | 18 ++++++-- .../jwt/algorithms/RSAAlgorithmTest.java | 9 ++++ 16 files changed, 166 insertions(+), 104 deletions(-) diff --git a/lib/build.gradle b/lib/build.gradle index c1a7c027..3d89413e 100644 --- a/lib/build.gradle +++ b/lib/build.gradle @@ -35,7 +35,6 @@ compileJava { dependencies { implementation 'com.fasterxml.jackson.core:jackson-databind:2.10.0.pr3' - implementation 'commons-codec:commons-codec:1.12' testImplementation 'org.bouncycastle:bcprov-jdk15on:1.60' testImplementation 'junit:junit:4.12' testImplementation 'net.jodah:concurrentunit:0.4.3' diff --git a/lib/src/main/java/com/auth0/jwt/JWTCreator.java b/lib/src/main/java/com/auth0/jwt/JWTCreator.java index 625715bd..07b609e8 100644 --- a/lib/src/main/java/com/auth0/jwt/JWTCreator.java +++ b/lib/src/main/java/com/auth0/jwt/JWTCreator.java @@ -10,14 +10,10 @@ import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModule; -import org.apache.commons.codec.binary.Base64; import java.nio.charset.StandardCharsets; import java.time.Instant; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.Map.Entry; /** @@ -480,11 +476,11 @@ private void addClaim(String name, Object value) { } private String sign() throws SignatureGenerationException { - String header = Base64.encodeBase64URLSafeString(headerJson.getBytes(StandardCharsets.UTF_8)); - String payload = Base64.encodeBase64URLSafeString(payloadJson.getBytes(StandardCharsets.UTF_8)); + String header = Base64.getUrlEncoder().withoutPadding().encodeToString(headerJson.getBytes(StandardCharsets.UTF_8)); + String payload = Base64.getUrlEncoder().withoutPadding().encodeToString(payloadJson.getBytes(StandardCharsets.UTF_8)); byte[] signatureBytes = algorithm.sign(header.getBytes(StandardCharsets.UTF_8), payload.getBytes(StandardCharsets.UTF_8)); - String signature = Base64.encodeBase64URLSafeString((signatureBytes)); + String signature = Base64.getUrlEncoder().withoutPadding().encodeToString((signatureBytes)); return String.format("%s.%s.%s", header, payload, signature); } diff --git a/lib/src/main/java/com/auth0/jwt/JWTDecoder.java b/lib/src/main/java/com/auth0/jwt/JWTDecoder.java index 2b35a9ab..49d33792 100644 --- a/lib/src/main/java/com/auth0/jwt/JWTDecoder.java +++ b/lib/src/main/java/com/auth0/jwt/JWTDecoder.java @@ -6,11 +6,11 @@ import com.auth0.jwt.interfaces.DecodedJWT; import com.auth0.jwt.interfaces.Header; import com.auth0.jwt.interfaces.Payload; -import org.apache.commons.codec.binary.Base64; -import org.apache.commons.codec.binary.StringUtils; import java.io.Serializable; +import java.nio.charset.StandardCharsets; import java.time.Instant; +import java.util.Base64; import java.util.Date; import java.util.List; import java.util.Map; @@ -35,10 +35,10 @@ final class JWTDecoder implements DecodedJWT, Serializable { String headerJson; String payloadJson; try { - headerJson = StringUtils.newStringUtf8(Base64.decodeBase64(parts[0])); - payloadJson = StringUtils.newStringUtf8(Base64.decodeBase64(parts[1])); - } catch (NullPointerException e) { - throw new JWTDecodeException("The UTF-8 Charset isn't initialized.", e); + headerJson = new String(Base64.getUrlDecoder().decode(parts[0]), StandardCharsets.UTF_8); + payloadJson = new String(Base64.getUrlDecoder().decode(parts[1]), StandardCharsets.UTF_8); + } catch (NullPointerException | IllegalArgumentException e) { + throw new JWTDecodeException("The UTF-8 Charset isn't initialized or the token is not valid Base64.", e); } header = converter.parseHeader(headerJson); payload = converter.parsePayload(payloadJson); diff --git a/lib/src/main/java/com/auth0/jwt/algorithms/ECDSAAlgorithm.java b/lib/src/main/java/com/auth0/jwt/algorithms/ECDSAAlgorithm.java index 043942b2..f3f5bd35 100644 --- a/lib/src/main/java/com/auth0/jwt/algorithms/ECDSAAlgorithm.java +++ b/lib/src/main/java/com/auth0/jwt/algorithms/ECDSAAlgorithm.java @@ -4,13 +4,13 @@ import com.auth0.jwt.exceptions.SignatureVerificationException; import com.auth0.jwt.interfaces.DecodedJWT; import com.auth0.jwt.interfaces.ECDSAKeyProvider; -import org.apache.commons.codec.binary.Base64; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SignatureException; import java.security.interfaces.ECPrivateKey; import java.security.interfaces.ECPublicKey; +import java.util.Base64; class ECDSAAlgorithm extends Algorithm { private final ECDSAKeyProvider keyProvider; @@ -36,9 +36,8 @@ class ECDSAAlgorithm extends Algorithm { @Override public void verify(DecodedJWT jwt) throws SignatureVerificationException { - byte[] signatureBytes = Base64.decodeBase64(jwt.getSignature()); - try { + byte[] signatureBytes = Base64.getUrlDecoder().decode(jwt.getSignature()); ECPublicKey publicKey = keyProvider.getPublicKeyById(jwt.getKeyId()); if (publicKey == null) { throw new IllegalStateException("The given Public Key is null."); @@ -48,7 +47,7 @@ public void verify(DecodedJWT jwt) throws SignatureVerificationException { if (!valid) { throw new SignatureVerificationException(this); } - } catch (NoSuchAlgorithmException | SignatureException | InvalidKeyException | IllegalStateException e) { + } catch (NoSuchAlgorithmException | SignatureException | InvalidKeyException | IllegalStateException | IllegalArgumentException e) { throw new SignatureVerificationException(this, e); } } diff --git a/lib/src/main/java/com/auth0/jwt/algorithms/HMACAlgorithm.java b/lib/src/main/java/com/auth0/jwt/algorithms/HMACAlgorithm.java index 8f5e74a4..d942dbdd 100644 --- a/lib/src/main/java/com/auth0/jwt/algorithms/HMACAlgorithm.java +++ b/lib/src/main/java/com/auth0/jwt/algorithms/HMACAlgorithm.java @@ -3,11 +3,11 @@ import com.auth0.jwt.exceptions.SignatureGenerationException; import com.auth0.jwt.exceptions.SignatureVerificationException; import com.auth0.jwt.interfaces.DecodedJWT; -import org.apache.commons.codec.binary.Base64; import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; +import java.util.Base64; class HMACAlgorithm extends Algorithm { private final CryptoHelper crypto; @@ -41,14 +41,13 @@ static byte[] getSecretBytes(String secret) throws IllegalArgumentException { @Override public void verify(DecodedJWT jwt) throws SignatureVerificationException { - byte[] signatureBytes = Base64.decodeBase64(jwt.getSignature()); - try { + byte[] signatureBytes = Base64.getUrlDecoder().decode(jwt.getSignature()); boolean valid = crypto.verifySignatureFor(getDescription(), secret, jwt.getHeader(), jwt.getPayload(), signatureBytes); if (!valid) { throw new SignatureVerificationException(this); } - } catch (IllegalStateException | InvalidKeyException | NoSuchAlgorithmException e) { + } catch (IllegalStateException | InvalidKeyException | NoSuchAlgorithmException | IllegalArgumentException e) { throw new SignatureVerificationException(this, e); } } diff --git a/lib/src/main/java/com/auth0/jwt/algorithms/NoneAlgorithm.java b/lib/src/main/java/com/auth0/jwt/algorithms/NoneAlgorithm.java index f70b72b2..076a6b94 100644 --- a/lib/src/main/java/com/auth0/jwt/algorithms/NoneAlgorithm.java +++ b/lib/src/main/java/com/auth0/jwt/algorithms/NoneAlgorithm.java @@ -3,7 +3,8 @@ import com.auth0.jwt.exceptions.SignatureGenerationException; import com.auth0.jwt.exceptions.SignatureVerificationException; import com.auth0.jwt.interfaces.DecodedJWT; -import org.apache.commons.codec.binary.Base64; + +import java.util.Base64; class NoneAlgorithm extends Algorithm { @@ -13,9 +14,14 @@ class NoneAlgorithm extends Algorithm { @Override public void verify(DecodedJWT jwt) throws SignatureVerificationException { - byte[] signatureBytes = Base64.decodeBase64(jwt.getSignature()); - if (signatureBytes.length > 0) { - throw new SignatureVerificationException(this); + try { + byte[] signatureBytes = Base64.getUrlDecoder().decode(jwt.getSignature()); + + if (signatureBytes.length > 0) { + throw new SignatureVerificationException(this); + } + } catch (IllegalArgumentException e) { + throw new SignatureVerificationException(this, e); } } diff --git a/lib/src/main/java/com/auth0/jwt/algorithms/RSAAlgorithm.java b/lib/src/main/java/com/auth0/jwt/algorithms/RSAAlgorithm.java index f4267996..396ccca0 100644 --- a/lib/src/main/java/com/auth0/jwt/algorithms/RSAAlgorithm.java +++ b/lib/src/main/java/com/auth0/jwt/algorithms/RSAAlgorithm.java @@ -4,13 +4,13 @@ import com.auth0.jwt.exceptions.SignatureVerificationException; import com.auth0.jwt.interfaces.DecodedJWT; import com.auth0.jwt.interfaces.RSAKeyProvider; -import org.apache.commons.codec.binary.Base64; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SignatureException; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; +import java.util.Base64; class RSAAlgorithm extends Algorithm { private final RSAKeyProvider keyProvider; @@ -32,9 +32,8 @@ class RSAAlgorithm extends Algorithm { @Override public void verify(DecodedJWT jwt) throws SignatureVerificationException { - byte[] signatureBytes = Base64.decodeBase64(jwt.getSignature()); - try { + byte[] signatureBytes = Base64.getUrlDecoder().decode(jwt.getSignature()); RSAPublicKey publicKey = keyProvider.getPublicKeyById(jwt.getKeyId()); if (publicKey == null) { throw new IllegalStateException("The given Public Key is null."); @@ -43,7 +42,7 @@ public void verify(DecodedJWT jwt) throws SignatureVerificationException { if (!valid) { throw new SignatureVerificationException(this); } - } catch (NoSuchAlgorithmException | SignatureException | InvalidKeyException | IllegalStateException e) { + } catch (NoSuchAlgorithmException | SignatureException | InvalidKeyException | IllegalArgumentException | IllegalStateException e) { throw new SignatureVerificationException(this, e); } } diff --git a/lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java b/lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java index fc9cd3e2..ee4ef4b8 100644 --- a/lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java +++ b/lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java @@ -5,7 +5,7 @@ import com.auth0.jwt.interfaces.ECDSAKeyProvider; import com.auth0.jwt.interfaces.RSAKeyProvider; import com.fasterxml.jackson.databind.ObjectMapper; -import org.apache.commons.codec.binary.Base64; + import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -48,7 +48,7 @@ public void shouldAddHeaderClaim() { assertThat(signed, is(notNullValue())); String[] parts = signed.split("\\."); - String headerJson = new String(Base64.decodeBase64(parts[0]), StandardCharsets.UTF_8); + String headerJson = new String(Base64.getUrlDecoder().decode(parts[0]), StandardCharsets.UTF_8); assertThat(headerJson, JsonMatcher.hasEntry("asd", 123)); } @@ -73,7 +73,7 @@ public void shouldOverwriteExistingHeaderIfHeaderMapContainsTheSameKey() { assertThat(signed, is(notNullValue())); String[] parts = signed.split("\\."); - String headerJson = new String(Base64.decodeBase64(parts[0]), StandardCharsets.UTF_8); + String headerJson = new String(Base64.getUrlDecoder().decode(parts[0]), StandardCharsets.UTF_8); assertThat(headerJson, JsonMatcher.hasEntry(PublicClaims.KEY_ID, "xyz")); } @@ -89,7 +89,7 @@ public void shouldOverwriteExistingHeadersWhenSettingSameHeaderKey() { assertThat(signed, is(notNullValue())); String[] parts = signed.split("\\."); - String headerJson = new String(Base64.decodeBase64(parts[0]), StandardCharsets.UTF_8); + String headerJson = new String(Base64.getUrlDecoder().decode(parts[0]), StandardCharsets.UTF_8); assertThat(headerJson, JsonMatcher.hasEntry(PublicClaims.KEY_ID, "abc")); } @@ -106,7 +106,7 @@ public void shouldRemoveHeaderIfTheValueIsNull() { assertThat(signed, is(notNullValue())); String[] parts = signed.split("\\."); - String headerJson = new String(Base64.decodeBase64(parts[0]), StandardCharsets.UTF_8); + String headerJson = new String(Base64.getUrlDecoder().decode(parts[0]), StandardCharsets.UTF_8); assertThat(headerJson, JsonMatcher.isNotPresent(PublicClaims.KEY_ID)); assertThat(headerJson, JsonMatcher.hasEntry("test2", "isSet")); } @@ -119,7 +119,7 @@ public void shouldAddKeyId() { assertThat(signed, is(notNullValue())); String[] parts = signed.split("\\."); - String headerJson = new String(Base64.decodeBase64(parts[0]), StandardCharsets.UTF_8); + String headerJson = new String(Base64.getUrlDecoder().decode(parts[0]), StandardCharsets.UTF_8); assertThat(headerJson, JsonMatcher.hasEntry("kid", "56a8bd44da435300010000015f5ed")); } @@ -135,7 +135,7 @@ public void shouldAddKeyIdIfAvailableFromRSAAlgorithms() throws Exception { assertThat(signed, is(notNullValue())); String[] parts = signed.split("\\."); - String headerJson = new String(Base64.decodeBase64(parts[0]), StandardCharsets.UTF_8); + String headerJson = new String(Base64.getUrlDecoder().decode(parts[0]), StandardCharsets.UTF_8); assertThat(headerJson, JsonMatcher.hasEntry("kid", "my-key-id")); } @@ -152,7 +152,7 @@ public void shouldNotOverwriteKeyIdIfAddedFromRSAAlgorithms() throws Exception { assertThat(signed, is(notNullValue())); String[] parts = signed.split("\\."); - String headerJson = new String(Base64.decodeBase64(parts[0]), StandardCharsets.UTF_8); + String headerJson = new String(Base64.getUrlDecoder().decode(parts[0]), StandardCharsets.UTF_8); assertThat(headerJson, JsonMatcher.hasEntry("kid", "my-key-id")); } @@ -168,7 +168,7 @@ public void shouldAddKeyIdIfAvailableFromECDSAAlgorithms() throws Exception { assertThat(signed, is(notNullValue())); String[] parts = signed.split("\\."); - String headerJson = new String(Base64.decodeBase64(parts[0]), StandardCharsets.UTF_8); + String headerJson = new String(Base64.getUrlDecoder().decode(parts[0]), StandardCharsets.UTF_8); assertThat(headerJson, JsonMatcher.hasEntry("kid", "my-key-id")); } @@ -185,7 +185,7 @@ public void shouldNotOverwriteKeyIdIfAddedFromECDSAAlgorithms() throws Exception assertThat(signed, is(notNullValue())); String[] parts = signed.split("\\."); - String headerJson = new String(Base64.decodeBase64(parts[0]), StandardCharsets.UTF_8); + String headerJson = new String(Base64.getUrlDecoder().decode(parts[0]), StandardCharsets.UTF_8); assertThat(headerJson, JsonMatcher.hasEntry("kid", "my-key-id")); } @@ -315,7 +315,7 @@ public void shouldSetCorrectAlgorithmInTheHeader() { assertThat(signed, is(notNullValue())); String[] parts = signed.split("\\."); - String headerJson = new String(Base64.decodeBase64(parts[0]), StandardCharsets.UTF_8); + String headerJson = new String(Base64.getUrlDecoder().decode(parts[0]), StandardCharsets.UTF_8); assertThat(headerJson, JsonMatcher.hasEntry("alg", "HS256")); } @@ -326,7 +326,7 @@ public void shouldSetDefaultTypeInTheHeader() { assertThat(signed, is(notNullValue())); String[] parts = signed.split("\\."); - String headerJson = new String(Base64.decodeBase64(parts[0]), StandardCharsets.UTF_8); + String headerJson = new String(Base64.getUrlDecoder().decode(parts[0]), StandardCharsets.UTF_8); assertThat(headerJson, JsonMatcher.hasEntry("typ", "JWT")); } @@ -339,7 +339,7 @@ public void shouldSetCustomTypeInTheHeader() { assertThat(signed, is(notNullValue())); String[] parts = signed.split("\\."); - String headerJson = new String(Base64.decodeBase64(parts[0]), StandardCharsets.UTF_8); + String headerJson = new String(Base64.getUrlDecoder().decode(parts[0]), StandardCharsets.UTF_8); assertThat(headerJson, JsonMatcher.hasEntry("typ", "passport")); } @@ -531,7 +531,7 @@ public void shouldAcceptCustomMapClaimOfBasicObjectTypes() throws Exception { assertThat(jwt, is(notNullValue())); String[] parts = jwt.split("\\."); - String body = new String(Base64.decodeBase64(parts[1]), StandardCharsets.UTF_8); + String body = new String(Base64.getUrlDecoder().decode(parts[1]), StandardCharsets.UTF_8); ObjectMapper mapper = new ObjectMapper(); Map map = (Map) mapper.readValue(body, Map.class).get("data"); @@ -593,7 +593,7 @@ public void shouldAcceptCustomListClaimOfBasicObjectTypes() throws Exception { assertThat(jwt, is(notNullValue())); String[] parts = jwt.split("\\."); - String body = new String(Base64.decodeBase64(parts[1]), StandardCharsets.UTF_8); + String body = new String(Base64.getUrlDecoder().decode(parts[1]), StandardCharsets.UTF_8); ObjectMapper mapper = new ObjectMapper(); List list = (List) mapper.readValue(body, Map.class).get("data"); diff --git a/lib/src/test/java/com/auth0/jwt/JWTDecoderTest.java b/lib/src/test/java/com/auth0/jwt/JWTDecoderTest.java index 0623ee2a..f3d491c3 100644 --- a/lib/src/test/java/com/auth0/jwt/JWTDecoderTest.java +++ b/lib/src/test/java/com/auth0/jwt/JWTDecoderTest.java @@ -4,7 +4,6 @@ import com.auth0.jwt.impl.NullClaim; import com.auth0.jwt.interfaces.Claim; import com.auth0.jwt.interfaces.DecodedJWT; -import org.apache.commons.codec.binary.Base64; import org.hamcrest.collection.IsCollectionWithSize; import org.hamcrest.core.IsCollectionContaining; import org.junit.Assert; @@ -15,6 +14,7 @@ import java.io.*; import java.nio.charset.StandardCharsets; import java.time.Instant; +import java.util.Base64; import java.util.Date; import java.util.Map; @@ -65,6 +65,24 @@ public void shouldThrowIfHeaderHasInvalidJSONFormat() { customJWT(invalidJson, validJson, "signature"); } + @Test + public void shouldThrowWhenHeaderNotValidBase64() { + exception.expect(JWTDecodeException.class); + exception.expectCause(isA(IllegalArgumentException.class)); + + String jwt = "eyJhbGciOiJub25l+IiwiY3R5IjoiSldUIn0.eyJpc3MiOiJhdXRoMCJ9.Ox-WRXRaGAuWt2KfPvWiGcCrPqZtbp_4OnQzZXaTfss"; + JWT.decode(jwt); + } + + @Test + public void shouldThrowWhenPayloadNotValidBase64() { + exception.expect(JWTDecodeException.class); + exception.expectCause(isA(IllegalArgumentException.class)); + + String jwt = "eyJhbGciOiJub25lIiwiY3R5IjoiSldUIn0.eyJpc3MiOiJhdXRo+MCJ9.Ox-WRXRaGAuWt2KfPvWiGcCrPqZtbp_4OnQzZXaTfss"; + JWT.decode(jwt); + } + // Parts @Test @@ -364,8 +382,8 @@ public void shouldSerializeAndDeserialize() throws Exception { //Helper Methods private DecodedJWT customJWT(String jsonHeader, String jsonPayload, String signature) { - String header = Base64.encodeBase64URLSafeString(jsonHeader.getBytes(StandardCharsets.UTF_8)); - String body = Base64.encodeBase64URLSafeString(jsonPayload.getBytes(StandardCharsets.UTF_8)); + String header = Base64.getUrlEncoder().withoutPadding().encodeToString(jsonHeader.getBytes(StandardCharsets.UTF_8)); + String body = Base64.getUrlEncoder().withoutPadding().encodeToString(jsonPayload.getBytes(StandardCharsets.UTF_8)); return JWT.decode(String.format("%s.%s.%s", header, body, signature)); } diff --git a/lib/src/test/java/com/auth0/jwt/JWTTest.java b/lib/src/test/java/com/auth0/jwt/JWTTest.java index a05b05ee..2bfdd334 100644 --- a/lib/src/test/java/com/auth0/jwt/JWTTest.java +++ b/lib/src/test/java/com/auth0/jwt/JWTTest.java @@ -3,7 +3,6 @@ import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.interfaces.Clock; import com.auth0.jwt.interfaces.DecodedJWT; -import org.apache.commons.codec.binary.Base64; import org.hamcrest.collection.IsCollectionWithSize; import org.hamcrest.core.IsCollectionContaining; import org.junit.Rule; @@ -14,6 +13,7 @@ import java.security.interfaces.ECKey; import java.security.interfaces.RSAKey; import java.time.Instant; +import java.util.Base64; import java.util.Date; import static org.hamcrest.Matchers.*; @@ -445,7 +445,7 @@ public void shouldCreateAnEmptyHMAC256SignedToken() { assertThat(signed, is(notNullValue())); String[] parts = signed.split("\\."); - String headerJson = new String(Base64.decodeBase64(parts[0]), StandardCharsets.UTF_8); + String headerJson = new String(Base64.getUrlDecoder().decode(parts[0]), StandardCharsets.UTF_8); assertThat(headerJson, JsonMatcher.hasEntry("alg", "HS256")); assertThat(headerJson, JsonMatcher.hasEntry("typ", "JWT")); assertThat(parts[1], is("e30")); @@ -461,7 +461,7 @@ public void shouldCreateAnEmptyHMAC384SignedToken() { assertThat(signed, is(notNullValue())); String[] parts = signed.split("\\."); - String headerJson = new String(Base64.decodeBase64(parts[0]), StandardCharsets.UTF_8); + String headerJson = new String(Base64.getUrlDecoder().decode(parts[0]), StandardCharsets.UTF_8); assertThat(headerJson, JsonMatcher.hasEntry("alg", "HS384")); assertThat(headerJson, JsonMatcher.hasEntry("typ", "JWT")); assertThat(parts[1], is("e30")); @@ -477,7 +477,7 @@ public void shouldCreateAnEmptyHMAC512SignedToken() { assertThat(signed, is(notNullValue())); String[] parts = signed.split("\\."); - String headerJson = new String(Base64.decodeBase64(parts[0]), StandardCharsets.UTF_8); + String headerJson = new String(Base64.getUrlDecoder().decode(parts[0]), StandardCharsets.UTF_8); assertThat(headerJson, JsonMatcher.hasEntry("alg", "HS512")); assertThat(headerJson, JsonMatcher.hasEntry("typ", "JWT")); assertThat(parts[1], is("e30")); @@ -493,7 +493,7 @@ public void shouldCreateAnEmptyRSA256SignedToken() throws Exception { assertThat(signed, is(notNullValue())); String[] parts = signed.split("\\."); - String headerJson = new String(Base64.decodeBase64(parts[0]), StandardCharsets.UTF_8); + String headerJson = new String(Base64.getUrlDecoder().decode(parts[0]), StandardCharsets.UTF_8); assertThat(headerJson, JsonMatcher.hasEntry("alg", "RS256")); assertThat(headerJson, JsonMatcher.hasEntry("typ", "JWT")); assertThat(parts[1], is("e30")); @@ -509,7 +509,7 @@ public void shouldCreateAnEmptyRSA384SignedToken() throws Exception { assertThat(signed, is(notNullValue())); String[] parts = signed.split("\\."); - String headerJson = new String(Base64.decodeBase64(parts[0]), StandardCharsets.UTF_8); + String headerJson = new String(Base64.getUrlDecoder().decode(parts[0]), StandardCharsets.UTF_8); assertThat(headerJson, JsonMatcher.hasEntry("alg", "RS384")); assertThat(headerJson, JsonMatcher.hasEntry("typ", "JWT")); assertThat(parts[1], is("e30")); @@ -525,7 +525,7 @@ public void shouldCreateAnEmptyRSA512SignedToken() throws Exception { assertThat(signed, is(notNullValue())); String[] parts = signed.split("\\."); - String headerJson = new String(Base64.decodeBase64(parts[0]), StandardCharsets.UTF_8); + String headerJson = new String(Base64.getUrlDecoder().decode(parts[0]), StandardCharsets.UTF_8); assertThat(headerJson, JsonMatcher.hasEntry("alg", "RS512")); assertThat(headerJson, JsonMatcher.hasEntry("typ", "JWT")); assertThat(parts[1], is("e30")); @@ -541,7 +541,7 @@ public void shouldCreateAnEmptyECDSA256SignedToken() throws Exception { assertThat(signed, is(notNullValue())); String[] parts = signed.split("\\."); - String headerJson = new String(Base64.decodeBase64(parts[0]), StandardCharsets.UTF_8); + String headerJson = new String(Base64.getUrlDecoder().decode(parts[0]), StandardCharsets.UTF_8); assertThat(headerJson, JsonMatcher.hasEntry("alg", "ES256")); assertThat(headerJson, JsonMatcher.hasEntry("typ", "JWT")); assertThat(parts[1], is("e30")); @@ -557,7 +557,7 @@ public void shouldCreateAnEmptyECDSA384SignedToken() throws Exception { assertThat(signed, is(notNullValue())); String[] parts = signed.split("\\."); - String headerJson = new String(Base64.decodeBase64(parts[0]), StandardCharsets.UTF_8); + String headerJson = new String(Base64.getUrlDecoder().decode(parts[0]), StandardCharsets.UTF_8); assertThat(headerJson, JsonMatcher.hasEntry("alg", "ES384")); assertThat(headerJson, JsonMatcher.hasEntry("typ", "JWT")); assertThat(parts[1], is("e30")); @@ -573,7 +573,7 @@ public void shouldCreateAnEmptyECDSA512SignedToken() throws Exception { assertThat(signed, is(notNullValue())); String[] parts = signed.split("\\."); - String headerJson = new String(Base64.decodeBase64(parts[0]), StandardCharsets.UTF_8); + String headerJson = new String(Base64.getUrlDecoder().decode(parts[0]), StandardCharsets.UTF_8); assertThat(headerJson, JsonMatcher.hasEntry("alg", "ES512")); assertThat(headerJson, JsonMatcher.hasEntry("typ", "JWT")); assertThat(parts[1], is("e30")); diff --git a/lib/src/test/java/com/auth0/jwt/algorithms/CryptoTestHelper.java b/lib/src/test/java/com/auth0/jwt/algorithms/CryptoTestHelper.java index 68733353..897500e7 100644 --- a/lib/src/test/java/com/auth0/jwt/algorithms/CryptoTestHelper.java +++ b/lib/src/test/java/com/auth0/jwt/algorithms/CryptoTestHelper.java @@ -1,21 +1,20 @@ package com.auth0.jwt.algorithms; -import static org.hamcrest.Matchers.*; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.fail; - import java.nio.charset.StandardCharsets; +import java.util.Base64; import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.apache.commons.codec.binary.Base64; +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; public abstract class CryptoTestHelper { private static final Pattern authHeaderPattern = Pattern.compile("^([\\w-]+)\\.([\\w-]+)\\.([\\w-]+)"); public static String asJWT(Algorithm algorithm, String header, String payload) { byte[] signatureBytes = algorithm.sign(header.getBytes(StandardCharsets.UTF_8), payload.getBytes(StandardCharsets.UTF_8)); - String jwtSignature = Base64.encodeBase64URLSafeString(signatureBytes); + String jwtSignature = Base64.getUrlEncoder().withoutPadding().encodeToString(signatureBytes); return String.format("%s.%s.%s", header, payload, jwtSignature); } diff --git a/lib/src/test/java/com/auth0/jwt/algorithms/ECDSAAlgorithmTest.java b/lib/src/test/java/com/auth0/jwt/algorithms/ECDSAAlgorithmTest.java index 1717d36a..75f96413 100644 --- a/lib/src/test/java/com/auth0/jwt/algorithms/ECDSAAlgorithmTest.java +++ b/lib/src/test/java/com/auth0/jwt/algorithms/ECDSAAlgorithmTest.java @@ -4,8 +4,6 @@ import com.auth0.jwt.exceptions.SignatureGenerationException; import com.auth0.jwt.exceptions.SignatureVerificationException; import com.auth0.jwt.interfaces.ECDSAKeyProvider; - -import org.apache.commons.codec.binary.Base64; import org.hamcrest.Matchers; import org.hamcrest.collection.IsIn; import org.junit.Assert; @@ -20,13 +18,15 @@ import java.security.interfaces.ECPrivateKey; import java.security.interfaces.ECPublicKey; import java.util.Arrays; +import java.util.Base64; import static com.auth0.jwt.PemUtils.readPrivateKeyFromFile; import static com.auth0.jwt.PemUtils.readPublicKeyFromFile; import static com.auth0.jwt.algorithms.CryptoTestHelper.asJWT; import static com.auth0.jwt.algorithms.CryptoTestHelper.assertSignaturePresent; import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.Matchers.*; +import static org.hamcrest.Matchers.isA; +import static org.hamcrest.Matchers.nullValue; import static org.junit.Assert.assertThat; import static org.junit.internal.matchers.ThrowableMessageMatcher.hasMessage; import static org.mockito.ArgumentMatchers.any; @@ -150,7 +150,7 @@ public void shouldFailECDSA256VerificationOnInvalidJOSESignatureLength() throws byte[] bytes = new byte[63]; new SecureRandom().nextBytes(bytes); - String signature = Base64.encodeBase64URLSafeString(bytes); + String signature = Base64.getUrlEncoder().withoutPadding().encodeToString(bytes); String jwt = "eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJhdXRoMCJ9." + signature; Algorithm algorithm = Algorithm.ECDSA256((ECKey) readPublicKeyFromFile(INVALID_PUBLIC_KEY_FILE_256, "EC")); algorithm.verify(JWT.decode(jwt)); @@ -163,7 +163,7 @@ public void shouldFailECDSA256VerificationOnInvalidJOSESignature() throws Except byte[] bytes = new byte[64]; new SecureRandom().nextBytes(bytes); - String signature = Base64.encodeBase64URLSafeString(bytes); + String signature = Base64.getUrlEncoder().withoutPadding().encodeToString(bytes); String jwt = "eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJhdXRoMCJ9." + signature; Algorithm algorithm = Algorithm.ECDSA256((ECKey) readPublicKeyFromFile(INVALID_PUBLIC_KEY_FILE_256, "EC")); algorithm.verify(JWT.decode(jwt)); @@ -177,7 +177,7 @@ public void shouldFailECDSA256VerificationOnInvalidDERSignature() throws Excepti byte[] bytes = new byte[64]; bytes[0] = 0x30; new SecureRandom().nextBytes(bytes); - String signature = Base64.encodeBase64URLSafeString(bytes); + String signature = Base64.getUrlEncoder().withoutPadding().encodeToString(bytes); String jwt = "eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJhdXRoMCJ9." + signature; Algorithm algorithm = Algorithm.ECDSA256((ECKey) readPublicKeyFromFile(INVALID_PUBLIC_KEY_FILE_256, "EC")); algorithm.verify(JWT.decode(jwt)); @@ -275,7 +275,7 @@ public void shouldFailECDSA384VerificationOnInvalidJOSESignatureLength() throws byte[] bytes = new byte[95]; new SecureRandom().nextBytes(bytes); - String signature = Base64.encodeBase64URLSafeString(bytes); + String signature = Base64.getUrlEncoder().withoutPadding().encodeToString(bytes); String jwt = "eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJhdXRoMCJ9." + signature; Algorithm algorithm = Algorithm.ECDSA384((ECKey) readPublicKeyFromFile(INVALID_PUBLIC_KEY_FILE_384, "EC")); algorithm.verify(JWT.decode(jwt)); @@ -288,7 +288,7 @@ public void shouldFailECDSA384VerificationOnInvalidJOSESignature() throws Except byte[] bytes = new byte[96]; new SecureRandom().nextBytes(bytes); - String signature = Base64.encodeBase64URLSafeString(bytes); + String signature = Base64.getUrlEncoder().withoutPadding().encodeToString(bytes); String jwt = "eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJhdXRoMCJ9." + signature; Algorithm algorithm = Algorithm.ECDSA384((ECKey) readPublicKeyFromFile(INVALID_PUBLIC_KEY_FILE_384, "EC")); algorithm.verify(JWT.decode(jwt)); @@ -302,7 +302,7 @@ public void shouldFailECDSA384VerificationOnInvalidDERSignature() throws Excepti byte[] bytes = new byte[96]; new SecureRandom().nextBytes(bytes); bytes[0] = 0x30; - String signature = Base64.encodeBase64URLSafeString(bytes); + String signature = Base64.getUrlEncoder().withoutPadding().encodeToString(bytes); String jwt = "eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJhdXRoMCJ9." + signature; Algorithm algorithm = Algorithm.ECDSA384((ECKey) readPublicKeyFromFile(INVALID_PUBLIC_KEY_FILE_384, "EC")); algorithm.verify(JWT.decode(jwt)); @@ -400,7 +400,7 @@ public void shouldFailECDSA512VerificationOnInvalidJOSESignatureLength() throws byte[] bytes = new byte[131]; new SecureRandom().nextBytes(bytes); - String signature = Base64.encodeBase64URLSafeString(bytes); + String signature = Base64.getUrlEncoder().withoutPadding().encodeToString(bytes); String jwt = "eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJhdXRoMCJ9." + signature; Algorithm algorithm = Algorithm.ECDSA512((ECKey) readPublicKeyFromFile(INVALID_PUBLIC_KEY_FILE_512, "EC")); algorithm.verify(JWT.decode(jwt)); @@ -413,7 +413,7 @@ public void shouldFailECDSA512VerificationOnInvalidJOSESignature() throws Except byte[] bytes = new byte[132]; new SecureRandom().nextBytes(bytes); - String signature = Base64.encodeBase64URLSafeString(bytes); + String signature = Base64.getUrlEncoder().withoutPadding().encodeToString(bytes); String jwt = "eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJhdXRoMCJ9." + signature; Algorithm algorithm = Algorithm.ECDSA512((ECKey) readPublicKeyFromFile(INVALID_PUBLIC_KEY_FILE_512, "EC")); algorithm.verify(JWT.decode(jwt)); @@ -427,7 +427,7 @@ public void shouldFailECDSA512VerificationOnInvalidDERSignature() throws Excepti byte[] bytes = new byte[132]; new SecureRandom().nextBytes(bytes); bytes[0] = 0x30; - String signature = Base64.encodeBase64URLSafeString(bytes); + String signature = Base64.getUrlEncoder().withoutPadding().encodeToString(bytes); String jwt = "eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJhdXRoMCJ9." + signature; Algorithm algorithm = Algorithm.ECDSA512((ECKey) readPublicKeyFromFile(INVALID_PUBLIC_KEY_FILE_512, "EC")); algorithm.verify(JWT.decode(jwt)); @@ -442,7 +442,7 @@ public void shouldFailJOSEToDERConversionOnInvalidJOSESignatureLength() throws E byte[] bytes = new byte[256]; new SecureRandom().nextBytes(bytes); - String signature = Base64.encodeBase64URLSafeString(bytes); + String signature = Base64.getUrlEncoder().withoutPadding().encodeToString(bytes); String jwt = "eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJhdXRoMCJ9." + signature; ECPublicKey publicKey = (ECPublicKey) readPublicKeyFromFile(PUBLIC_KEY_FILE_256, "EC"); @@ -506,7 +506,18 @@ public void shouldThrowOnVerifyWhenTheSignatureIsNotPrepared() throws Exception algorithm.verify(JWT.decode(jwt)); } - //Sign + @Test + public void shouldThrowWhenSignatureNotValidBase64() throws Exception { + exception.expect(SignatureVerificationException.class); + exception.expectCause(isA(IllegalArgumentException.class)); + + String jwt = "eyJhbGciOiJFUzUxMiJ9.eyJpc3MiOiJhdXRoMCJ9.MIGIAkIB4Ik8MixIeHBFIZkJjquymLzN6Q7DQr2pgw2uJ0UW726GsDVCsb4RTFeUTTrKaHZHtHPRoTuTEHCuerwvxo4+EICQgGALKocz3lL8qfH1444LNBLaOSNJp3RNkB5YHDEhQEsox21PMA9kau2TcxkOW9jGX6b9N9FhlGo0mmWFhVCR1YNg"; + ECKey key = (ECKey) readPublicKeyFromFile(PUBLIC_KEY_FILE_512, "EC"); + Algorithm algorithm = Algorithm.ECDSA512(key); + algorithm.verify(JWT.decode(jwt)); + } + + //Sign private static final String ES256Header = "eyJhbGciOiJFUzI1NiJ9"; private static final String ES384Header = "eyJhbGciOiJFUzM4NCJ9"; private static final String ES512Header = "eyJhbGciOiJFUzUxMiJ9"; @@ -530,7 +541,7 @@ public void shouldDoECDSA256Signing() throws Exception { public void shouldDoECDSA256SigningWithBothKeys() throws Exception { Algorithm algorithm = Algorithm.ECDSA256((ECPublicKey) readPublicKeyFromFile(PUBLIC_KEY_FILE_256, "EC"), (ECPrivateKey) readPrivateKeyFromFile(PRIVATE_KEY_FILE_256, "EC")); byte[] signatureBytes = algorithm.sign(ES256HeaderBytes, auth0IssPayloadBytes); - String jwtSignature = Base64.encodeBase64URLSafeString(signatureBytes); + String jwtSignature = Base64.getUrlEncoder().withoutPadding().encodeToString(signatureBytes); String jwt = String.format("%s.%s.%s", ES256Header, auth0IssPayload, jwtSignature); assertSignaturePresent(jwt); @@ -1154,12 +1165,12 @@ public void shouldBeEqualSignatureMethodDecodeResults() throws Exception { bout.write('.'); bout.write(payloadBytes); - String jwtSignature1 = Base64.encodeBase64URLSafeString(algorithm.sign(bout.toByteArray())); + String jwtSignature1 = Base64.getUrlEncoder().withoutPadding().encodeToString(algorithm.sign(bout.toByteArray())); String jwt1 = String.format("%s.%s.%s", header, payload, jwtSignature1); algorithm.verify(JWT.decode(jwt1)); - String jwtSignature2 = Base64.encodeBase64URLSafeString(algorithm.sign(headerBytes, payloadBytes)); + String jwtSignature2 = Base64.getUrlEncoder().withoutPadding().encodeToString(algorithm.sign(headerBytes, payloadBytes)); String jwt2 = String.format("%s.%s.%s", header, payload, jwtSignature2); algorithm.verify(JWT.decode(jwt2)); diff --git a/lib/src/test/java/com/auth0/jwt/algorithms/ECDSABouncyCastleProviderTests.java b/lib/src/test/java/com/auth0/jwt/algorithms/ECDSABouncyCastleProviderTests.java index 60e1457d..c1d8cc08 100644 --- a/lib/src/test/java/com/auth0/jwt/algorithms/ECDSABouncyCastleProviderTests.java +++ b/lib/src/test/java/com/auth0/jwt/algorithms/ECDSABouncyCastleProviderTests.java @@ -1,11 +1,9 @@ package com.auth0.jwt.algorithms; -import static com.auth0.jwt.algorithms.CryptoTestHelper.*; import com.auth0.jwt.JWT; import com.auth0.jwt.exceptions.SignatureGenerationException; import com.auth0.jwt.exceptions.SignatureVerificationException; import com.auth0.jwt.interfaces.ECDSAKeyProvider; -import org.apache.commons.codec.binary.Base64; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.junit.AfterClass; import org.junit.BeforeClass; @@ -18,9 +16,12 @@ import java.security.interfaces.ECKey; import java.security.interfaces.ECPrivateKey; import java.security.interfaces.ECPublicKey; +import java.util.Base64; import static com.auth0.jwt.PemUtils.readPrivateKeyFromFile; import static com.auth0.jwt.PemUtils.readPublicKeyFromFile; +import static com.auth0.jwt.algorithms.CryptoTestHelper.asJWT; +import static com.auth0.jwt.algorithms.CryptoTestHelper.assertSignaturePresent; import static com.auth0.jwt.algorithms.ECDSAAlgorithmTest.*; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.Matchers.*; @@ -87,7 +88,7 @@ public void shouldThrowOnECDSA256VerificationWithDERSignature() throws Exception exception.expectCause(isA(SignatureException.class)); exception.expectCause(hasMessage(is("Invalid JOSE signature format."))); - String jwt = "eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJhdXRoMCJ9.MEYCIQDiJWTf5jS/hFPj/0hpCWn7x1n/h+xPMjKWCs9MMusS9AIhAMcFPJVLe2A9uvb8hl8sRO2IpGoKDRpDmyH14ixNPAHW"; + String jwt = "eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJhdXRoMCJ9.MEYCIQDiJWTf5jShFPj0hpCWn7x1nhxPMjKWCs9MMusS9AIhAMcFPJVLe2A9uvb8hl8sRO2IpGoKDRpDmyH14ixNPAHW"; ECKey key = (ECKey) readPublicKeyFromFile(PUBLIC_KEY_FILE_256, "EC"); Algorithm algorithm = Algorithm.ECDSA256(key); algorithm.verify(JWT.decode(jwt)); @@ -107,7 +108,7 @@ public void shouldThrowOnECDSA256VerificationWithDERSignatureWithBothKeys() thro exception.expectCause(isA(SignatureException.class)); exception.expectCause(hasMessage(is("Invalid JOSE signature format."))); - String jwt = "eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJhdXRoMCJ9.MEYCIQDiJWTf5jS/hFPj/0hpCWn7x1n/h+xPMjKWCs9MMusS9AIhAMcFPJVLe2A9uvb8hl8sRO2IpGoKDRpDmyH14ixNPAHW"; + String jwt = "eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJhdXRoMCJ9.MEYCIQDiJWTf5jShFPj0hpCWn7x1nhxPMjKWCs9MMusS9AIhAMcFPJVLe2A9uvb8hl8sRO2IpGoKDRpDmyH14ixNPAHW"; Algorithm algorithm = Algorithm.ECDSA256((ECPublicKey) readPublicKeyFromFile(PUBLIC_KEY_FILE_256, "EC"), (ECPrivateKey) readPrivateKeyFromFile(PRIVATE_KEY_FILE_256, "EC")); algorithm.verify(JWT.decode(jwt)); } @@ -164,7 +165,7 @@ public void shouldFailECDSA256VerificationOnInvalidJOSESignatureLength() throws byte[] bytes = new byte[63]; new SecureRandom().nextBytes(bytes); - String signature = Base64.encodeBase64URLSafeString(bytes); + String signature = Base64.getUrlEncoder().withoutPadding().encodeToString(bytes); String jwt = "eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJhdXRoMCJ9." + signature; Algorithm algorithm = Algorithm.ECDSA256((ECKey) readPublicKeyFromFile(INVALID_PUBLIC_KEY_FILE_256, "EC")); algorithm.verify(JWT.decode(jwt)); @@ -177,7 +178,7 @@ public void shouldFailECDSA256VerificationOnInvalidJOSESignature() throws Except byte[] bytes = new byte[64]; new SecureRandom().nextBytes(bytes); - String signature = Base64.encodeBase64URLSafeString(bytes); + String signature = Base64.getUrlEncoder().withoutPadding().encodeToString(bytes); String jwt = "eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJhdXRoMCJ9." + signature; Algorithm algorithm = Algorithm.ECDSA256((ECKey) readPublicKeyFromFile(INVALID_PUBLIC_KEY_FILE_256, "EC")); algorithm.verify(JWT.decode(jwt)); @@ -191,7 +192,7 @@ public void shouldFailECDSA256VerificationOnInvalidDERSignature() throws Excepti byte[] bytes = new byte[64]; bytes[0] = 0x30; new SecureRandom().nextBytes(bytes); - String signature = Base64.encodeBase64URLSafeString(bytes); + String signature = Base64.getUrlEncoder().withoutPadding().encodeToString(bytes); String jwt = "eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJhdXRoMCJ9." + signature; Algorithm algorithm = Algorithm.ECDSA256((ECKey) readPublicKeyFromFile(INVALID_PUBLIC_KEY_FILE_256, "EC")); algorithm.verify(JWT.decode(jwt)); @@ -212,7 +213,7 @@ public void shouldThrowOnECDSA384VerificationWithDERSignature() throws Exception exception.expectCause(isA(SignatureException.class)); exception.expectCause(hasMessage(is("Invalid JOSE signature format."))); - String jwt = "eyJhbGciOiJFUzM4NCJ9.eyJpc3MiOiJhdXRoMCJ9.MGUCMQDnRRTlUo10XXB/KRjyNAEqm+4dmh7ohkEmbk2+gHxtH6GdGDq2L4Idua+hG2Ut+ccCMH8CE2v/HCTMuk3pzAtoOtxkB8rXPK2KF6m8LUuEdCqPwF2yxVJn8ZxpzAur+DEv8w=="; + String jwt = "eyJhbGciOiJFUzM4NCJ9.eyJpc3MiOiJhdXRoMCJ9.MGUCMQDnRRTlUo10XXBKRjyNAEqm4dmh7ohkEmbk2gHxtH6GdGDq2L4IduahG2UtccCMH8CE2vHCTMuk3pzAtoOtxkB8rXPK2KF6m8LUuEdCqPwF2yxVJn8ZxpzAurDEv8w"; ECKey key = (ECKey) readPublicKeyFromFile(PUBLIC_KEY_FILE_384, "EC"); Algorithm algorithm = Algorithm.ECDSA384(key); algorithm.verify(JWT.decode(jwt)); @@ -232,7 +233,7 @@ public void shouldThrowOnECDSA384VerificationWithDERSignatureWithBothKeys() thro exception.expectCause(isA(SignatureException.class)); exception.expectCause(hasMessage(is("Invalid JOSE signature format."))); - String jwt = "eyJhbGciOiJFUzM4NCJ9.eyJpc3MiOiJhdXRoMCJ9.MGUCMQDnRRTlUo10XXB/KRjyNAEqm+4dmh7ohkEmbk2+gHxtH6GdGDq2L4Idua+hG2Ut+ccCMH8CE2v/HCTMuk3pzAtoOtxkB8rXPK2KF6m8LUuEdCqPwF2yxVJn8ZxpzAur+DEv8w=="; + String jwt = "eyJhbGciOiJFUzM4NCJ9.eyJpc3MiOiJhdXRoMCJ9.MGUCMQDnRRTlUo10XXBKRjyNAEqm4dmh7ohkEmbk2gHxtH6GdGDq2L4IduahG2UtccCMH8CE2vHCTMuk3pzAtoOtxkB8rXPK2KF6m8LUuEdCqPwF2yxVJn8ZxpzAurDEv8w"; Algorithm algorithm = Algorithm.ECDSA384((ECPublicKey) readPublicKeyFromFile(PUBLIC_KEY_FILE_384, "EC"), (ECPrivateKey) readPrivateKeyFromFile(PRIVATE_KEY_FILE_384, "EC")); algorithm.verify(JWT.decode(jwt)); } @@ -289,7 +290,7 @@ public void shouldFailECDSA384VerificationOnInvalidJOSESignatureLength() throws byte[] bytes = new byte[95]; new SecureRandom().nextBytes(bytes); - String signature = Base64.encodeBase64URLSafeString(bytes); + String signature = Base64.getUrlEncoder().withoutPadding().encodeToString(bytes); String jwt = "eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJhdXRoMCJ9." + signature; Algorithm algorithm = Algorithm.ECDSA384((ECKey) readPublicKeyFromFile(INVALID_PUBLIC_KEY_FILE_384, "EC")); algorithm.verify(JWT.decode(jwt)); @@ -302,7 +303,7 @@ public void shouldFailECDSA384VerificationOnInvalidJOSESignature() throws Except byte[] bytes = new byte[96]; new SecureRandom().nextBytes(bytes); - String signature = Base64.encodeBase64URLSafeString(bytes); + String signature = Base64.getUrlEncoder().withoutPadding().encodeToString(bytes); String jwt = "eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJhdXRoMCJ9." + signature; Algorithm algorithm = Algorithm.ECDSA384((ECKey) readPublicKeyFromFile(INVALID_PUBLIC_KEY_FILE_384, "EC")); algorithm.verify(JWT.decode(jwt)); @@ -316,7 +317,7 @@ public void shouldFailECDSA384VerificationOnInvalidDERSignature() throws Excepti byte[] bytes = new byte[96]; new SecureRandom().nextBytes(bytes); bytes[0] = 0x30; - String signature = Base64.encodeBase64URLSafeString(bytes); + String signature = Base64.getUrlEncoder().withoutPadding().encodeToString(bytes); String jwt = "eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJhdXRoMCJ9." + signature; Algorithm algorithm = Algorithm.ECDSA384((ECKey) readPublicKeyFromFile(INVALID_PUBLIC_KEY_FILE_384, "EC")); algorithm.verify(JWT.decode(jwt)); @@ -337,7 +338,7 @@ public void shouldThrowOnECDSA512VerificationWithDERSignature() throws Exception exception.expectCause(isA(SignatureException.class)); exception.expectCause(hasMessage(is("Invalid JOSE signature format."))); - String jwt = "eyJhbGciOiJFUzUxMiJ9.eyJpc3MiOiJhdXRoMCJ9.MIGIAkIB4Ik8MixIeHBFIZkJjquymLzN6Q7DQr2pgw2uJ0/UW726GsDVCsb4RTFeUTTrK+aHZHtHPRoTuTEHCuerwvxo4EICQgGALKocz3lL8qfH1444LNBLaOSNJp3RNkB5YHDEhQEsox21PMA9kau2TcxkOW9jGX6b9N9FhlGo0/mmWFhVCR1YNg=="; + String jwt = "eyJhbGciOiJFUzUxMiJ9.eyJpc3MiOiJhdXRoMCJ9.MIGIAkIB4Ik8MixIeHBFIZkJjquymLzN6Q7DQr2pgw2uJ0UW726GsDVCsb4RTFeUTTrKaHZHtHPRoTuTEHCuerwvxo4EICQgGALKocz3lL8qfH1444LNBLaOSNJp3RNkB5YHDEhQEsox21PMA9kau2TcxkOW9jGX6b9N9FhlGo0mmWFhVCR1YNg"; ECKey key = (ECKey) readPublicKeyFromFile(PUBLIC_KEY_FILE_512, "EC"); Algorithm algorithm = Algorithm.ECDSA512(key); algorithm.verify(JWT.decode(jwt)); @@ -357,7 +358,7 @@ public void shouldThrowECDSA512VerificationWithDERSignatureWithBothKeys() throws exception.expectCause(isA(SignatureException.class)); exception.expectCause(hasMessage(is("Invalid JOSE signature format."))); - String jwt = "eyJhbGciOiJFUzUxMiJ9.eyJpc3MiOiJhdXRoMCJ9.MIGIAkIB4Ik8MixIeHBFIZkJjquymLzN6Q7DQr2pgw2uJ0/UW726GsDVCsb4RTFeUTTrK+aHZHtHPRoTuTEHCuerwvxo4EICQgGALKocz3lL8qfH1444LNBLaOSNJp3RNkB5YHDEhQEsox21PMA9kau2TcxkOW9jGX6b9N9FhlGo0/mmWFhVCR1YNg=="; + String jwt = "eyJhbGciOiJFUzUxMiJ9.eyJpc3MiOiJhdXRoMCJ9.MIGIAkIB4Ik8MixIeHBFIZkJjquymLzN6Q7DQr2pgw2uJ0UW726GsDVCsb4RTFeUTTrKaHZHtHPRoTuTEHCuerwvxo4EICQgGALKocz3lL8qfH1444LNBLaOSNJp3RNkB5YHDEhQEsox21PMA9kau2TcxkOW9jGX6b9N9FhlGo0mmWFhVCR1YNg"; Algorithm algorithm = Algorithm.ECDSA512((ECPublicKey) readPublicKeyFromFile(PUBLIC_KEY_FILE_512, "EC"), (ECPrivateKey) readPrivateKeyFromFile(PRIVATE_KEY_FILE_512, "EC")); algorithm.verify(JWT.decode(jwt)); } @@ -414,7 +415,7 @@ public void shouldFailECDSA512VerificationOnInvalidJOSESignatureLength() throws byte[] bytes = new byte[131]; new SecureRandom().nextBytes(bytes); - String signature = Base64.encodeBase64URLSafeString(bytes); + String signature = Base64.getUrlEncoder().withoutPadding().encodeToString(bytes); String jwt = "eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJhdXRoMCJ9." + signature; Algorithm algorithm = Algorithm.ECDSA512((ECKey) readPublicKeyFromFile(INVALID_PUBLIC_KEY_FILE_512, "EC")); algorithm.verify(JWT.decode(jwt)); @@ -427,7 +428,7 @@ public void shouldFailECDSA512VerificationOnInvalidJOSESignature() throws Except byte[] bytes = new byte[132]; new SecureRandom().nextBytes(bytes); - String signature = Base64.encodeBase64URLSafeString(bytes); + String signature = Base64.getUrlEncoder().withoutPadding().encodeToString(bytes); String jwt = "eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJhdXRoMCJ9." + signature; Algorithm algorithm = Algorithm.ECDSA512((ECKey) readPublicKeyFromFile(INVALID_PUBLIC_KEY_FILE_512, "EC")); algorithm.verify(JWT.decode(jwt)); @@ -441,7 +442,7 @@ public void shouldFailECDSA512VerificationOnInvalidDERSignature() throws Excepti byte[] bytes = new byte[132]; new SecureRandom().nextBytes(bytes); bytes[0] = 0x30; - String signature = Base64.encodeBase64URLSafeString(bytes); + String signature = Base64.getUrlEncoder().withoutPadding().encodeToString(bytes); String jwt = "eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJhdXRoMCJ9." + signature; Algorithm algorithm = Algorithm.ECDSA512((ECKey) readPublicKeyFromFile(INVALID_PUBLIC_KEY_FILE_512, "EC")); algorithm.verify(JWT.decode(jwt)); @@ -456,7 +457,7 @@ public void shouldFailJOSEToDERConversionOnInvalidJOSESignatureLength() throws E byte[] bytes = new byte[256]; new SecureRandom().nextBytes(bytes); - String signature = Base64.encodeBase64URLSafeString(bytes); + String signature = Base64.getUrlEncoder().withoutPadding().encodeToString(bytes); String jwt = "eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJhdXRoMCJ9." + signature; ECPublicKey publicKey = (ECPublicKey) readPublicKeyFromFile(PUBLIC_KEY_FILE_256, "EC"); diff --git a/lib/src/test/java/com/auth0/jwt/algorithms/HMACAlgorithmTest.java b/lib/src/test/java/com/auth0/jwt/algorithms/HMACAlgorithmTest.java index f6f784c8..d2719005 100644 --- a/lib/src/test/java/com/auth0/jwt/algorithms/HMACAlgorithmTest.java +++ b/lib/src/test/java/com/auth0/jwt/algorithms/HMACAlgorithmTest.java @@ -276,4 +276,18 @@ public void shouldBeEqualSignatureMethodResults() throws Exception { assertThat(algorithm.sign(bout.toByteArray()), is(algorithm.sign(header, payload))); } -} + + @Test + public void shouldThrowWhenSignatureNotValidBase64() throws Exception { + exception.expect(SignatureVerificationException.class); + exception.expectCause(isA(IllegalArgumentException.class)); + + CryptoHelper crypto = mock(CryptoHelper.class); + when(crypto.verifySignatureFor(anyString(), any(byte[].class), any(String.class), any(String.class), any(byte[].class))) + .thenThrow(NoSuchAlgorithmException.class); + + Algorithm algorithm = new HMACAlgorithm(crypto, "some-alg", "some-algorithm", "secret".getBytes(StandardCharsets.UTF_8)); + String jwt = "eyJhbGciOiJIUzI1NiIsImN0eSI6IkpXVCJ9.eyJpc3MiOiJhdXRoMCJ9.mZ0m_N1J4PgeqWm+i903JuUoDRZDBPB7HwkS4nVyWH1M"; + algorithm.verify(JWT.decode(jwt)); + } +} \ No newline at end of file diff --git a/lib/src/test/java/com/auth0/jwt/algorithms/NoneAlgorithmTest.java b/lib/src/test/java/com/auth0/jwt/algorithms/NoneAlgorithmTest.java index f07ca5fa..848c99b0 100644 --- a/lib/src/test/java/com/auth0/jwt/algorithms/NoneAlgorithmTest.java +++ b/lib/src/test/java/com/auth0/jwt/algorithms/NoneAlgorithmTest.java @@ -7,8 +7,10 @@ import org.junit.Test; import org.junit.rules.ExpectedException; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.nullValue; +import java.security.interfaces.ECKey; + +import static com.auth0.jwt.PemUtils.readPublicKeyFromFile; +import static org.hamcrest.Matchers.*; import static org.junit.Assert.assertThat; public class NoneAlgorithmTest { @@ -44,4 +46,14 @@ public void shouldFailNoneVerificationWhenSignatureIsPresent() { public void shouldReturnNullSigningKeyId() { assertThat(Algorithm.none().getSigningKeyId(), is(nullValue())); } -} + + @Test + public void shouldThrowWhenSignatureNotValidBase64() { + exception.expect(SignatureVerificationException.class); + exception.expectCause(isA(IllegalArgumentException.class)); + + String jwt = "eyJhbGciOiJub25lIiwiY3R5IjoiSldUIn0.eyJpc3MiOiJhdXRoMCJ9.Ox-WRXRaGAuWt2KfPvW+iGcCrPqZtbp_4OnQzZXaTfss"; + Algorithm algorithm = Algorithm.none(); + algorithm.verify(JWT.decode(jwt)); + } +} \ No newline at end of file diff --git a/lib/src/test/java/com/auth0/jwt/algorithms/RSAAlgorithmTest.java b/lib/src/test/java/com/auth0/jwt/algorithms/RSAAlgorithmTest.java index f44ae367..c812c149 100644 --- a/lib/src/test/java/com/auth0/jwt/algorithms/RSAAlgorithmTest.java +++ b/lib/src/test/java/com/auth0/jwt/algorithms/RSAAlgorithmTest.java @@ -568,4 +568,13 @@ public void shouldFailOnRSA256SigningWithDeprecatedMethodWhenProvidedPrivateKeyI algorithm.sign(new byte[0]); } + @Test + public void shouldThrowWhenSignatureNotValidBase64() throws Exception { + exception.expect(SignatureVerificationException.class); + exception.expectCause(isA(IllegalArgumentException.class)); + + String jwt = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhdXRoMCJ9.dxXF3MdsyW-AuvwJpaQtrZ33fAde9xWxpLIg9cO2tMLH2GSRNu+LAe61KsJusZhqZB9Iy7DvflcmRz-9OZndm6cj_ThGeJH2LLc90K83UEvvRPo8l85RrQb8PcanxCgIs2RcZOLygERizB3pr5icGkzR7R2y6zgNCjKJ5_NJ6EiZsGN6_nc2PRK_DbyY-Wn0QDxIxKoA5YgQJ9qafe7IN980pXvQv2Z62c3XR8dYuaXBqhthBj-AbaFHEpZapN-V-TmuLNzR2MCB6Xr7BYMuCaqWf_XU8og4XNe8f_8w9Wv5vvgqMM1KhqVpG5VdMJv4o_L4NoCROHhtUQSLRh2M9cA"; + Algorithm algorithm = Algorithm.RSA256((RSAKey) readPrivateKeyFromFile(PRIVATE_KEY_FILE, "RSA")); + algorithm.verify(JWT.decode(jwt)); + } } \ No newline at end of file From 99a405b7a1bc03e21052259bdcc7d2007405f2fc Mon Sep 17 00:00:00 2001 From: Jim Anderson Date: Sun, 8 Mar 2020 20:53:42 -0500 Subject: [PATCH 12/14] Deprecate Date and array based claim methods (#403) --- .../main/java/com/auth0/jwt/JWTCreator.java | 18 ++++++++++++++---- .../java/com/auth0/jwt/interfaces/Claim.java | 3 ++- .../java/com/auth0/jwt/interfaces/Clock.java | 3 ++- .../java/com/auth0/jwt/interfaces/Payload.java | 9 ++++++--- .../com/auth0/jwt/interfaces/Verification.java | 3 ++- 5 files changed, 26 insertions(+), 10 deletions(-) diff --git a/lib/src/main/java/com/auth0/jwt/JWTCreator.java b/lib/src/main/java/com/auth0/jwt/JWTCreator.java index 07b609e8..6434ea27 100644 --- a/lib/src/main/java/com/auth0/jwt/JWTCreator.java +++ b/lib/src/main/java/com/auth0/jwt/JWTCreator.java @@ -147,8 +147,9 @@ public Builder withExpiresAt(Instant expiresAt) { * * @param expiresAt the Expires At value. * @return this same Builder instance. + * @deprecated Use {@linkplain #withExpiresAt(Instant)} instead. */ - // TODO - Deprecate this method in favor of withExpiresAtInstant + @Deprecated public Builder withExpiresAt(Date expiresAt) { return withExpiresAt(expiresAt.toInstant()); } @@ -169,8 +170,9 @@ public Builder withNotBefore(Instant notBefore) { * * @param notBefore the Not Before value. * @return this same Builder instance. + * @deprecated Use {@linkplain #withNotBefore(Instant)} instead. */ - // TODO - Deprecate this method in favor of withNotBeforeInstant + @Deprecated public Builder withNotBefore(Date notBefore) { return withNotBefore(notBefore.toInstant()); } @@ -191,8 +193,9 @@ public Builder withIssuedAt(Instant issuedAt) { * * @param issuedAt the Issued At value. * @return this same Builder instance. + * @deprecated Use {@linkplain #withIssuedAt(Instant)} instead. */ - // TODO - Deprecate this method in favor of withIssuedAtInstant + @Deprecated public Builder withIssuedAt(Date issuedAt) { return withIssuedAt(issuedAt.toInstant()); } @@ -299,8 +302,9 @@ public Builder withClaim(String name, Instant value) throws IllegalArgumentExcep * @param value the Claim's value. * @return this same Builder instance. * @throws IllegalArgumentException if the name is null. + * @deprecated Use {@linkplain #withClaim(String, Instant)} instead. */ - // TODO - Deprecate this method in favor of withClaim(String name, Instant value) + @Deprecated public Builder withClaim(String name, Date value) throws IllegalArgumentException { return withClaim(name, value.toInstant()); } @@ -312,7 +316,9 @@ public Builder withClaim(String name, Date value) throws IllegalArgumentExceptio * @param items the Claim's value. * @return this same Builder instance. * @throws IllegalArgumentException if the name is null. + * @deprecated Use {@linkplain #withClaim(String, List)} instead. */ + @Deprecated public Builder withArrayClaim(String name, String[] items) throws IllegalArgumentException { assertNonNull(name); addClaim(name, items); @@ -326,7 +332,9 @@ public Builder withArrayClaim(String name, String[] items) throws IllegalArgumen * @param items the Claim's value. * @return this same Builder instance. * @throws IllegalArgumentException if the name is null. + * @deprecated Use {@linkplain #withClaim(String, List)} instead. */ + @Deprecated public Builder withArrayClaim(String name, Integer[] items) throws IllegalArgumentException { assertNonNull(name); addClaim(name, items); @@ -340,7 +348,9 @@ public Builder withArrayClaim(String name, Integer[] items) throws IllegalArgume * @param items the Claim's value. * @return this same Builder instance. * @throws IllegalArgumentException if the name is null + * @deprecated Use {@linkplain #withClaim(String, List)} instead. */ + @Deprecated public Builder withArrayClaim(String name, Long[] items) throws IllegalArgumentException { assertNonNull(name); addClaim(name, items); diff --git a/lib/src/main/java/com/auth0/jwt/interfaces/Claim.java b/lib/src/main/java/com/auth0/jwt/interfaces/Claim.java index d6bd7372..577f4361 100644 --- a/lib/src/main/java/com/auth0/jwt/interfaces/Claim.java +++ b/lib/src/main/java/com/auth0/jwt/interfaces/Claim.java @@ -71,8 +71,9 @@ public interface Claim { * If the value can't be converted to a Date, null will be returned. * * @return the value as a Date or null. + * @deprecated Use {@linkplain #asInstant()} instead. */ - // TODO - Deprecate this method in favor of asInstant() + @Deprecated Date asDate(); /** diff --git a/lib/src/main/java/com/auth0/jwt/interfaces/Clock.java b/lib/src/main/java/com/auth0/jwt/interfaces/Clock.java index 21e6d3f8..5666d187 100644 --- a/lib/src/main/java/com/auth0/jwt/interfaces/Clock.java +++ b/lib/src/main/java/com/auth0/jwt/interfaces/Clock.java @@ -18,7 +18,8 @@ public interface Clock { * Returns a new Date representing Today's time. * @return a new Date representing Today's time. + * @deprecated Use {@linkplain #getNow()} instead */ - // TODO - Deprecate this method in favor of getNow() + @Deprecated Date getToday(); } diff --git a/lib/src/main/java/com/auth0/jwt/interfaces/Payload.java b/lib/src/main/java/com/auth0/jwt/interfaces/Payload.java index 9b2fddf1..bb46acc3 100644 --- a/lib/src/main/java/com/auth0/jwt/interfaces/Payload.java +++ b/lib/src/main/java/com/auth0/jwt/interfaces/Payload.java @@ -55,24 +55,27 @@ public interface Payload { * Get the value of the "exp" claim, or null if it's not available. * * @return the Expiration Time value or null. + * @deprecated Use {@linkplain #getExpiresAtInstant()} instead. */ - // TODO - deprecate in favor of getExpiresAtInstant + @Deprecated Date getExpiresAt(); /** * Get the value of the "nbf" claim, or null if it's not available. * * @return the Not Before value or null. + * @deprecated Use {@linkplain #getNotBeforeInstant()} instead. */ - // TODO - deprecate in favor of getNotBeforeInstant + @Deprecated Date getNotBefore(); /** * Get the value of the "iat" claim, or null if it's not available. * * @return the Issued At value or null. + * @deprecated Use {@linkplain #getIssuedAtInstant()} instead. */ - // TODO - deprecate in favor of getIssuedAtInstant + @Deprecated Date getIssuedAt(); /** diff --git a/lib/src/main/java/com/auth0/jwt/interfaces/Verification.java b/lib/src/main/java/com/auth0/jwt/interfaces/Verification.java index 024a33f3..9b416acb 100644 --- a/lib/src/main/java/com/auth0/jwt/interfaces/Verification.java +++ b/lib/src/main/java/com/auth0/jwt/interfaces/Verification.java @@ -148,8 +148,9 @@ public interface Verification { * @param value the Claim's value. * @return this same Verification instance. * @throws IllegalArgumentException if the name is null. + * @deprecated Use {@linkplain #withClaim(String, Instant)} instead. */ - // TODO deprecate this in favor of withClaim(String name, Instant value) + @Deprecated Verification withClaim(String name, Date value) throws IllegalArgumentException; /** From bc0adad13b8a827a84d911b1193b07f5e2fb8061 Mon Sep 17 00:00:00 2001 From: Jim Anderson Date: Wed, 11 Mar 2020 11:32:30 -0500 Subject: [PATCH 13/14] Correctly serialize dateTime claims in lists and maps (#406) --- .../com/auth0/jwt/impl/PayloadSerializer.java | 7 ++++++ .../java/com/auth0/jwt/JWTCreatorTest.java | 23 +++++++++---------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/lib/src/main/java/com/auth0/jwt/impl/PayloadSerializer.java b/lib/src/main/java/com/auth0/jwt/impl/PayloadSerializer.java index 69ada53b..cb84fedc 100644 --- a/lib/src/main/java/com/auth0/jwt/impl/PayloadSerializer.java +++ b/lib/src/main/java/com/auth0/jwt/impl/PayloadSerializer.java @@ -6,6 +6,7 @@ import java.io.IOException; import java.time.Instant; +import java.util.Date; import java.util.List; import java.util.Map; @@ -59,6 +60,8 @@ public void serialize(ClaimsHolder holder, JsonGenerator gen, SerializerProvider private void handleSerialization(Object value, JsonGenerator gen) throws IOException { if (value instanceof Instant) { // EXPIRES_AT, ISSUED_AT, NOT_BEFORE, custom Instant claims gen.writeNumber(instantToSeconds((Instant) value)); + } else if (value instanceof Date) { + gen.writeNumber(dateToSeconds((Date) value)); } else if (value instanceof Map) { serializeMap((Map) value, gen); } else if (value instanceof List) { @@ -89,4 +92,8 @@ private void serializeList(List list, JsonGenerator gen) throws IOException { private long instantToSeconds(Instant instant) { return instant.getEpochSecond(); } + + private long dateToSeconds(Date date) { + return date.getTime() / 1000; + } } diff --git a/lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java b/lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java index ee4ef4b8..4d046e92 100644 --- a/lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java +++ b/lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java @@ -2,6 +2,7 @@ import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.impl.PublicClaims; +import com.auth0.jwt.interfaces.DecodedJWT; import com.auth0.jwt.interfaces.ECDSAKeyProvider; import com.auth0.jwt.interfaces.RSAKeyProvider; import com.fasterxml.jackson.databind.ObjectMapper; @@ -507,7 +508,7 @@ public void shouldAcceptCustomMapClaimOfBasicObjectTypes() throws Exception { data.put("integer", 1); data.put("long", Long.MAX_VALUE); data.put("double", 123.456d); - data.put("date", new Date(123L)); + data.put("date", new Date(123000)); data.put("instant", Instant.ofEpochSecond(123L)); data.put("boolean", true); @@ -520,7 +521,7 @@ public void shouldAcceptCustomMapClaimOfBasicObjectTypes() throws Exception { Map sub = new HashMap<>(); sub.put("subKey", "subValue"); - sub.put("subDate", new Date(567L)); + sub.put("subDate", new Date(567000)); sub.put("subInstant", Instant.ofEpochSecond(567L)); data.put("map", sub); @@ -530,10 +531,9 @@ public void shouldAcceptCustomMapClaimOfBasicObjectTypes() throws Exception { assertThat(jwt, is(notNullValue())); String[] parts = jwt.split("\\."); - - String body = new String(Base64.getUrlDecoder().decode(parts[1]), StandardCharsets.UTF_8); - ObjectMapper mapper = new ObjectMapper(); - Map map = (Map) mapper.readValue(body, Map.class).get("data"); + + DecodedJWT decodedJWT = JWT.decode(jwt); + Map map = decodedJWT.getClaim("data").asMap(); assertThat(map.get("string"), is("abc")); assertThat(map.get("integer"), is(1)); @@ -568,7 +568,7 @@ public void shouldAcceptCustomListClaimOfBasicObjectTypes() throws Exception { data.add(1); data.add(Long.MAX_VALUE); data.add(123.456d); - data.add(new Date(123L)); + data.add(new Date(123000)); data.add(Instant.ofEpochSecond(123L)); data.add(true); @@ -581,7 +581,7 @@ public void shouldAcceptCustomListClaimOfBasicObjectTypes() throws Exception { Map sub = new HashMap<>(); sub.put("subKey", "subValue"); - sub.put("subDate", new Date(567L)); + sub.put("subDate", new Date(567000)); sub.put("subInstant", Instant.ofEpochSecond(567L)); data.add(sub); @@ -592,10 +592,9 @@ public void shouldAcceptCustomListClaimOfBasicObjectTypes() throws Exception { assertThat(jwt, is(notNullValue())); String[] parts = jwt.split("\\."); - - String body = new String(Base64.getUrlDecoder().decode(parts[1]), StandardCharsets.UTF_8); - ObjectMapper mapper = new ObjectMapper(); - List list = (List) mapper.readValue(body, Map.class).get("data"); + + DecodedJWT decodedJWT = JWT.decode(jwt); + List list = decodedJWT.getClaim("data").asList(Object.class); assertThat(list.get(0), is("abc")); assertThat(list.get(1), is(1)); From 8e0fa7818084a7ad968ff14e20f5da0dbd3e8ede Mon Sep 17 00:00:00 2001 From: CodeDead Date: Mon, 30 Mar 2020 20:30:56 +0200 Subject: [PATCH 14/14] Feature/readme (#397) * Markdown formatting, grammatical changes * Added a comma * Grammatical changes * Changed to lowercase * Mark classes Co-authored-by: Damien Guard --- README.md | 210 ++++++++++++++++++++++++++---------------------------- 1 file changed, 101 insertions(+), 109 deletions(-) diff --git a/README.md b/README.md index f9dd39f3..89c7fab6 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,3 @@ - - # Java JWT [![CircleCI](https://img.shields.io/circleci/project/github/auth0/java-jwt.svg?style=flat-square)](https://circleci.com/gh/auth0/java-jwt/tree/master) @@ -9,15 +7,15 @@ A Java implementation of [JSON Web Token (JWT) - RFC 7519](https://tools.ietf.org/html/rfc7519). -If you're looking for an **Android** version of the JWT Decoder take a look at our [JWTDecode.Android](https://github.com/auth0/JWTDecode.Android) library. +If you're looking for the **Android** version of the JWT Decoder, take a look at our [JWTDecode.Android](https://github.com/auth0/JWTDecode.Android) library. ## Installation -The library is available on both Maven Central and Bintray, and the Javadoc is published [here](https://javadoc.io/doc/com.auth0/java-jwt/latest/index.html). +The library is available on both [Maven Central](https://mvnrepository.com/artifact/com.auth0/java-jwt) and [Bintray](https://bintray.com/bintray/jcenter/com.auth0%3Ajava-jwt). The Javadoc is published [here](https://javadoc.io/doc/com.auth0/java-jwt/latest/index.html). ### Maven -```xml +```XML com.auth0 java-jwt @@ -27,13 +25,13 @@ The library is available on both Maven Central and Bintray, and the Javadoc is p ### Gradle -```gradle +```Gradle implementation 'com.auth0:java-jwt:3.9.0' ``` ## Available Algorithms -The library implements JWT Verification and Signing using the following algorithms: +The library implements *JWT Verification* and *Signing* using the following algorithms: | JWS | Algorithm | Description | | :-------------: | :-------------: | :----- | @@ -51,20 +49,19 @@ The library implements JWT Verification and Signing using the following algorith ### Pick the Algorithm -The Algorithm defines how a token is signed and verified. It can be instantiated with the raw value of the secret in the case of HMAC algorithms, or the key pairs or `KeyProvider` in the case of RSA and ECDSA algorithms. Once created, the instance is reusable for token signing and verification operations. - -When using RSA or ECDSA algorithms and you just need to **sign** JWTs you can avoid specifying a Public Key by passing a `null` value. The same can be done with the Private Key when you just need to **verify** JWTs. +The *Algorithm* defines how a token is signed and verified. It can be instantiated with the raw value of the secret in the case of HMAC algorithms, or the key pairs or `KeyProvider` in the case of RSA and ECDSA algorithms. Once created, the instance is reusable for token signing and verification operations. +When using RSA or ECDSA algorithms and you just need to **sign** JWTs you can avoid specifying a public key by passing a `null` value. The same can be done with the Private Key when you just need to **verify** JWTs. #### Using static secrets or keys: -```java -//HMAC +```Java +// HMAC Algorithm algorithmHS = Algorithm.HMAC256("secret"); -//RSA -RSAPublicKey publicKey = //Get the key instance -RSAPrivateKey privateKey = //Get the key instance +// RSA +RSAPublicKey publicKey = // Get the key instance +RSAPrivateKey privateKey = // Get the key instance Algorithm algorithmRS = Algorithm.RSA256(publicKey, privateKey); ``` @@ -72,24 +69,23 @@ Algorithm algorithmRS = Algorithm.RSA256(publicKey, privateKey); #### Using a KeyProvider: -By using a `KeyProvider` you can change in runtime the key used either to verify the token signature or to sign a new token for RSA or ECDSA algorithms. This is achieved by implementing either `RSAKeyProvider` or `ECDSAKeyProvider` methods: +By using a `KeyProvider` you can, in runtime, change the key used either to verify the token signature or to sign a new token for RSA or ECDSA algorithms. This is achieved by implementing either `RSAKeyProvider` or `ECDSAKeyProvider` methods: -- `getPublicKeyById(String kid)`: Its called during token signature verification and it should return the key used to verify the token. If key rotation is being used, e.g. [JWK](https://tools.ietf.org/html/rfc7517) it can fetch the correct rotation key using the id. (Or just return the same key all the time). +- `getPublicKeyById(String kid)`: Its called during token signature verification and it should return the key used to verify the token. If key rotation is being used, e.g. [JWK](https://tools.ietf.org/html/rfc7517), it can fetch the correct rotation key using the id (or just return the same key all the time). - `getPrivateKey()`: Its called during token signing and it should return the key that will be used to sign the JWT. -- `getPrivateKeyId()`: Its called during token signing and it should return the id of the key that identifies the one returned by `getPrivateKey()`. This value is preferred over the one set in the `JWTCreator.Builder#withKeyId(String)` method. If you don't need to set a `kid` value avoid instantiating an Algorithm using a `KeyProvider`. - +- `getPrivateKeyId()`: Its called during token signing and it should return the id of the key that identifies the one returned by `getPrivateKey()`. This value is preferred over the one set in the `JWTCreator.Builder#withKeyId(String)` method. If you don't need to set a `kid` value, avoid instantiating an Algorithm using a `KeyProvider`. The following example shows how this would work with `JwkStore`, an imaginary [JWK Set](https://auth0.com/docs/jwks) implementation. For simple key rotation using JWKS, try the [jwks-rsa-java](https://github.com/auth0/jwks-rsa-java) library. -```java +```Java final JwkStore jwkStore = new JwkStore("{JWKS_FILE_HOST}"); -final RSAPrivateKey privateKey = //Get the key instance -final String privateKeyId = //Create an Id for the above key +final RSAPrivateKey privateKey = // Get the key instance +final String privateKeyId = // Create an Id for the above key RSAKeyProvider keyProvider = new RSAKeyProvider() { @Override public RSAPublicKey getPublicKeyById(String kid) { - //Received 'kid' value might be null if it wasn't defined in the Token's header + // Received 'kid' value might be null if it wasn't defined in the token's header RSAPublicKey publicKey = jwkStore.get(kid); return (RSAPublicKey) publicKey; } @@ -106,69 +102,68 @@ RSAKeyProvider keyProvider = new RSAKeyProvider() { }; Algorithm algorithm = Algorithm.RSA256(keyProvider); -//Use the Algorithm to create and verify JWTs. +// Use the Algorithm to create and verify JWTs. ``` ### Create and Sign a Token -You'll first need to create a `JWTCreator` instance by calling `JWT.create()`. Use the builder to define the custom Claims your token needs to have. Finally to get the String token call `sign()` and pass the `Algorithm` instance. +You'll first need to create a `JWTCreator` instance by calling `JWT.create()`. Use the builder to define the custom claims your token needs to have. Finally to get the `String` token call `sign()` and pass the `Algorithm` instance. * Example using `HS256` -```java +```Java try { Algorithm algorithm = Algorithm.HMAC256("secret"); String token = JWT.create() .withIssuer("auth0") .sign(algorithm); } catch (JWTCreationException exception){ - //Invalid Signing configuration / Couldn't convert Claims. + // Invalid Signing configuration / Couldn't convert Claims. } ``` * Example using `RS256` -```java -RSAPublicKey publicKey = //Get the key instance -RSAPrivateKey privateKey = //Get the key instance +```Java +RSAPublicKey publicKey = // Get the key instance +RSAPrivateKey privateKey = // Get the key instance try { Algorithm algorithm = Algorithm.RSA256(publicKey, privateKey); String token = JWT.create() .withIssuer("auth0") .sign(algorithm); } catch (JWTCreationException exception){ - //Invalid Signing configuration / Couldn't convert Claims. + // Invalid Signing configuration / Couldn't convert Claims. } ``` -If a Claim couldn't be converted to JSON or the Key used in the signing process was invalid a `JWTCreationException` will raise. - +If a `Claim` couldn't be converted to JSON or the key used in the signing process was invalid, a `JWTCreationException` will be thrown. ### Verify a Token -You'll first need to create a `JWTVerifier` instance by calling `JWT.require()` and passing the `Algorithm` instance. If you require the token to have specific Claim values, use the builder to define them. The instance returned by the method `build()` is reusable, so you can define it once and use it to verify different tokens. Finally call `verifier.verify()` passing the token. +You'll first need to create a `JWTVerifier` instance by calling `JWT.require()` and passing the `Algorithm` instance. If you require the token to have specific `Claim` values, use the builder to define them. The instance returned by the method `build()` is reusable, so you can define it once and use it to verify different tokens. Finally call `verifier.verify()` passing the token. * Example using `HS256` -```java +```Java String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXUyJ9.eyJpc3MiOiJhdXRoMCJ9.AbIJTDMFc7yUa5MhvcP03nJPyCPzZtQcGEp-zWfOkEE"; try { Algorithm algorithm = Algorithm.HMAC256("secret"); JWTVerifier verifier = JWT.require(algorithm) .withIssuer("auth0") - .build(); //Reusable verifier instance + .build(); // Reusable verifier instance DecodedJWT jwt = verifier.verify(token); } catch (JWTVerificationException exception){ - //Invalid signature/claims + // Invalid signature/claims } ``` * Example using `RS256` -```java +```Java String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXUyJ9.eyJpc3MiOiJhdXRoMCJ9.AbIJTDMFc7yUa5MhvcP03nJPyCPzZtQcGEp-zWfOkEE"; -RSAPublicKey publicKey = //Get the key instance -RSAPrivateKey privateKey = //Get the key instance +RSAPublicKey publicKey = // Get the key instance +RSAPrivateKey privateKey = // Get the key instance try { Algorithm algorithm = Algorithm.RSA256(publicKey, privateKey); JWTVerifier verifier = JWT.require(algorithm) @@ -180,38 +175,37 @@ try { } ``` -If the token has an invalid signature or the Claim requirement is not met, a `JWTVerificationException` will raise. - +If the token has an invalid signature or the `Claim` requirement is not met, a `JWTVerificationException` will be thrown. #### Time Validation -The JWT token may include DateNumber fields that can be used to validate that: -* The token was issued in a past date `"iat" < TODAY` -* The token hasn't expired yet `"exp" > TODAY` and -* The token can already be used. `"nbf" < TODAY` +The JWT token may include *DateNumber* fields that can be used to validate that: +* The token was issued in the past (`"iat" < TODAY`) +* The token hasn't expired yet (`"exp" > TODAY`) and +* The token can already be used (`"nbf" < TODAY`) -When verifying a token the time validation occurs automatically, resulting in a `JWTVerificationException` being throw when the values are invalid. If any of the previous fields are missing they won't be considered in this validation. +When verifying a token the time validation occurs automatically, resulting in a `JWTVerificationException` being thrown when the values are invalid. If any of the previous fields are missing they won't be considered in this validation. -To specify a **leeway window** in which the Token should still be considered valid, use the `acceptLeeway()` method in the `JWTVerifier` builder and pass a positive seconds value. This applies to every item listed above. +To specify a **leeway window** in which the token should still be considered valid, use the `acceptLeeway()` method in the `JWTVerifier` builder and pass a positive seconds value. This applies to every item listed above. -```java +```Java JWTVerifier verifier = JWT.require(algorithm) .acceptLeeway(1) // 1 sec for nbf, iat and exp .build(); ``` -You can also specify a custom value for a given DateNumber claim and override the default one for only that claim. +You can also specify a custom value for a given `DateNumber` claim and override the default one for only that claim. -```java +```Java JWTVerifier verifier = JWT.require(algorithm) - .acceptLeeway(1) //1 sec for nbf and iat - .acceptExpiresAt(5) //5 secs for exp + .acceptLeeway(1) // 1 sec for nbf and iat + .acceptExpiresAt(5) // 5 secs for exp .build(); ``` If you need to test this behaviour in your lib/app cast the `Verification` instance to a `BaseVerification` to gain visibility of the `verification.build()` method that accepts a custom `Clock`. e.g.: -```java +```Java BaseVerification verification = (BaseVerification) JWT.require(algorithm) .acceptLeeway(1) .acceptExpiresAt(5); @@ -221,63 +215,62 @@ JWTVerifier verifier = verification.build(clock); ### Decode a Token -```java +```Java String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXUyJ9.eyJpc3MiOiJhdXRoMCJ9.AbIJTDMFc7yUa5MhvcP03nJPyCPzZtQcGEp-zWfOkEE"; try { DecodedJWT jwt = JWT.decode(token); } catch (JWTDecodeException exception){ - //Invalid token + // Invalid token } ``` -If the token has an invalid syntax or the header or payload are not JSONs, a `JWTDecodeException` will raise. - +If the token has an invalid syntax or the `Header` or `Payload are invalid JSON objects, a `JWTDecodeException` will be thrown. ### Header Claims #### Algorithm ("alg") -Returns the Algorithm value or null if it's not defined in the Header. +Returns the `Algorithm` value or `null` if it's not defined in the `Header`. -```java +```Java String algorithm = jwt.getAlgorithm(); ``` #### Type ("typ") -Returns the Type value or null if it's not defined in the Header. +Returns the *Type* value or `null` if it's not defined in the `Header`. -```java +```Java String type = jwt.getType(); ``` #### Content Type ("cty") -Returns the Content Type value or null if it's not defined in the Header. +Returns the *Content Type* value or `null` if it's not defined in the `Header`. -```java +```Java String contentType = jwt.getContentType(); ``` #### Key Id ("kid") -Returns the Key Id value or null if it's not defined in the Header. +Returns the *Key Id* value or `null` if it's not defined in the `Header`. -```java +```Java String keyId = jwt.getKeyId(); ``` #### Private Claims -Additional Claims defined in the token's Header can be obtained by calling `getHeaderClaim()` and passing the Claim name. A Claim will always be returned, even if it can't be found. You can check if a Claim's value is null by calling `claim.isNull()`. +Additional claims defined in the token's `Header` can be obtained by calling `getHeaderClaim()` and passing the `Claim` name. A `Claim` will always be returned, even if it can't be found. You can check if a `Claim`'s value is `null` by calling `claim.isNull()`. -```java +```Java Claim claim = jwt.getHeaderClaim("owner"); ``` -When creating a Token with the `JWT.create()` you can specify header Claims by calling `withHeader()` and passing both the map of claims. +When creating a token with the `JWT.create()` you can specify header claims by calling `withHeader()` and passing both the map of claims. -```java +```Java Map headerClaims = new HashMap(); headerClaims.put("owner", "auth0"); String token = JWT.create() @@ -285,14 +278,13 @@ String token = JWT.create() .sign(algorithm); ``` -> The `alg` and `typ` values will always be included in the Header after the signing process. - +> The `alg` and `typ` values will always be included in the `Header` after the signing process. ### Payload Claims #### Issuer ("iss") -Returns the Issuer value or null if it's not defined in the Payload. +Returns the *Issuer* value or `null` if it's not defined in the `Payload`. ```java String issuer = jwt.getIssuer(); @@ -300,7 +292,7 @@ String issuer = jwt.getIssuer(); #### Subject ("sub") -Returns the Subject value or null if it's not defined in the Payload. +Returns the *Subject* value or `null` if it's not defined in the `Payload`. ```java String subject = jwt.getSubject(); @@ -308,7 +300,7 @@ String subject = jwt.getSubject(); #### Audience ("aud") -Returns the Audience value or null if it's not defined in the Payload. +Returns the *Audience* value or `null` if it's not defined in the `Payload`. ```java List audience = jwt.getAudience(); @@ -316,63 +308,63 @@ List audience = jwt.getAudience(); #### Expiration Time ("exp") -Returns the Expiration Time value or null if it's not defined in the Payload. +Returns the *Expiration Time* value or `null` if it's not defined in the `Payload`. -```java +```Java Instant expiresAt = jwt.getExpiresAt(); ``` #### Not Before ("nbf") -Returns the Not Before value or null if it's not defined in the Payload. +Returns the *Not Before* value or `null` if it's not defined in the `Payload`. -```java +```Java Instant notBefore = jwt.getNotBefore(); ``` #### Issued At ("iat") -Returns the Issued At value or null if it's not defined in the Payload. +Returns the *Issued At* value or `null` if it's not defined in the `Payload`. -```java +```Java Instant issuedAt = jwt.getIssuedAt(); ``` #### JWT ID ("jti") -Returns the JWT ID value or null if it's not defined in the Payload. +Returns the *JWT ID* value or `null` if it's not defined in the `Payload`. -```java +```Java String id = jwt.getId(); ``` #### Private Claims -Additional Claims defined in the token's Payload can be obtained by calling `getClaims()` or `getClaim()` and passing the Claim name. A Claim will always be returned, even if it can't be found. You can check if a Claim's value is null by calling `claim.isNull()`. +Additional claims defined in the token's `Payload` can be obtained by calling `getClaims()` or `getClaim()` and passing the `Claim` name. A `Claim` will always be returned, even if it can't be found. You can check if a `Claim`'s value is `null` by calling `claim.isNull()`. -```java -Map claims = jwt.getClaims(); //Key is the Claim name +```Java +Map claims = jwt.getClaims(); // Key is the Claim name Claim claim = claims.get("isAdmin"); ``` -or +Alternatively: -```java +```Java Claim claim = jwt.getClaim("isAdmin"); ``` -When creating a Token with the `JWT.create()` you can specify a custom Claim by calling `withClaim()` and passing both the name and the value. +When creating a token with the `JWT.create()` you can specify a custom `Claim` by calling `withClaim()` and passing both the name and the value. -```java +```Java String token = JWT.create() .withClaim("name", 123) .withArrayClaim("array", new Integer[]{1, 2, 3}) .sign(algorithm); ``` -You can also verify custom Claims on the `JWT.require()` by calling `withClaim()` and passing both the name and the required value. +You can also verify custom claims on the `JWT.require()` by calling `withClaim()` and passing both the name and the required value. -```java +```Java JWTVerifier verifier = JWT.require(algorithm) .withClaim("name", 123) .withArrayClaim("array", 1, 2, 3) @@ -380,31 +372,31 @@ JWTVerifier verifier = JWT.require(algorithm) DecodedJWT jwt = verifier.verify("my.jwt.token"); ``` -> Currently supported classes for custom JWT Claim creation and verification are: Boolean, Integer, Double, String, Instant (for NumericDate) and Arrays of type String and Integer. - +> Currently supported classes for custom JWT Claim creation and verification are: `Boolean`, `Integer`, `Double`, `String`, `Instant` (for `NumericDate`) and `Arrays` of type `String` and `Integer`. ### Claim Class -The Claim class is a wrapper for the Claim values. It allows you to get the Claim as different class types. The available helpers are: + +The `Claim` class is a wrapper for the `Claim` values. It allows you to get the `Claim` as different class types. The available helpers are: #### Primitives -* **asBoolean()**: Returns the Boolean value or null if it can't be converted. -* **asInt()**: Returns the Integer value or null if it can't be converted. -* **asDouble()**: Returns the Double value or null if it can't be converted. -* **asLong()**: Returns the Long value or null if it can't be converted. -* **asString()**: Returns the String value or null if it can't be converted. -* **asInstant()**: Returns the Instant value or null if it can't be converted. This must be a NumericDate (Unix Epoch/Timestamp). Note that the [JWT Standard](https://tools.ietf.org/html/rfc7519#section-2) specified that all the *NumericDate* values must be in seconds. + +* **asBoolean()**: Returns the `Boolean` value or `null` if it can't be converted. +* **asInt()**: Returns the `Integer` value or `null` if it can't be converted. +* **asDouble()**: Returns the `Double` value or `null` if it can't be converted. +* **asLong()**: Returns the `Long` value or `null` if it can't be converted. +* **asString()**: Returns the `String` value or `null` if it can't be converted. +* **asInstant()**: Returns the `Instant` value or `null` if it can't be converted. This must be a `NumericDate` (Unix Epoch/Timestamp). Note that the [JWT Standard](https://tools.ietf.org/html/rfc7519#section-2) specified that all the *NumericDate* values must be in seconds. #### Custom Classes and Collections -To obtain a Claim as a Collection you'll need to provide the **Class Type** of the contents to convert from. + +To obtain a `Claim` as a `Collection` you'll need to provide the **Class Type** of the contents to convert from. * **as(class)**: Returns the value parsed as **Class Type**. For collections you should use the `asArray` and `asList` methods. * **asMap()**: Returns the value parsed as **Map**. -* **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. -* **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. - -If the values can't be converted to the given **Class Type** a `JWTDecodeException` will raise. - +* **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. +* **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. +If the values can't be converted to the given **Class Type** a `JWTDecodeException` will be thrown. ## What is Auth0? @@ -415,11 +407,11 @@ Auth0 helps you to: * Add support for **[linking different user accounts](https://docs.auth0.com/link-accounts)** with the same user. * Support for generating signed [Json Web Tokens](https://docs.auth0.com/jwt) to call your APIs and **flow the user identity** securely. * Analytics of how, when and where users are logging in. -* Pull data from other sources and add it to the user profile, through [JavaScript rules](https://docs.auth0.com/rules). +* Pull data from other sources and add it to the user profile through [JavaScript rules](https://docs.auth0.com/rules). ## Create a free account in Auth0 -1. Go to [Auth0](https://auth0.com) and click Sign Up. +1. Go to [Auth0](https://auth0.com) and click *Sign Up*. 2. Use Google, GitHub or Microsoft Account to login. ## Issue Reporting