From f3631c8346558e348acf1f290ba5d7241f5e6e2a Mon Sep 17 00:00:00 2001 From: Johan Geerts Date: Fri, 5 Oct 2018 10:49:50 +0200 Subject: [PATCH 1/5] Support multiple issuers #246 --- .../main/java/com/auth0/jwt/JWTVerifier.java | 15 ++++++--- .../auth0/jwt/interfaces/Verification.java | 2 +- .../java/com/auth0/jwt/JWTVerifierTest.java | 32 ++++++++++++++++--- 3 files changed, 40 insertions(+), 9 deletions(-) diff --git a/lib/src/main/java/com/auth0/jwt/JWTVerifier.java b/lib/src/main/java/com/auth0/jwt/JWTVerifier.java index 6f513c7e..ae5d2ecd 100644 --- a/lib/src/main/java/com/auth0/jwt/JWTVerifier.java +++ b/lib/src/main/java/com/auth0/jwt/JWTVerifier.java @@ -61,8 +61,8 @@ public static class BaseVerification implements Verification { * @return this same Verification instance. */ @Override - public Verification withIssuer(String issuer) { - requireClaim(PublicClaims.ISSUER, issuer); + public Verification withIssuer(String... issuer) { + requireClaim(PublicClaims.ISSUER, Arrays.asList(issuer)); return this; } @@ -379,7 +379,8 @@ private void verifyClaims(DecodedJWT jwt, Map claims) throws Tok assertValidDateClaim(jwt.getNotBefore(), (Long) entry.getValue(), false); break; case PublicClaims.ISSUER: - assertValidStringClaim(entry.getKey(), jwt.getIssuer(), (String) entry.getValue()); + //noinspection unchecked + assertValidIssuerClaim(jwt.getIssuer(), (List) entry.getValue()); break; case PublicClaims.JWT_ID: assertValidStringClaim(entry.getKey(), jwt.getId(), (String) entry.getValue()); @@ -451,7 +452,13 @@ private void assertDateIsPast(Date date, long leeway, Date today) { private void assertValidAudienceClaim(List audience, List value) { if (audience == null || !audience.containsAll(value)) { - throw new InvalidClaimException("The Claim 'aud' value doesn't contain the required audience."); + throw new InvalidClaimException(String.format("The Claim '%s' value doesn't contain the required audience.", PublicClaims.AUDIENCE)); + } + } + + private void assertValidIssuerClaim(String issuer, List value) { + if (issuer == null || !value.contains(issuer)) { + throw new InvalidClaimException(String.format("The Claim '%s' value doesn't match the required issuer.", PublicClaims.ISSUER)); } } } 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 465ebe5d..d24aba66 100644 --- a/lib/src/main/java/com/auth0/jwt/interfaces/Verification.java +++ b/lib/src/main/java/com/auth0/jwt/interfaces/Verification.java @@ -5,7 +5,7 @@ import java.util.Date; public interface Verification { - Verification withIssuer(String issuer); + Verification withIssuer(String... issuer); Verification withSubject(String subject); diff --git a/lib/src/test/java/com/auth0/jwt/JWTVerifierTest.java b/lib/src/test/java/com/auth0/jwt/JWTVerifierTest.java index d5826c24..a62b7095 100644 --- a/lib/src/test/java/com/auth0/jwt/JWTVerifierTest.java +++ b/lib/src/test/java/com/auth0/jwt/JWTVerifierTest.java @@ -51,10 +51,21 @@ public void shouldValidateIssuer() throws Exception { assertThat(jwt, is(notNullValue())); } + @Test + public void shouldValidateMultipleIssuers() { + String token = "eyJhbGciOiJIUzI1NiIsImN0eSI6IkpXVCJ9.eyJpc3MiOiJhdXRoMCJ9.mZ0m_N1J4PgeqWmi903JuUoDRZDBPB7HwkS4nVyWH1M"; + DecodedJWT jwt = JWTVerifier.init(Algorithm.HMAC256("secret")) + .withIssuer("otherIssuer", "auth0") + .build() + .verify(token); + + assertThat(jwt, is(notNullValue())); + } + @Test public void shouldThrowOnInvalidIssuer() throws Exception { exception.expect(InvalidClaimException.class); - exception.expectMessage("The Claim 'iss' value doesn't match the required one."); + exception.expectMessage("The Claim 'iss' value doesn't match the required issuer."); String token = "eyJhbGciOiJIUzI1NiIsImN0eSI6IkpXVCJ9.eyJpc3MiOiJhdXRoMCJ9.mZ0m_N1J4PgeqWmi903JuUoDRZDBPB7HwkS4nVyWH1M"; JWTVerifier.init(Algorithm.HMAC256("secret")) .withIssuer("invalid") @@ -557,12 +568,25 @@ public void shouldThrowOnInvalidJWTId() throws Exception { public void shouldRemoveClaimWhenPassingNull() throws Exception { Algorithm algorithm = mock(Algorithm.class); JWTVerifier verifier = JWTVerifier.init(algorithm) - .withIssuer("iss") - .withIssuer(null) + .withSubject("1234567890") + .withSubject(null) .build(); assertThat(verifier.claims, is(notNullValue())); - assertThat(verifier.claims, not(hasKey("iss"))); + assertThat(verifier.claims, not(hasKey("sub"))); + } + + @Test + public void shouldThrowWhenNoIssuerPresent() { + exception.expect(InvalidClaimException.class); + exception.expectMessage("The Claim 'iss' value doesn't match the required issuer."); + + String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.Rq8IxqeX7eA6GgYxlcHdPFVRNFFZc5rEI3MQTZZbK3I"; + + JWTVerifier.init(Algorithm.HMAC256("secret")) + .withIssuer("oauth") + .build() + .verify(token); } @Test From 54697e08f548d67abd1c357b98341c14df7432ec Mon Sep 17 00:00:00 2001 From: Johan Geerts Date: Wed, 23 Jan 2019 13:05:17 +0100 Subject: [PATCH 2/5] Implemented comments after review --- lib/src/main/java/com/auth0/jwt/JWTVerifier.java | 6 +++--- lib/src/test/java/com/auth0/jwt/JWTVerifierTest.java | 11 ++++++----- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/src/main/java/com/auth0/jwt/JWTVerifier.java b/lib/src/main/java/com/auth0/jwt/JWTVerifier.java index 6cc2b5cc..00ce957c 100644 --- a/lib/src/main/java/com/auth0/jwt/JWTVerifier.java +++ b/lib/src/main/java/com/auth0/jwt/JWTVerifier.java @@ -58,7 +58,7 @@ public static class BaseVerification implements Verification { /** * Require a specific Issuer ("iss") claim. * - * @param issuer the required Issuer value + * @param issuer the required Issuer value. If multiple values are given, the claim must at least match one of them * @return this same Verification instance. */ @Override @@ -481,13 +481,13 @@ private void assertDateIsPast(Date date, long leeway, Date today) { private void assertValidAudienceClaim(List audience, List value) { if (audience == null || !audience.containsAll(value)) { - throw new InvalidClaimException(String.format("The Claim '%s' value doesn't contain the required audience.", PublicClaims.AUDIENCE)); + throw new InvalidClaimException("The Claim 'aud' value doesn't contain the required audience."); } } private void assertValidIssuerClaim(String issuer, List value) { if (issuer == null || !value.contains(issuer)) { - throw new InvalidClaimException(String.format("The Claim '%s' value doesn't match the required issuer.", PublicClaims.ISSUER)); + throw new InvalidClaimException("The Claim 'iss' value doesn't match the required issuer."); } } } diff --git a/lib/src/test/java/com/auth0/jwt/JWTVerifierTest.java b/lib/src/test/java/com/auth0/jwt/JWTVerifierTest.java index 677bb21d..c2c19cc1 100644 --- a/lib/src/test/java/com/auth0/jwt/JWTVerifierTest.java +++ b/lib/src/test/java/com/auth0/jwt/JWTVerifierTest.java @@ -53,13 +53,14 @@ public void shouldValidateIssuer() throws Exception { @Test public void shouldValidateMultipleIssuers() { - String token = "eyJhbGciOiJIUzI1NiIsImN0eSI6IkpXVCJ9.eyJpc3MiOiJhdXRoMCJ9.mZ0m_N1J4PgeqWmi903JuUoDRZDBPB7HwkS4nVyWH1M"; - DecodedJWT jwt = JWTVerifier.init(Algorithm.HMAC256("secret")) + String auth0Token = "eyJhbGciOiJIUzI1NiIsImN0eSI6IkpXVCJ9.eyJpc3MiOiJhdXRoMCJ9.mZ0m_N1J4PgeqWmi903JuUoDRZDBPB7HwkS4nVyWH1M"; + String otherIssuertoken = "eyJhbGciOiJIUzI1NiIsImN0eSI6IkpXVCJ9.eyJpc3MiOiJvdGhlcklzc3VlciJ9.k4BCOJJl-c0_Y-49VD_mtt-u0QABKSV5i3W-RKc74co"; + JWTVerifier verifier = JWTVerifier.init(Algorithm.HMAC256("secret")) .withIssuer("otherIssuer", "auth0") - .build() - .verify(token); + .build(); - assertThat(jwt, is(notNullValue())); + assertThat(verifier.verify(auth0Token), is(notNullValue())); + assertThat(verifier.verify(otherIssuertoken), is(notNullValue())); } @Test From b8bb5604d2e861417904ab46b7d42436e7d73b29 Mon Sep 17 00:00:00 2001 From: Johan Geerts Date: Thu, 31 Jan 2019 14:26:07 +0100 Subject: [PATCH 3/5] Implemented comments after review, rolled back remove claim unit test --- lib/src/main/java/com/auth0/jwt/JWTVerifier.java | 10 ++++------ lib/src/test/java/com/auth0/jwt/JWTVerifierTest.java | 4 ++-- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/lib/src/main/java/com/auth0/jwt/JWTVerifier.java b/lib/src/main/java/com/auth0/jwt/JWTVerifier.java index 3efc358e..73e52933 100644 --- a/lib/src/main/java/com/auth0/jwt/JWTVerifier.java +++ b/lib/src/main/java/com/auth0/jwt/JWTVerifier.java @@ -66,7 +66,7 @@ public static class BaseVerification implements Verification { */ @Override public Verification withIssuer(String... issuer) { - requireClaim(PublicClaims.ISSUER, Arrays.asList(issuer)); + requireClaim(PublicClaims.ISSUER, issuer); return this; } @@ -90,7 +90,7 @@ public Verification withSubject(String subject) { */ @Override public Verification withAudience(String... audience) { - requireClaim(PublicClaims.AUDIENCE, Arrays.asList(audience)); + requireClaim(PublicClaims.AUDIENCE, audience); return this; } @@ -398,8 +398,7 @@ private void verifyClaims(DecodedJWT jwt, Map claims) throws Tok for (Map.Entry entry : claims.entrySet()) { switch (entry.getKey()) { case PublicClaims.AUDIENCE: - //noinspection unchecked - assertValidAudienceClaim(jwt.getAudience(), (List) entry.getValue()); + assertValidAudienceClaim(jwt.getAudience(), Arrays.asList((String[]) entry.getValue())); break; case PublicClaims.EXPIRES_AT: assertValidDateClaim(jwt.getExpiresAt(), (Long) entry.getValue(), true); @@ -411,8 +410,7 @@ private void verifyClaims(DecodedJWT jwt, Map claims) throws Tok assertValidDateClaim(jwt.getNotBefore(), (Long) entry.getValue(), false); break; case PublicClaims.ISSUER: - //noinspection unchecked - assertValidIssuerClaim(jwt.getIssuer(), (List) entry.getValue()); + assertValidIssuerClaim(jwt.getIssuer(), Arrays.asList((String[]) entry.getValue())); break; case PublicClaims.JWT_ID: assertValidStringClaim(entry.getKey(), jwt.getId(), (String) entry.getValue()); diff --git a/lib/src/test/java/com/auth0/jwt/JWTVerifierTest.java b/lib/src/test/java/com/auth0/jwt/JWTVerifierTest.java index c2c19cc1..bebbedd4 100644 --- a/lib/src/test/java/com/auth0/jwt/JWTVerifierTest.java +++ b/lib/src/test/java/com/auth0/jwt/JWTVerifierTest.java @@ -594,8 +594,8 @@ public void shouldThrowOnInvalidJWTId() throws Exception { public void shouldRemoveClaimWhenPassingNull() throws Exception { Algorithm algorithm = mock(Algorithm.class); JWTVerifier verifier = JWTVerifier.init(algorithm) - .withSubject("1234567890") - .withSubject(null) + .withIssuer("iss") + .withIssuer(null) .build(); assertThat(verifier.claims, is(notNullValue())); From c77b7e7b48022fa011608529410e613970a9ab70 Mon Sep 17 00:00:00 2001 From: Johan Geerts Date: Mon, 18 Feb 2019 10:20:27 +0100 Subject: [PATCH 4/5] Implemented comments after review --- lib/src/main/java/com/auth0/jwt/JWTVerifier.java | 8 ++++---- .../test/java/com/auth0/jwt/JWTVerifierTest.java | 15 +-------------- 2 files changed, 5 insertions(+), 18 deletions(-) diff --git a/lib/src/main/java/com/auth0/jwt/JWTVerifier.java b/lib/src/main/java/com/auth0/jwt/JWTVerifier.java index 73e52933..a1c7f82b 100644 --- a/lib/src/main/java/com/auth0/jwt/JWTVerifier.java +++ b/lib/src/main/java/com/auth0/jwt/JWTVerifier.java @@ -66,7 +66,7 @@ public static class BaseVerification implements Verification { */ @Override public Verification withIssuer(String... issuer) { - requireClaim(PublicClaims.ISSUER, issuer); + requireClaim(PublicClaims.ISSUER, issuer == null ? null : Arrays.asList(issuer)); return this; } @@ -90,7 +90,7 @@ public Verification withSubject(String subject) { */ @Override public Verification withAudience(String... audience) { - requireClaim(PublicClaims.AUDIENCE, audience); + requireClaim(PublicClaims.AUDIENCE, audience == null ? null : Arrays.asList(audience)); return this; } @@ -398,7 +398,7 @@ private void verifyClaims(DecodedJWT jwt, Map claims) throws Tok for (Map.Entry entry : claims.entrySet()) { switch (entry.getKey()) { case PublicClaims.AUDIENCE: - assertValidAudienceClaim(jwt.getAudience(), Arrays.asList((String[]) entry.getValue())); + assertValidAudienceClaim(jwt.getAudience(), (List) entry.getValue()); break; case PublicClaims.EXPIRES_AT: assertValidDateClaim(jwt.getExpiresAt(), (Long) entry.getValue(), true); @@ -410,7 +410,7 @@ private void verifyClaims(DecodedJWT jwt, Map claims) throws Tok assertValidDateClaim(jwt.getNotBefore(), (Long) entry.getValue(), false); break; case PublicClaims.ISSUER: - assertValidIssuerClaim(jwt.getIssuer(), Arrays.asList((String[]) entry.getValue())); + assertValidIssuerClaim(jwt.getIssuer(), (List) entry.getValue()); break; case PublicClaims.JWT_ID: assertValidStringClaim(entry.getKey(), jwt.getId(), (String) entry.getValue()); diff --git a/lib/src/test/java/com/auth0/jwt/JWTVerifierTest.java b/lib/src/test/java/com/auth0/jwt/JWTVerifierTest.java index bebbedd4..c6691a02 100644 --- a/lib/src/test/java/com/auth0/jwt/JWTVerifierTest.java +++ b/lib/src/test/java/com/auth0/jwt/JWTVerifierTest.java @@ -599,20 +599,7 @@ public void shouldRemoveClaimWhenPassingNull() throws Exception { .build(); assertThat(verifier.claims, is(notNullValue())); - assertThat(verifier.claims, not(hasKey("sub"))); - } - - @Test - public void shouldThrowWhenNoIssuerPresent() { - exception.expect(InvalidClaimException.class); - exception.expectMessage("The Claim 'iss' value doesn't match the required issuer."); - - String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.Rq8IxqeX7eA6GgYxlcHdPFVRNFFZc5rEI3MQTZZbK3I"; - - JWTVerifier.init(Algorithm.HMAC256("secret")) - .withIssuer("oauth") - .build() - .verify(token); + assertThat(verifier.claims, not(hasKey("iss"))); } @Test From 7cdbf4782616f94dd572f0c70b9b79bb7ed2dc73 Mon Sep 17 00:00:00 2001 From: Johan Geerts Date: Fri, 8 Mar 2019 17:39:30 +0100 Subject: [PATCH 5/5] Added tests to increase coverage --- .../java/com/auth0/jwt/JWTVerifierTest.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/lib/src/test/java/com/auth0/jwt/JWTVerifierTest.java b/lib/src/test/java/com/auth0/jwt/JWTVerifierTest.java index c6691a02..e297c59c 100644 --- a/lib/src/test/java/com/auth0/jwt/JWTVerifierTest.java +++ b/lib/src/test/java/com/auth0/jwt/JWTVerifierTest.java @@ -74,6 +74,18 @@ public void shouldThrowOnInvalidIssuer() throws Exception { .verify(token); } + @Test + public void shouldThrowOnNullIssuer() throws Exception { + exception.expect(InvalidClaimException.class); + exception.expectMessage("The Claim 'iss' value doesn't match the required issuer."); + + String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.t-IDcSemACt8x4iTMCda8Yhe3iZaWbvV5XKSTbuAn0M"; + JWTVerifier.init(Algorithm.HMAC256("secret")) + .withIssuer("auth0") + .build() + .verify(token); + } + @Test public void shouldValidateSubject() throws Exception { String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.Rq8IxqeX7eA6GgYxlcHdPFVRNFFZc5rEI3MQTZZbK3I"; @@ -138,6 +150,18 @@ public void shouldThrowOnInvalidAudience() throws Exception { .verify(token); } + @Test + public void shouldRemoveAudienceWhenPassingNull() throws Exception { + Algorithm algorithm = mock(Algorithm.class); + JWTVerifier verifier = JWTVerifier.init(algorithm) + .withAudience("John") + .withAudience(null) + .build(); + + assertThat(verifier.claims, is(notNullValue())); + assertThat(verifier.claims, not(hasKey("aud"))); + } + @Test public void shouldThrowOnNullCustomClaimName() throws Exception { exception.expect(IllegalArgumentException.class);