[java-idp-oidc] branch maint-3.0 updated: JOIDC-65 JWT client authentication support is incomplete

Henri Mikkonen henri.mikkonen at iki.fi
Tue Dec 28 13:04:29 UTC 2021


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

hjmikkon pushed a commit to branch maint-3.0
in repository java-idp-oidc.

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

The following commit(s) were added to refs/heads/maint-3.0 by this push:
     new 6d6568a8 JOIDC-65 JWT client authentication support is incomplete
6d6568a8 is described below

commit 6d6568a806d873cf3210b49275fe8c003c81abf8
Author: Henri Mikkonen <henri.mikkonen at iki.fi>
AuthorDate: Tue Dec 28 15:03:05 2021 +0200

    JOIDC-65 JWT client authentication support is incomplete
    
    https://shibboleth.atlassian.net/browse/JOIDC-65
    
    Refactored the incoming JWT authentication validation: created a new abstract-api-jwt -flow, which
    is inherited by token, introspection and revocation flows. Its -beans.xml contains wiring of claims
    validators for the required JWT claims. The bean is wired to the ValaidateEndpointAuthentication action.
---
 .../impl/ValidateEndpointAuthentication.java       |  97 ++++--------------
 ...entIDFromOIDCMetadataContextLookupFunction.java |  79 +++++++++++++++
 .../claims/impl/JwtIdentifierClaimsValidator.java  | 110 +++++++++++++++++++++
 .../oauth2/introspection/introspection-beans.xml   |  10 +-
 .../oauth2/introspection/introspection-flow.xml    |   2 +-
 .../flows/oauth2/revocation/revocation-beans.xml   |  10 +-
 .../flows/oauth2/revocation/revocation-flow.xml    |   2 +-
 .../oidc-abstract-api-jwt-beans.xml                |  56 +++++++++++
 .../oidc-abstract-api-jwt-flow.xml                 |  11 +++
 .../idp/flows/oidc/token/token-beans.xml           |  12 +--
 .../shibboleth/idp/flows/oidc/token/token-flow.xml |   2 +-
 .../impl/ValidateEndpointAuthenticationTest.java   | 105 ++++++++++++++------
 .../impl/JwtIdentifierClaimsValidatorTest.java     | 105 ++++++++++++++++++++
 13 files changed, 462 insertions(+), 139 deletions(-)

diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateEndpointAuthentication.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateEndpointAuthentication.java
index 26774daf..a8dce0d6 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateEndpointAuthentication.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateEndpointAuthentication.java
@@ -18,8 +18,7 @@
 package net.shibboleth.idp.plugin.oidc.op.profile.impl;
 
 import java.text.ParseException;
-import java.util.ArrayList;
-import java.util.Date;
+import java.util.Collections;
 import java.util.List;
 import java.util.function.Function;
 
@@ -51,19 +50,16 @@ import com.nimbusds.openid.connect.sdk.rp.OIDCClientMetadata;
 
 import net.shibboleth.idp.plugin.oidc.op.config.navigate.TokenEndpointAuthMethodLookupFunction;
 import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCMetadataContext;
-import net.shibboleth.idp.plugin.oidc.op.security.jwt.claims.impl.IssuedAtClaimsValidator;
 import net.shibboleth.oidc.jwt.claims.ClaimsValidator;
+import net.shibboleth.oidc.jwt.claims.JWTClaimsValidation;
 import net.shibboleth.oidc.jwt.claims.JWTValidationException;
 import net.shibboleth.oidc.security.impl.JWTSignatureValidationUtil;
 import net.shibboleth.oidc.security.impl.OIDCSignatureValidationParameters;
-import net.shibboleth.oidc.security.jwt.claims.impl.AudienceClaimsValidator;
 import net.shibboleth.oidc.security.jwt.claims.impl.ChainingJWTClaimsValidation;
-import net.shibboleth.oidc.security.jwt.claims.impl.ExpiryClaimsValidator;
 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;
 
 /**
  * Validates the endpoint authentication with the token_endpoint_auth_method stored to the client's metadata.
@@ -94,15 +90,9 @@ public class ValidateEndpointAuthentication extends AbstractOIDCRequestAction<Ab
      */
     @Nonnull private Function<ProfileRequestContext, SecurityParametersContext> securityParametersLookupStrategy;
 
-    /** The validator used for the expiration time of the incoming JWTs. */
-    @Nonnull private ClaimsValidator expiryClaimsValidator;
-
-    /** The validator used for the issuance time of the incoming JWTs. */
-    @Nonnull private ClaimsValidator issuedAtClaimsValidator;
-
-    /** The validator used for the audience of the incoming JWTs. */
-    @Nonnull private ClaimsValidator audienceClaimsValidator;
-
+    /** The claims validator to be applied for validating the incoming JWTs. */
+    @NonnullAfterInit private JWTClaimsValidation claimsValidation;
+    
     /**
      * Constructor.
      */
@@ -111,11 +101,6 @@ public class ValidateEndpointAuthentication extends AbstractOIDCRequestAction<Ab
                 new InboundMessageContextLookup());
         tokenEndpointAuthMethodsLookupStrategy = new TokenEndpointAuthMethodLookupFunction();
         securityParametersLookupStrategy = new ChildContextLookup<>(SecurityParametersContext.class);
-        audienceClaimsValidator = new AudienceClaimsValidator();
-        ((AudienceClaimsValidator) audienceClaimsValidator).setAudienceLookupStrategy(
-                (P, J) -> getHttpServletRequest().getRequestURL().toString());
-        expiryClaimsValidator = new ExpiryClaimsValidator();
-        issuedAtClaimsValidator = new IssuedAtClaimsValidator();
     }
         
     /**
@@ -167,33 +152,13 @@ public class ValidateEndpointAuthentication extends AbstractOIDCRequestAction<Ab
     }
 
     /**
-     * Set the validator used for the expiration time of the incoming JWTs.
+     * Set the claims validator used for validating the incoming JWTs.
      * 
-     * @param validator What to set.
+     * @param validators What to set.
      */
-    public void setExpiryClaimsValidator(@Nonnull final ClaimsValidator validator) {
+    public void setClaimsValidation(@Nonnull final JWTClaimsValidation validation) {
         ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-        expiryClaimsValidator = Constraint.isNotNull(validator, "ExpiryClaimsValidator cannot be null");
-    }
-
-    /**
-     * Set the validator used for the issuance time of the incoming JWTs.
-     * 
-     * @param validator What to set.
-     */
-    public void setIssuedAtClaimsValidator(@Nonnull final ClaimsValidator validator) {
-        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-        issuedAtClaimsValidator = Constraint.isNotNull(validator, "IssuedAtClaimsValidator cannot be null");
-    }
-
-    /**
-     * Set the validator used for the audience of the incoming JWTs.
-     * 
-     * @param validator What to set.
-     */
-    public void setAudienceClaimsValidator(@Nonnull final ClaimsValidator validator) {
-        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-        audienceClaimsValidator = Constraint.isNotNull(validator, "AudienceClaimsValidator cannot be null");
+        claimsValidation = Constraint.isNotNull(validation, "Claims validator cannot be null");
     }
 
 
@@ -205,6 +170,10 @@ public class ValidateEndpointAuthentication extends AbstractOIDCRequestAction<Ab
         if (replayCache == null) {
             throw new ComponentInitializationException("ReplayCache cannot be null");
         }
+        
+        if (claimsValidation == null) {
+            throw new ComponentInitializationException("ClaimsValidator cannot be null");
+        }
     }
     
     /** {@inheritDoc} */
@@ -288,52 +257,22 @@ public class ValidateEndpointAuthentication extends AbstractOIDCRequestAction<Ab
      * @param profileRequestContext The profile request context.
      * @return true if JWT meets the requirements, false otherwise.
      */
-    protected boolean validateJwtClaims(final SignedJWT jwt, final String clientId, @Nonnull final ProfileRequestContext profileRequestContext ) {
+    protected boolean validateJwtClaims(final SignedJWT jwt, final String clientId,
+            @Nonnull final ProfileRequestContext profileRequestContext ) {
         final JWTClaimsSet claimsSet;
         try {
             claimsSet = jwt.getJWTClaimsSet();
         } catch (ParseException e) {
-            log.error("{} Could not parse the JWT into claims set, it cannot be validated", getLogPrefix());
-            return false;
-        }
-        final List<String> aud = claimsSet.getAudience();
-        if (aud == null || aud.isEmpty()) {
-            log.warn("{} The incoming JWT from {} is missing audience (aud)", getLogPrefix(), clientId);
-            return false;
-        }
-        final Date exp = claimsSet.getExpirationTime();
-        if (exp == null) {
-            log.warn("{} The incoming JWT from {} is missing expiration time (exp)", getLogPrefix(), clientId);
+            log.error("{} Could not parse the JWT from {} into claims set, it cannot be validated",
+                    getLogPrefix(), clientId);
             return false;
         }
-        final String jit = claimsSet.getJWTID();
-        if (StringSupport.trimOrNull(jit) == null) {
-            log.warn("{} The incoming JWT from {} is missing JWT identifier (jit)", getLogPrefix(), clientId);
-            return false;            
-        }
-        final ChainingJWTClaimsValidation validatorChain = new ChainingJWTClaimsValidation();
-        final List<ClaimsValidator> validators = new ArrayList<>();
-
-        log.info("{} Validating against {}", getLogPrefix(), getHttpServletRequest().getRequestURL());
-        
-        final Date iat = claimsSet.getIssueTime();
-        if (iat != null) {
-            validators.add(issuedAtClaimsValidator);
-        }
-        validators.add(expiryClaimsValidator);
-        validators.add(audienceClaimsValidator);
-        
-        validatorChain.setClaimValidators(validators);
         try {
-            validatorChain.validate(claimsSet, profileRequestContext);
+            claimsValidation.validate(claimsSet, profileRequestContext);
         } catch (final JWTValidationException e) {
             log.warn("{} JWT validation failed for the client {}", getLogPrefix(), clientId, e);
             return false;
         }
-        if (!replayCache.check(getClass().getName(), jit, exp.toInstant())) {
-            log.warn("{} Replay detected for the incoming JWT from {}", getLogPrefix(), clientId);
-            return false;
-        }
         return true;
 
     } 
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/logic/ClientIDFromOIDCMetadataContextLookupFunction.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/logic/ClientIDFromOIDCMetadataContextLookupFunction.java
new file mode 100644
index 00000000..b4029b70
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/logic/ClientIDFromOIDCMetadataContextLookupFunction.java
@@ -0,0 +1,79 @@
+/*
+ * 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.idp.plugin.oidc.op.profile.logic;
+
+import java.util.function.BiFunction;
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.profile.context.navigate.InboundMessageContextLookup;
+
+import com.nimbusds.jwt.JWTClaimsSet;
+import com.nimbusds.openid.connect.sdk.rp.OIDCClientInformation;
+
+import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCMetadataContext;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+
+/**
+ * A function that returns client_id via client information stored in {@link OIDCMetadataContext}.
+ * 
+ * TODO: the class is temporarily located here due to OP plugin 3.0.3 patch.
+ */
+public class ClientIDFromOIDCMetadataContextLookupFunction
+        implements BiFunction<ProfileRequestContext, JWTClaimsSet, String> {
+
+    /** Strategy that will return {@link OIDCMetadataContext}. */
+    @Nonnull private Function<ProfileRequestContext, OIDCMetadataContext> oidcMetadataContextLookupStrategy;
+
+    /**
+     * Constructor.
+     */
+    public ClientIDFromOIDCMetadataContextLookupFunction() {
+        oidcMetadataContextLookupStrategy = new ChildContextLookup<>(OIDCMetadataContext.class).compose(
+                new InboundMessageContextLookup());
+    }
+
+    /**
+     * Set the strategy used to return the {@link OIDCMetadataContext}.
+     * 
+     * @param strategy The lookup strategy.
+     */
+    public void setOidcMetadataContextLookupStrategy(
+            @Nonnull final Function<ProfileRequestContext, OIDCMetadataContext> strategy) {
+        oidcMetadataContextLookupStrategy =
+                Constraint.isNotNull(strategy, "OIDCMetadataContext lookup strategy cannot be null");
+    }
+
+    /** {@inheritDoc} */
+    @Override @Nullable
+    public String apply(@Nonnull final ProfileRequestContext prc, @Nullable final JWTClaimsSet claimsSet) {
+        final OIDCMetadataContext oidcMetadataContext = oidcMetadataContextLookupStrategy.apply(prc);
+        if (oidcMetadataContext == null) {
+            return null;
+        }
+        final OIDCClientInformation clientInformation = oidcMetadataContext.getClientInformation();
+        if (clientInformation == null || clientInformation.getID() == null) {
+            return null;
+        }
+        return clientInformation.getID().toString();
+    }
+}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/security/jwt/claims/impl/JwtIdentifierClaimsValidator.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/security/jwt/claims/impl/JwtIdentifierClaimsValidator.java
new file mode 100644
index 00000000..12d9cee3
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/security/jwt/claims/impl/JwtIdentifierClaimsValidator.java
@@ -0,0 +1,110 @@
+/*
+ * 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.idp.plugin.oidc.op.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.
+ * 
+ * TODO: the class is temporarily located here due to OP plugin 3.0.3 patch. To be moved to oidc-commons:
+ * net.shibboleth.oidc.security.jwt.claims.impl -package.
+ */
+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 + "'");
+        }
+    }
+}
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth2/introspection/introspection-beans.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth2/introspection/introspection-beans.xml
index c919f4d3..7085f62d 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth2/introspection/introspection-beans.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth2/introspection/introspection-beans.xml
@@ -28,21 +28,13 @@
     <bean id="ValidateEndpointAuthentication"
         class="net.shibboleth.idp.plugin.oidc.op.profile.impl.ValidateEndpointAuthentication" scope="prototype"
         p:httpServletRequest-ref="shibboleth.HttpServletRequest" p:replayCache-ref="shibboleth.ReplayCache"
-        p:expiryClaimsValidator-ref="ExpiryClaimsValidator" p:issuedAtClaimsValidator-ref="IssuedAtClaimsValidator">
+        p:claimsValidation-ref="shibboleth.oidc.JWTClaimsValidation">
         <property name="securityParametersLookupStrategy">
             <bean parent="shibboleth.Functions.Compose"
                 c:g-ref="shibboleth.ChildLookup.SecurityParameters"
                 c:f-ref="shibboleth.ChildLookup.RelyingParty" />
         </property>
     </bean>
-    
-    <bean id="ExpiryClaimsValidator"
-        class="net.shibboleth.oidc.security.jwt.claims.impl.ExpiryClaimsValidator"
-        p:clockSkew="%{idp.policy.clockSkew:PT1M}" />
-
-    <bean id="IssuedAtClaimsValidator"
-        class="net.shibboleth.idp.plugin.oidc.op.security.jwt.claims.impl.IssuedAtClaimsValidator"
-        p:messageLifetime="%{idp.policy.messageLifetime:PT1M}" p:clockSkew="%{idp.policy.clockSkew:PT1M}" />
 
     <bean id="FormOutboundMessage"
         class="net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl.FormOutboundIntrospectionResponseMessage" scope="prototype"
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth2/introspection/introspection-flow.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth2/introspection/introspection-flow.xml
index a8174188..21548afc 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth2/introspection/introspection-flow.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth2/introspection/introspection-flow.xml
@@ -1,7 +1,7 @@
 <flow xmlns="http://www.springframework.org/schema/webflow"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow.xsd"
-    parent="oidc/abstract-api, oidc/metadata-lookup">
+    parent="oidc/abstract-api-jwt, oidc/metadata-lookup">
 
     <action-state id="InitializeMandatoryContexts">
         <evaluate expression="InitializeProfileRequestContext" />
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth2/revocation/revocation-beans.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth2/revocation/revocation-beans.xml
index 064193a9..3d93dcf5 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth2/revocation/revocation-beans.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth2/revocation/revocation-beans.xml
@@ -29,21 +29,13 @@
     <bean id="ValidateEndpointAuthentication"
         class="net.shibboleth.idp.plugin.oidc.op.profile.impl.ValidateEndpointAuthentication" scope="prototype"
         p:httpServletRequest-ref="shibboleth.HttpServletRequest" p:replayCache-ref="shibboleth.ReplayCache"
-        p:expiryClaimsValidator-ref="ExpiryClaimsValidator" p:issuedAtClaimsValidator-ref="IssuedAtClaimsValidator">
+        p:claimsValidation-ref="shibboleth.oidc.JWTClaimsValidation">
         <property name="securityParametersLookupStrategy">
             <bean parent="shibboleth.Functions.Compose"
                 c:g-ref="shibboleth.ChildLookup.SecurityParameters"
                 c:f-ref="shibboleth.ChildLookup.RelyingParty" />
         </property>
     </bean>
-    
-    <bean id="ExpiryClaimsValidator"
-        class="net.shibboleth.oidc.security.jwt.claims.impl.ExpiryClaimsValidator"
-        p:clockSkew="%{idp.policy.clockSkew:PT1M}" />
-
-    <bean id="IssuedAtClaimsValidator"
-        class="net.shibboleth.idp.plugin.oidc.op.security.jwt.claims.impl.IssuedAtClaimsValidator"
-        p:messageLifetime="%{idp.policy.messageLifetime:PT1M}" p:clockSkew="%{idp.policy.clockSkew:PT1M}" />
 
     <bean id="RevokeToken" class="net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl.RevokeToken" scope="prototype"
         c:sealer-ref="#{'%{idp.oidc.tokenSealer:shibboleth.oidc.TokenSealer}'.trim()}"
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth2/revocation/revocation-flow.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth2/revocation/revocation-flow.xml
index 4b3edf0e..28c8fe83 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth2/revocation/revocation-flow.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oauth2/revocation/revocation-flow.xml
@@ -1,7 +1,7 @@
 <flow xmlns="http://www.springframework.org/schema/webflow"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow.xsd"
-    parent="oidc/abstract-api, oidc/metadata-lookup">
+    parent="oidc/abstract-api-jwt, oidc/metadata-lookup">
 
     <action-state id="InitializeMandatoryContexts">
         <evaluate expression="InitializeProfileRequestContext" />
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/abstract-api-jwt/oidc-abstract-api-jwt-beans.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/abstract-api-jwt/oidc-abstract-api-jwt-beans.xml
new file mode 100644
index 00000000..190f66fc
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/abstract-api-jwt/oidc-abstract-api-jwt-beans.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans" xmlns:c="http://www.springframework.org/schema/c"
+    xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
+    xmlns:util="http://www.springframework.org/schema/util" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
+                           http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
+    default-init-method="initialize" default-destroy-method="destroy">
+
+    <bean id="shibboleth.oidc.JWTClaimsValidation"
+        class="net.shibboleth.oidc.security.jwt.claims.impl.ChainingJWTClaimsValidation"
+        p:claimValidators-ref="ClaimsValidators" />
+    
+    <bean id="ClientIDFromOIDCMetadataContextLookupFunction"
+        class="net.shibboleth.idp.plugin.oidc.op.profile.logic.ClientIDFromOIDCMetadataContextLookupFunction" />
+
+    <bean id="ExpiryClaimsValidator"
+        class="net.shibboleth.oidc.security.jwt.claims.impl.ExpiryClaimsValidator"
+        p:clockSkew="%{idp.policy.clockSkew:PT1M}" />
+
+    <bean id="IssuedAtClaimsValidator"
+        class="net.shibboleth.idp.plugin.oidc.op.security.jwt.claims.impl.IssuedAtClaimsValidator"
+        p:messageLifetime="%{idp.policy.messageLifetime:PT1M}" p:clockSkew="%{idp.policy.clockSkew:PT1M}"
+        p:requiredRule="false" />
+
+    <bean id="IssuerClaimsValidator"
+        class="net.shibboleth.oidc.security.jwt.claims.impl.ExactMatchClaimsValidator"
+        p:claimName="iss" p:valueToMatchLookupStrategy-ref="ClientIDFromOIDCMetadataContextLookupFunction" />
+
+    <bean id="SubjectClaimsValidator"
+        class="net.shibboleth.oidc.security.jwt.claims.impl.ExactMatchClaimsValidator"
+        p:claimName="sub" p:valueToMatchLookupStrategy-ref="ClientIDFromOIDCMetadataContextLookupFunction" />
+
+    <bean id="AudienceClaimsValidator"
+        class="net.shibboleth.oidc.security.jwt.claims.impl.AudienceClaimsValidator">
+        <property name="audienceLookupStrategy">
+            <bean parent="shibboleth.BiFunctions.Expression"
+                c:expression="#custom.getRequestURL().toString()"
+                p:customObject-ref="shibboleth.HttpServletRequest" />
+        </property>
+    </bean>
+
+    <bean id="JwtIdentifierClaimsValidator"
+        class="net.shibboleth.idp.plugin.oidc.op.security.jwt.claims.impl.JwtIdentifierClaimsValidator"
+        p:clockSkew="%{idp.policy.clockSkew:PT1M}" p:replayCache-ref="shibboleth.ReplayCache" />
+
+    <util:list id="ClaimsValidators" value-type="net.shibboleth.oidc.jwt.claims.ClaimsValidator">
+        <ref bean="ExpiryClaimsValidator" />
+        <ref bean="IssuedAtClaimsValidator" />
+        <ref bean="IssuerClaimsValidator" />
+        <ref bean="SubjectClaimsValidator" />
+        <ref bean="AudienceClaimsValidator" />
+        <ref bean="JwtIdentifierClaimsValidator" />
+    </util:list>
+
+</beans>
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/abstract-api-jwt/oidc-abstract-api-jwt-flow.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/abstract-api-jwt/oidc-abstract-api-jwt-flow.xml
new file mode 100644
index 00000000..ef4f26b2
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/abstract-api-jwt/oidc-abstract-api-jwt-flow.xml
@@ -0,0 +1,11 @@
+<flow xmlns="http://www.springframework.org/schema/webflow" 
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow.xsd"
+    parent="oidc/abstract-api"
+    abstract="true">
+
+    <!--  Actions common to OIDC/OAuth2 profile backend flows involving JWT client authentication. -->
+
+    <bean-import resource="../../oidc/abstract-api-jwt/oidc-abstract-api-jwt-beans.xml" />
+
+</flow>
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-beans.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-beans.xml
index c6d5e264..9d1b98e9 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-beans.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-beans.xml
@@ -27,22 +27,14 @@
     <bean id="ValidateEndpointAuthentication"
         class="net.shibboleth.idp.plugin.oidc.op.profile.impl.ValidateEndpointAuthentication" scope="prototype"
         p:httpServletRequest-ref="shibboleth.HttpServletRequest" p:replayCache-ref="shibboleth.ReplayCache"
-        p:expiryClaimsValidator-ref="ExpiryClaimsValidator" p:issuedAtClaimsValidator-ref="IssuedAtClaimsValidator">
+        p:claimsValidation-ref="shibboleth.oidc.JWTClaimsValidation">
         <property name="securityParametersLookupStrategy">
             <bean parent="shibboleth.Functions.Compose"
                 c:g-ref="shibboleth.ChildLookup.SecurityParameters"
                 c:f-ref="shibboleth.ChildLookup.RelyingParty" />
         </property>
     </bean>
-    
-    <bean id="ExpiryClaimsValidator"
-        class="net.shibboleth.oidc.security.jwt.claims.impl.ExpiryClaimsValidator"
-        p:clockSkew="%{idp.policy.clockSkew:PT1M}" />
-
-    <bean id="IssuedAtClaimsValidator"
-        class="net.shibboleth.idp.plugin.oidc.op.security.jwt.claims.impl.IssuedAtClaimsValidator"
-        p:messageLifetime="%{idp.policy.messageLifetime:PT1M}" p:clockSkew="%{idp.policy.clockSkew:PT1M}" />
-
+        
     <bean id="ValidateGrantType" class="net.shibboleth.idp.plugin.oidc.op.profile.impl.ValidateGrantType"
         scope="prototype" />
 
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-flow.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-flow.xml
index 0202592a..9f31a083 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-flow.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-flow.xml
@@ -1,6 +1,6 @@
 <flow xmlns="http://www.springframework.org/schema/webflow" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow.xsd"
-    parent="oidc/abstract-api, oidc/metadata-lookup, oidc/consent-lookup">
+    parent="oidc/abstract-api-jwt, oidc/metadata-lookup, oidc/consent-lookup">
 
     <action-state id="InitializeMandatoryContexts">
         <evaluate expression="InitializeProfileRequestContext" />
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateEndpointAuthenticationTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateEndpointAuthenticationTest.java
index 377e34f8..18562cf0 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateEndpointAuthenticationTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateEndpointAuthenticationTest.java
@@ -69,12 +69,20 @@ import com.nimbusds.openid.connect.sdk.rp.OIDCClientInformation;
 import com.nimbusds.openid.connect.sdk.rp.OIDCClientMetadata;
 
 import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCMetadataContext;
+import net.shibboleth.idp.plugin.oidc.op.profile.logic.ClientIDFromOIDCMetadataContextLookupFunction;
+import net.shibboleth.idp.plugin.oidc.op.security.jwt.claims.impl.IssuedAtClaimsValidator;
+import net.shibboleth.idp.plugin.oidc.op.security.jwt.claims.impl.JwtIdentifierClaimsValidator;
 import net.shibboleth.idp.profile.context.navigate.AbstractRelyingPartyLookupFunction;
 import net.shibboleth.idp.profile.context.navigate.WebflowRequestContextProfileRequestContextLookup;
 import net.shibboleth.idp.profile.testing.ActionTestingSupport;
 import net.shibboleth.idp.profile.testing.RequestContextBuilder;
+import net.shibboleth.oidc.jwt.claims.JWTClaimsValidation;
 import net.shibboleth.oidc.security.credential.BasicJWKCredential;
 import net.shibboleth.oidc.security.impl.OIDCSignatureValidationParameters;
+import net.shibboleth.oidc.security.jwt.claims.impl.AudienceClaimsValidator;
+import net.shibboleth.oidc.security.jwt.claims.impl.ChainingJWTClaimsValidation;
+import net.shibboleth.oidc.security.jwt.claims.impl.ExactMatchClaimsValidator;
+import net.shibboleth.oidc.security.jwt.claims.impl.ExpiryClaimsValidator;
 import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
 
 /**
@@ -193,11 +201,34 @@ public class ValidateEndpointAuthenticationTest {
         if (newFunction != null) {
             action.setTokenEndpointAuthMethodsLookupStrategy(newFunction);
         }
-        action.setHttpServletRequest(new MockHttpServletRequest());
+        MockHttpServletRequest httpRequest = new MockHttpServletRequest();
+        action.setHttpServletRequest(httpRequest);
+        action.setClaimsValidation(constructClaimsValidation(httpRequest, replayCache));
         action.initialize();
         return action;
     }
     
+    protected JWTClaimsValidation constructClaimsValidation(final MockHttpServletRequest httpRequest,
+            final ReplayCache replayCache) {
+        final ChainingJWTClaimsValidation claimsValidation = new ChainingJWTClaimsValidation();
+        final ExpiryClaimsValidator expValidator = new ExpiryClaimsValidator();
+        final IssuedAtClaimsValidator iatValidator = new IssuedAtClaimsValidator();
+        iatValidator.setRequiredRule(false);
+        final ExactMatchClaimsValidator issValidator = new ExactMatchClaimsValidator();
+        issValidator.setClaimName("iss");
+        issValidator.setValueToMatchLookupStrategy(new ClientIDFromOIDCMetadataContextLookupFunction());
+        final ExactMatchClaimsValidator subValidator = new ExactMatchClaimsValidator();
+        subValidator.setClaimName("sub");
+        subValidator.setValueToMatchLookupStrategy(new ClientIDFromOIDCMetadataContextLookupFunction());
+        final AudienceClaimsValidator audValidator = new AudienceClaimsValidator();
+        audValidator.setAudienceLookupStrategy((prc, claims) -> httpRequest.getRequestURL().toString());
+        final JwtIdentifierClaimsValidator jitValidator = new JwtIdentifierClaimsValidator();
+        jitValidator.setReplayCache(replayCache);
+        claimsValidation.setClaimValidators(List.of(expValidator, iatValidator, issValidator, subValidator,
+                audValidator, jitValidator));
+        return claimsValidation;
+    }
+    
     @Test
     public void testNoEnabledMethods() throws Exception {
         ValidateEndpointAuthentication action = constructAction(null);
@@ -267,55 +298,67 @@ public class ValidateEndpointAuthenticationTest {
     public void testFailingPrivateKeyJwt() throws Exception {
         testFailingClientAuth(ClientAuthenticationMethod.PRIVATE_KEY_JWT);
     }
+
+    @Test
+    public void testInvalidSecretJwt_iatInTheFuture() throws Exception {
+        testFailingJwtAuth(ClientAuthenticationMethod.CLIENT_SECRET_JWT, createSecretJWT(claimsSetWithIatInTheFuture()));
+    }
+
+    @Test
+    public void testInvalidSecretJwt_expInThePast() throws Exception {
+        testFailingJwtAuth(ClientAuthenticationMethod.CLIENT_SECRET_JWT, createSecretJWT(claimsSetWithExpInThePast()));
+    }
+    
+    @Test
+    public void testInvalidSecretJwt_withoutJit() throws Exception {
+        testFailingJwtAuth(ClientAuthenticationMethod.CLIENT_SECRET_JWT, createSecretJWT(claimsSetWithoutJit()));
+    }
     
+    @Test
+    public void testInvalidSecretJwt_jitReplayDetected() throws Exception {
+        testFailingJwtAuth(ClientAuthenticationMethod.CLIENT_SECRET_JWT, createSecretJWT(validClaimsSet()), true);
+    }
+
     @Test
     public void testInvalidPrivateKeyJwt_iatInTheFuture() throws Exception {
-        ClientAuthenticationMethod method = ClientAuthenticationMethod.PRIVATE_KEY_JWT;
-        ValidateEndpointAuthentication action = 
-                constructAction(new ListMethodsFunction(method));
-        final SignedJWT jwt = createPrivateKeyJWT(claimsSetWithIatInTheFuture());
-        final Event event = 
-                action.execute(initializeRequestCtx(
-                        initializeTokenRequest(method, jwt), method, true));
-        ActionTestingSupport.assertEvent(event, EventIds.ACCESS_DENIED);
+        testFailingJwtAuth(ClientAuthenticationMethod.PRIVATE_KEY_JWT, createPrivateKeyJWT(claimsSetWithIatInTheFuture()));
     }
 
     @Test
     public void testInvalidPrivateKeyJwt_expInThePast() throws Exception {
-        ClientAuthenticationMethod method = ClientAuthenticationMethod.PRIVATE_KEY_JWT;
-        ValidateEndpointAuthentication action = 
-                constructAction(new ListMethodsFunction(method));
-        final SignedJWT jwt = createPrivateKeyJWT(claimsSetWithExpInThePast());
-        final Event event = 
-                action.execute(initializeRequestCtx(
-                        initializeTokenRequest(method, jwt), method, true));
-        ActionTestingSupport.assertEvent(event, EventIds.ACCESS_DENIED);
+        testFailingJwtAuth(ClientAuthenticationMethod.PRIVATE_KEY_JWT, createPrivateKeyJWT(claimsSetWithExpInThePast()));
     }
     
     @Test
     public void testInvalidPrivateKeyJwt_withoutJit() throws Exception {
-        ClientAuthenticationMethod method = ClientAuthenticationMethod.PRIVATE_KEY_JWT;
-        ValidateEndpointAuthentication action = 
-                constructAction(new ListMethodsFunction(method));
-        final SignedJWT jwt = createPrivateKeyJWT(claimsSetWithoutJit());
-        final Event event = 
-                action.execute(initializeRequestCtx(
-                        initializeTokenRequest(method, jwt), method, true));
-        ActionTestingSupport.assertEvent(event, EventIds.ACCESS_DENIED);
+        testFailingJwtAuth(ClientAuthenticationMethod.PRIVATE_KEY_JWT, createPrivateKeyJWT(claimsSetWithoutJit()));
     }
     
     @Test
     public void testInvalidPrivateKeyJwt_jitReplayDetected() throws Exception {
-        ClientAuthenticationMethod method = ClientAuthenticationMethod.PRIVATE_KEY_JWT;
+        testFailingJwtAuth(ClientAuthenticationMethod.PRIVATE_KEY_JWT, createPrivateKeyJWT(validClaimsSet()), true);
+    }
+    
+    protected void testFailingJwtAuth(final ClientAuthenticationMethod method, final SignedJWT jwt) throws Exception {
         ValidateEndpointAuthentication action = 
                 constructAction(new ListMethodsFunction(method));
-        final SignedJWT jwt = createPrivateKeyJWT(validClaimsSet());
         final RequestContext requestCtx = initializeRequestCtx(
                 initializeTokenRequest(method, jwt), method, true);
-        Assert.assertNull(action.execute(requestCtx));
         ActionTestingSupport.assertEvent(action.execute(requestCtx), EventIds.ACCESS_DENIED);
     }
-    
+
+    protected void testFailingJwtAuth(final ClientAuthenticationMethod method, final SignedJWT jwt, boolean replay)
+            throws Exception {
+        ValidateEndpointAuthentication action = 
+                constructAction(new ListMethodsFunction(method));
+        final RequestContext requestCtx = initializeRequestCtx(
+                initializeTokenRequest(method, jwt), method, true);
+        if (replay) {
+            Assert.assertNull(action.execute(requestCtx));            
+        }
+        ActionTestingSupport.assertEvent(action.execute(requestCtx), EventIds.ACCESS_DENIED);
+    }
+
     protected JWTClaimsSet claimsSetWithIatInTheFuture() {
         return new JWTClaimsSet.Builder()
                 .subject(clientId.toString())
@@ -358,6 +401,10 @@ public class ValidateEndpointAuthenticationTest {
                 .jwtID("mockId")
                 .build();
     }
+    
+    protected SignedJWT createSecretJWT(final JWTClaimsSet claimsSet) throws JOSEException {
+        return createSecretJWT(claimsSet, clientSecret.getValue());
+    }
 
     protected SignedJWT createSecretJWT(final JWTClaimsSet claimsSet, final String clientSecret)
             throws JOSEException {
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/security/jwt/claims/impl/JwtIdentifierClaimsValidatorTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/security/jwt/claims/impl/JwtIdentifierClaimsValidatorTest.java
new file mode 100644
index 00000000..7925aded
--- /dev/null
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/security/jwt/claims/impl/JwtIdentifierClaimsValidatorTest.java
@@ -0,0 +1,105 @@
+/* 
+ * 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.idp.plugin.oidc.op.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}. 
+ * 
+ * TODO: temporarily here for the OIDC OP 3.0.3 patch. To be included in commons.
+ */
+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