[java-oidc-common] branch main updated: Port up changes to "iat" validator and new ID validator.

Scott Cantor cantor.2 at osu.edu
Thu Dec 30 17:09:55 UTC 2021


This is an automated email from the git hooks/post-receive script.

scantor pushed a commit to branch main
in repository java-oidc-common.

View the commit online:
http://git.shibboleth.net/view/?p=java-oidc-common.git;a=commit;h=c7078ed2e9d9d32760a3fda4a89f8d08eacb86a2

The following commit(s) were added to refs/heads/main by this push:
     new c7078ed  Port up changes to "iat" validator and new ID validator.
c7078ed is described below

commit c7078ed2e9d9d32760a3fda4a89f8d08eacb86a2
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Thu Dec 30 12:09:52 2021 -0500

    Port up changes to "iat" validator and new ID validator.
---
 oidc-common-crypto-impl/pom.xml                    |  19 +++-
 .../jwt/claims/impl/IssuedAtClaimsValidator.java   |  85 +++++++++++-----
 .../claims/impl/JWTIdentifierClaimsValidator.java  | 108 +++++++++++++++++++++
 .../claims/impl/IssuedAtClaimsValidatorTest.java   |  88 +++++++++++++----
 .../impl/JWTIdentifierClaimsValidatorTest.java     | 101 +++++++++++++++++++
 5 files changed, 355 insertions(+), 46 deletions(-)

diff --git a/oidc-common-crypto-impl/pom.xml b/oidc-common-crypto-impl/pom.xml
index 9ebbc04..7e844ae 100644
--- a/oidc-common-crypto-impl/pom.xml
+++ b/oidc-common-crypto-impl/pom.xml
@@ -19,6 +19,12 @@
     </properties>
     
     <dependencies>
+        <dependency>
+            <groupId>com.nimbusds</groupId>
+            <artifactId>nimbus-jose-jwt</artifactId>
+            <scope>compile</scope>
+        </dependency>
+
         <dependency>
             <groupId>${project.groupId}</groupId>
             <artifactId>oidc-common-crypto-api</artifactId>
@@ -34,16 +40,13 @@
             <artifactId>idp-authn-api</artifactId>
             <scope>provided</scope>
         </dependency>
-        <dependency>
-            <groupId>com.nimbusds</groupId>
-            <artifactId>nimbus-jose-jwt</artifactId>
-            <scope>compile</scope>
-        </dependency>
         <dependency>
             <groupId>javax.servlet</groupId>
             <artifactId>javax.servlet-api</artifactId>
             <scope>provided</scope>
         </dependency>
+
+         <!-- Test scope -->
          <dependency>
             <groupId>${spring.groupId}</groupId>
             <artifactId>spring-test</artifactId>
@@ -55,6 +58,12 @@
             <version>${opensaml.version}</version>
             <type>test-jar</type>
             <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${opensaml.groupId}</groupId>
+            <artifactId>opensaml-storage-impl</artifactId>
+            <version>${opensaml.version}</version>
+            <scope>test</scope>
         </dependency>
          <dependency>
             <groupId>${idp.groupId}</groupId>
diff --git a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/jwt/claims/impl/IssuedAtClaimsValidator.java b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/jwt/claims/impl/IssuedAtClaimsValidator.java
index 540db8f..fc287fe 100644
--- a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/jwt/claims/impl/IssuedAtClaimsValidator.java
+++ b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/jwt/claims/impl/IssuedAtClaimsValidator.java
@@ -34,35 +34,62 @@ import net.shibboleth.utilities.java.support.component.ComponentSupport;
 import net.shibboleth.utilities.java.support.logic.Constraint;
 
 /**
- * Iff the 'iat' claim is present in the ID Token, verifies it is not to far away from 
- * the current time. A configured window/deviation is allowed. See section 3.1.3.7 of OpenID Connect core 1.0.
+ * If the 'iat' claim is present in the JWT, verifies it is not to far away from the current time.
+ * The message lifetime, clock skew and the claim existence requirement can be configured.
  */
 @ThreadSafeAfterInit
 public class IssuedAtClaimsValidator extends AbstractClaimsValidator {
     
-    /**
-     *  Maximum amount (in either direction from now) of duration for which a token is valid after 
-     *  it is issued (Default value: 60 seconds). 
-     */
-    @Nonnull private Duration iatWindow;
-    
+    /** Clock skew adjustment in both directions to consider still acceptable (Default value: 1 minute). */
+    @Nonnull private Duration clockSkew;
+
+    /** Amount of time for which a message is valid after it is issued (Default value: 1 minute). */
+    @Nonnull private Duration messageLifetime;
+
+    /** Whether this rule is required to be met. */
+    private boolean requiredRule;
+
     /** Constructor.*/
     public IssuedAtClaimsValidator() {
-        iatWindow = Duration.ofSeconds(60);
+        messageLifetime = Duration.ofMinutes(1);
+        clockSkew = Duration.ofMinutes(1);
+        requiredRule = true;
+    }
+    
+    /**
+     * Sets the amount of time for which a message is valid.
+     * 
+     * @param lifetime amount of time for which a message is valid
+     */
+    public void setMessageLifetime(@Nonnull final Duration lifetime) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        
+        Constraint.isNotNull(lifetime, "Token lifetime cannot be null");
+        Constraint.isFalse(lifetime.isNegative(), "Token lifetime cannot be negative");
+
+        messageLifetime = lifetime;
     }
     
     /**
-     * Sets the amount of time for which a token is valid from when it was issued.
+     * Set the clock skew.
      * 
-     * @param window amount of time for which a token is valid
+     * @param skew clock skew to set
      */
-    public void setIatWindow(@Nonnull final Duration window) {
+    public void setClockSkew(@Nonnull final Duration skew) {  
         ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
         
-        Constraint.isNotNull(window, "Token issued at window cannot be null");
-        Constraint.isFalse(window.isNegative(), "Token issued at window cannot be negative");
+        clockSkew = Constraint.isNotNull(skew, "Clock skew cannot be null");
+    }
 
-        iatWindow = window;
+    /**
+     * Sets whether this rule is required to be met.
+     * 
+     * @param required whether this rule is required to be met
+     */
+    public void setRequiredRule(final boolean required) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        
+        requiredRule = required;
     }
 
     /** {@inheritDoc} */
@@ -73,17 +100,27 @@ public class IssuedAtClaimsValidator extends AbstractClaimsValidator {
         final Date iatDate = claims.getIssueTime();
         if (iatDate != null) {           
             final Instant iat = iatDate.toInstant();        
-            final Instant now = Instant.now();        
-            final Duration iatDifference = Duration.between(now, iat).abs();
-            
-            if (iatWindow.compareTo(iatDifference)  < 0) {
-                throw new JWTValidationException("JWT issued-at time is too far away from the current time. "
+            final Instant now = Instant.now();
+            final Instant latestValid = now.plus(clockSkew.abs());
+            final Instant expiration = iat.plus(clockSkew.abs()).plus(messageLifetime);
+
+            // Check message wasn't issued in the future
+            if (iat.isAfter(latestValid)) {
+                throw new JWTValidationException("JWT was rejected because it was issued in the future. "
+                        + "Token issued at '"+iat+"' was too far away from the current time '"+now+"' "
+                        + "with acceptable lifetime of '" + messageLifetime + "', clockSkew of '" + clockSkew + "'");
+            }
+
+            // Check message has not expired
+            if (expiration.isBefore(now)) {
+                throw new JWTValidationException("JWT was rejected due to issue instance expiration. "
                         + "Token issued at '"+iat+"' was too far away from the current time '"+now+"' "
-                                + "with acceptable deviation of "
-                                + "'"+iatWindow+"', difference is '"+iatDifference+"'");
+                        + "with acceptable lifetime of '" + messageLifetime + "', clockSkew of '" + clockSkew + "'");
+
             }
+        } else if (requiredRule) {
+            throw new JWTValidationException("JWT was rejected due to missing required 'iat' claim.");
         }
-        
     }
 
-}
+}
\ No newline at end of file
diff --git a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/jwt/claims/impl/JWTIdentifierClaimsValidator.java b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/jwt/claims/impl/JWTIdentifierClaimsValidator.java
new file mode 100644
index 0000000..ab45f20
--- /dev/null
+++ b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/jwt/claims/impl/JWTIdentifierClaimsValidator.java
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.oidc.security.jwt.claims.impl;
+
+import java.time.Duration;
+import java.util.Date;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.storage.ReplayCache;
+
+import com.nimbusds.jwt.JWTClaimsSet;
+
+import net.shibboleth.oidc.jwt.claims.AbstractClaimsValidator;
+import net.shibboleth.oidc.jwt.claims.JWTValidationException;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.primitive.StringSupport;
+
+/**
+ * Verifies the identifier (jit) from the JWT against repla via configurable {@link ReplayCache}. Single identifier is
+ * stored in the cache for the lifetime of the JWT: expiration instant is taken from the 'exp' -claim.
+ */
+public class JWTIdentifierClaimsValidator extends AbstractClaimsValidator {
+
+    /** Message replay cache instance to use. */
+    @NonnullAfterInit private ReplayCache replayCache;
+    
+    /**
+     * Clock skew adjustment in both directions to consider still acceptable (Default value: 1 minute).
+     */
+    @Nonnull private Duration clockSkew;
+
+    /**
+     * Constructor.
+     */
+    public JWTIdentifierClaimsValidator() {
+        clockSkew = Duration.ofMinutes(1);
+    }
+    
+    /**
+     * Set the replay cache instance to use.
+     * 
+     * @param cache replay cache to set.
+     */
+    public void setReplayCache(@Nonnull final ReplayCache cache) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        replayCache = Constraint.isNotNull(cache, "ReplayCache cannot be null");
+    }
+
+    /**
+     * Set the clock skew.
+     * 
+     * @param skew clock skew to set.
+     */
+    public void setClockSkew(@Nonnull final Duration skew) {  
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        clockSkew = Constraint.isNotNull(skew, "Clock skew cannot be null");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void doInitialize() throws ComponentInitializationException {
+        super.doInitialize();
+        
+        if (replayCache == null) {
+            throw new ComponentInitializationException("ReplayCache can not be null");
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void doValidate(@Nonnull final JWTClaimsSet claims, @Nullable final ProfileRequestContext context)
+                throws JWTValidationException {
+
+        final Date exp = claims.getExpirationTime();
+        if (exp == null) {
+            throw new JWTValidationException("The claims set is missing required expiration time (exp)");
+        }
+        final String jit = claims.getJWTID();
+        if (StringSupport.trimOrNull(jit) == null) {
+            throw new JWTValidationException("The claims set is missing required JWT identifier (jit)");
+        }
+        if (!replayCache.check(getClass().getName(), jit, exp.toInstant().plus(clockSkew))) {
+            throw new JWTValidationException("Replay detected for jit '" + jit + "'");
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/jwt/claims/impl/IssuedAtClaimsValidatorTest.java b/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/jwt/claims/impl/IssuedAtClaimsValidatorTest.java
index a450b82..d796fab 100644
--- a/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/jwt/claims/impl/IssuedAtClaimsValidatorTest.java
+++ b/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/jwt/claims/impl/IssuedAtClaimsValidatorTest.java
@@ -23,7 +23,7 @@ import java.util.Date;
 
 import javax.annotation.Nonnull;
 
-
+import org.opensaml.profile.context.ProfileRequestContext;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
@@ -33,23 +33,25 @@ import net.shibboleth.oidc.jwt.claims.JWTValidationException;
 import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
 
 /** Test for the {@link IssuedAtClaimsValidator}. */
-public class IssuedAtClaimsValidatorTest extends AbstractClaimsValidatorTest {
+public class IssuedAtClaimsValidatorTest {
     
     /** The validator to test.*/
     @Nonnull private IssuedAtClaimsValidator validator;
 
+    @Nonnull private ProfileRequestContext prc;
     
     @BeforeMethod
     public void setup() throws ComponentInitializationException {
-        super.setup();
         validator = new IssuedAtClaimsValidator();
+        prc = new ProfileRequestContext();
     }
     
     @Test
     public void doValidateTest() throws JWTValidationException, ComponentInitializationException {
         final JWTClaimsSet claimsSet = new JWTClaimsSet.Builder().issueTime(Date.from(Instant.now())).build();
         validator.setId("test-validator");
-        validator.setIatWindow(Duration.ofMinutes(1));
+        validator.setMessageLifetime(Duration.ofMinutes(1));
+        validator.setClockSkew(Duration.ofMinutes(1));
         validator.initialize();
         validator.validate(claimsSet, prc);
     }
@@ -60,26 +62,79 @@ public class IssuedAtClaimsValidatorTest extends AbstractClaimsValidatorTest {
         final JWTClaimsSet claimsSet = new JWTClaimsSet.Builder().issueTime(
                 Date.from(Instant.now().minus(Duration.ofMinutes(10)))).build();
         validator.setId("test-validator");
-        validator.setIatWindow(Duration.ofMinutes(20));
+        validator.setMessageLifetime(Duration.ofMinutes(6));
+        validator.setClockSkew(Duration.ofMinutes(6));
         validator.initialize();
         validator.validate(claimsSet, prc);
     }
-    
+
+    @Test
+    public void doValidateTestIssuedInPastButInWindow_longClockSkew() 
+            throws JWTValidationException, ComponentInitializationException {
+        final JWTClaimsSet claimsSet = new JWTClaimsSet.Builder().issueTime(
+                Date.from(Instant.now().minus(Duration.ofMinutes(10)))).build();
+        validator.setId("test-validator");
+        validator.setMessageLifetime(Duration.ofMinutes(1));
+        validator.setClockSkew(Duration.ofMinutes(15));
+        validator.initialize();
+        validator.validate(claimsSet, prc);
+    }
+
+    @Test
+    public void doValidateTestIssuedInPastButInWindow_longLifetime() 
+            throws JWTValidationException, ComponentInitializationException {
+        final JWTClaimsSet claimsSet = new JWTClaimsSet.Builder().issueTime(
+                Date.from(Instant.now().minus(Duration.ofMinutes(10)))).build();
+        validator.setId("test-validator");
+        validator.setMessageLifetime(Duration.ofMinutes(15));
+        validator.setClockSkew(Duration.ofMinutes(1));
+        validator.initialize();
+        validator.validate(claimsSet, prc);
+    }
+
+    @Test
+    public void doValidateTestIssuedInFutureButInWindow() 
+            throws JWTValidationException, ComponentInitializationException {
+        final JWTClaimsSet claimsSet = new JWTClaimsSet.Builder().issueTime(
+                Date.from(Instant.now().plus(Duration.ofMinutes(5)))).build();
+        validator.setId("test-validator");
+        validator.setMessageLifetime(Duration.ofMinutes(1));
+        validator.setClockSkew(Duration.ofMinutes(6));
+        validator.initialize();
+        validator.validate(claimsSet, prc);
+    }
+
     @Test
-    public void doValidateTestNoIssuedAtClaim() throws JWTValidationException, ComponentInitializationException {
+    public void doValidateTestNoOptionalIssuedAtClaim()
+            throws JWTValidationException, ComponentInitializationException {
         final JWTClaimsSet claimsSet = new JWTClaimsSet.Builder().build();
         validator.setId("test-validator");
-        validator.setIatWindow(Duration.ofMinutes(1));
+        validator.setMessageLifetime(Duration.ofMinutes(1));
+        validator.setClockSkew(Duration.ofMinutes(1));
+        validator.setRequiredRule(false);
         validator.initialize();
         validator.validate(claimsSet, prc);
     }
-    
+
+    @Test(expectedExceptions = JWTValidationException.class)
+    public void doValidateTestNoRequiredIssuedAtClaim()
+            throws JWTValidationException, ComponentInitializationException {
+        final JWTClaimsSet claimsSet = new JWTClaimsSet.Builder().build();
+        validator.setId("test-validator");
+        validator.setMessageLifetime(Duration.ofMinutes(1));
+        validator.setClockSkew(Duration.ofMinutes(1));
+        validator.setRequiredRule(true);
+        validator.initialize();
+        validator.validate(claimsSet, prc);
+    }
+
     @Test(expectedExceptions = JWTValidationException.class)
     public void doInValidateTestIssuedTooFarInPast() throws JWTValidationException, ComponentInitializationException {
         final JWTClaimsSet claimsSet = new JWTClaimsSet.Builder().issueTime(
-                Date.from(Instant.now().minus(Duration.ofMinutes(10)))).build();
+                Date.from(Instant.now().minus(Duration.ofMinutes(11)))).build();
         validator.setId("test-validator");
-        validator.setIatWindow(Duration.ofMinutes(1));
+        validator.setMessageLifetime(Duration.ofMinutes(5));
+        validator.setClockSkew(Duration.ofMinutes(5));
         validator.initialize();
         validator.validate(claimsSet, prc);
     }
@@ -87,13 +142,12 @@ public class IssuedAtClaimsValidatorTest extends AbstractClaimsValidatorTest {
     @Test(expectedExceptions = JWTValidationException.class)
     public void doInValidateTestIssuedTooFarInFuture() throws JWTValidationException, ComponentInitializationException {
         final JWTClaimsSet claimsSet = new JWTClaimsSet.Builder().issueTime(
-                Date.from(Instant.now().plus(Duration.ofMinutes(10)))).build();
+                Date.from(Instant.now().plus(Duration.ofMinutes(6)))).build();
         validator.setId("test-validator");
-        validator.setIatWindow(Duration.ofMinutes(1));
+        validator.setMessageLifetime(Duration.ofMinutes(5));
+        validator.setClockSkew(Duration.ofMinutes(5));
         validator.initialize();
         validator.validate(claimsSet, prc);
     }
-    
-    
-     
-}
+
+}
\ No newline at end of file
diff --git a/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/jwt/claims/impl/JWTIdentifierClaimsValidatorTest.java b/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/jwt/claims/impl/JWTIdentifierClaimsValidatorTest.java
new file mode 100644
index 0000000..29bc89e
--- /dev/null
+++ b/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/jwt/claims/impl/JWTIdentifierClaimsValidatorTest.java
@@ -0,0 +1,101 @@
+/* 
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.oidc.security.jwt.claims.impl;
+
+import java.time.Instant;
+import java.util.Date;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.storage.ReplayCache;
+import org.opensaml.storage.impl.MemoryStorageService;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.nimbusds.jwt.JWTClaimsSet;
+
+import net.shibboleth.oidc.jwt.claims.JWTValidationException;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+
+/** Test for the {@link JWTIdentifierClaimsValidator}. */
+public class JWTIdentifierClaimsValidatorTest {
+
+    /** The validator to test. */
+    private JWTIdentifierClaimsValidator validator;
+    
+    @Nonnull private ProfileRequestContext prc;
+    
+    @BeforeMethod
+    public void setup() throws ComponentInitializationException {
+        validator = new JWTIdentifierClaimsValidator();
+        ReplayCache replayCache = new ReplayCache();
+        MemoryStorageService storageService = new MemoryStorageService();
+        storageService.setId("mockId");
+        storageService.initialize();
+        replayCache.setStorage(storageService);
+        validator.setReplayCache(replayCache);
+        validator.setId("test-validator");
+        validator.initialize();
+        prc = new ProfileRequestContext();
+    }
+    
+    @Test(expectedExceptions = ComponentInitializationException.class)
+    public void testNoReplayCache() throws ComponentInitializationException {
+        validator = new JWTIdentifierClaimsValidator();
+        validator.setId("test-validator");
+        validator.initialize();        
+    }
+    
+    @Test(expectedExceptions = JWTValidationException.class)
+    public void doValidateTest_failsWhenNoExp() throws JWTValidationException {
+        final JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
+                .jwtID("mockId")
+                .build();
+        validator.doValidate(claimsSet, prc);
+    }
+
+    @Test(expectedExceptions = JWTValidationException.class)
+    public void doValidateTest_failsWhenNoJit() throws JWTValidationException {
+        final JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
+                .expirationTime(Date.from(Instant.now().plusSeconds(300)))
+                .build();
+        validator.doValidate(claimsSet, prc);
+    }
+    
+    @Test
+    public void doValidateTest_success() throws JWTValidationException {
+        final JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
+                .jwtID("mockId")
+                .expirationTime(Date.from(Instant.now().plusSeconds(300)))
+                .build();
+        validator.doValidate(claimsSet, prc);
+        
+    }
+    
+    @Test(expectedExceptions = JWTValidationException.class)
+    public void doValidateTest_failsWhenReplayed() throws JWTValidationException {
+        final JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
+                .jwtID("mockId")
+                .expirationTime(Date.from(Instant.now().plusSeconds(300)))
+                .build();
+        validator.doValidate(claimsSet, prc);
+        validator.doValidate(claimsSet, prc);        
+    }
+
+}

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list