[java-idp-plugin-oidc-rp] branch main updated: JOIDCRP-1 - Convert JWT validation to oidc-common library

Phil Smart philip.smart at jisc.ac.uk
Fri Jun 4 13:35:09 UTC 2021


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

philsmart pushed a commit to branch main
in repository java-idp-plugin-oidc-rp.

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

The following commit(s) were added to refs/heads/main by this push:
       new  6d11d39   JOIDCRP-1 - Convert JWT validation to oidc-common library
6d11d39 is described below

commit 6d11d39608b5668d92ef6cb24789c76341809af6
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Fri Jun 4 14:35:07 2021 +0100

    JOIDCRP-1 - Convert JWT validation to oidc-common library
    
     - Moved over the core logic to odic-commons. Still some validations
    that need to be added.
     - Some of the predicates could be in oidc-commons and not in this
    plugin?
    
    https://issues.shibboleth.net/jira/browse/JOIDCRP-1
---
 .../oidc/rp/AbstractOIDCAuthenticationAction.java  | 149 +++++++++++++++++
 .../idp/plugin/authn/oidc/rp/OIDCRPException.java  |  71 ++++++++
 idp-oidc-rp-impl/pom.xml                           |   5 +
 .../rp/impl/OIDCAuthenticationTimeRequested.java   |  16 ++
 .../OIDCContextAudienceClaimLookupStrategy.java    |  61 +++++++
 .../rp/impl/OIDCIssuerClaimLookupStrategy.java     |  68 ++++++++
 .../oidc/rp/impl/ValidateIDTokenAudience.java      |  91 ----------
 .../rp/impl/ValidateIDTokenAuthenticationTime.java | 185 ---------------------
 .../rp/impl/ValidateIDTokenExpirationTime.java     |  87 ----------
 .../authn/oidc/rp/impl/ValidateIDTokenIssuer.java  |  83 ---------
 .../authn/oidc/rp/impl/ValidateTokenClaims.java    | 164 ++++++++++++++++++
 .../OIDCRelyingParty/oidc-relying-party-beans.xml  |  99 +++++++++--
 .../OIDCRelyingParty/oidc-relying-party-flow.xml   |   7 +-
 13 files changed, 619 insertions(+), 467 deletions(-)

diff --git a/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/AbstractOIDCAuthenticationAction.java b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/AbstractOIDCAuthenticationAction.java
new file mode 100644
index 0000000..1a964e4
--- /dev/null
+++ b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/AbstractOIDCAuthenticationAction.java
@@ -0,0 +1,149 @@
+/*
+ * 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.authn.oidc.rp;
+
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
+import org.opensaml.profile.action.ActionSupport;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.shibboleth.idp.authn.AbstractAuthenticationAction;
+import net.shibboleth.idp.authn.AuthnEventIds;
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.idp.plugin.authn.oidc.rp.context.OpenIDConnectContext;
+import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+
+/**
+ * <p>A base class for OIDC authentication related actions.</p>
+ * 
+ * <p>In addition to the work performed by {@link AbstractAuthenticationAction}, this action also looks up
+ * and makes available the {@link OpenIDConnectContext}.</p>
+ * 
+ * <p>OIDC authentication action implementations should override the
+ * {@link #doExecute(ProfileRequestContext, AuthenticationContext, OpenIDConnectContext)} 
+ * method.</p>
+ * 
+ * @event {@link AuthnEventIds#INVALID_AUTHN_CTX}
+ * @pre <pre>ProfileRequestContext.getSubcontext(AuthenticationContext.class) != null</pre>
+ * @post <pre>AuthenticationContext.getSubcontext(OpenIDConnectContext.class) != null</pre>
+ */
+public abstract class AbstractOIDCAuthenticationAction extends AbstractAuthenticationAction {
+    
+    /** Class logger. */
+    @Nonnull @NotEmpty private final Logger log = LoggerFactory.getLogger(AbstractOIDCAuthenticationAction.class);
+    
+    /** Lookup strategy to locate the OIDC authentication context. */
+    @Nonnull private Function<ProfileRequestContext,OpenIDConnectContext> oidcContextLookupStrategy;
+    
+    /** The OIDC authentication Context.*/
+    @Nullable private OpenIDConnectContext oidcContext;
+        
+    
+    /** Constructor.*/
+    protected AbstractOIDCAuthenticationAction() {
+        //prc -> ac -> oidc_context
+        oidcContextLookupStrategy = new ChildContextLookup<>(OpenIDConnectContext.class).
+                compose(new ChildContextLookup<>(AuthenticationContext.class));
+    }
+    
+    
+    /**
+     * Set OIDC authentication context lookup strategy to use.
+     * 
+     * @param strategy lookup strategy
+     */
+    public void setOIDCContextLookupStrategy(
+            @Nonnull final Function<ProfileRequestContext,OpenIDConnectContext> strategy) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+
+        oidcContextLookupStrategy = Constraint.isNotNull(strategy, "OIDCContextLookup strategy cannot be null");
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    protected final boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext,
+            @Nonnull final AuthenticationContext authenticationContext) {
+
+        if (!super.doPreExecute(profileRequestContext, authenticationContext)) {
+            return false;
+        }
+      
+        oidcContext = oidcContextLookupStrategy.apply(profileRequestContext);
+        if (oidcContext == null) {
+            log.warn("{} No OIDC context returned by lookup strategy",getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.INVALID_AUTHN_CTX);
+            return false;
+            
+        }        
+        
+        return doPreExecute(profileRequestContext, authenticationContext, oidcContext);
+    }
+    
+    /**
+     * Delegates to {@link #doExecute(ProfileRequestContext, AuthenticationContext, 
+     * OpenIDConnectContext)} to perform the actual authentication. Implementations can not 
+     * override this method.
+     * 
+     * @param profileRequestContext the current IdP profile request context
+     * @param authenticationContext the current authentication context
+     */
+    @Override
+    protected final void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
+            @Nonnull final AuthenticationContext authenticationContext) {
+        doExecute(profileRequestContext,authenticationContext,oidcContext);
+    }
+    
+    /**
+     * Performs this authentication action's pre-execute step. Default implementation just returns true.
+     * 
+     * @param profileRequestContext the current IdP profile request context
+     * @param authenticationContext the current authentication context
+     * @param context the OIDC authentication context
+     * 
+     * @return true iff execution should continue
+     */
+    protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext,
+            @Nonnull final AuthenticationContext authenticationContext,
+            @Nonnull final OpenIDConnectContext context) {
+        return true;
+    }
+    
+    /**
+     * Performs this OIDC authentication action using the supplied OIDC context. Implementations
+     * should override this method.
+     * 
+     * @param profileRequestContext the current IdP profile request context
+     * @param authenticationContext the current authentication context
+     * @param context the OIDC authentication context
+     */
+    protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
+            @Nonnull final AuthenticationContext authenticationContext,
+            @Nonnull final OpenIDConnectContext context) {
+        
+    }
+
+}
diff --git a/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/OIDCRPException.java b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/OIDCRPException.java
new file mode 100644
index 0000000..32a33e5
--- /dev/null
+++ b/idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/OIDCRPException.java
@@ -0,0 +1,71 @@
+/*
+ * 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.authn.oidc.rp;
+
+import javax.annotation.concurrent.ThreadSafe;
+
+/** 
+ * An exception to signal a general OIDC RelyingParty error.
+ */
+ at ThreadSafe
+public class OIDCRPException extends Exception{
+
+    /** Serial UID. */
+    private static final long serialVersionUID = -2380145079984333546L;
+
+    /** Constructor. */
+    public OIDCRPException() {
+        super();
+        
+    }
+
+    /**
+     * Constructor.
+     * 
+     * @param message exception message
+     * @param cause exception to be wrapped by this one
+     */
+    public OIDCRPException(final String message, final Throwable cause) {
+        super(message, cause);
+        
+    }
+
+    /**
+     * Constructor.
+     * 
+     * @param message exception message
+     */
+    public OIDCRPException(final String message) {
+        super(message);
+        
+    }
+
+    /**
+     * Constructor.
+     * 
+     * @param cause exception to be wrapped by this one
+     */
+    public OIDCRPException(final Throwable cause) {
+        super(cause);
+        
+    }
+
+
+
+
+}
diff --git a/idp-oidc-rp-impl/pom.xml b/idp-oidc-rp-impl/pom.xml
index 6f2cd13..21c33e4 100644
--- a/idp-oidc-rp-impl/pom.xml
+++ b/idp-oidc-rp-impl/pom.xml
@@ -56,6 +56,11 @@
             <artifactId>oidc-common-crypto-impl</artifactId>
             <scope>provided</scope>
         </dependency>
+        <dependency>
+            <groupId>net.shibboleth.oidc</groupId>
+            <artifactId>oidc-common-crypto-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
         <dependency>
             <groupId>javax.servlet</groupId>
             <artifactId>javax.servlet-api</artifactId>
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCAuthenticationTimeRequested.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCAuthenticationTimeRequested.java
new file mode 100644
index 0000000..54089e1
--- /dev/null
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCAuthenticationTimeRequested.java
@@ -0,0 +1,16 @@
+package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
+
+import java.util.function.Predicate;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+/** Was the authentication_time requested? */
+//TODO well no if you do not complete this predicate.
+public class OIDCAuthenticationTimeRequested implements Predicate<ProfileRequestContext> {
+
+    @Override
+    public boolean test(ProfileRequestContext t) {
+        return false;
+    }
+
+}
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCContextAudienceClaimLookupStrategy.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCContextAudienceClaimLookupStrategy.java
new file mode 100644
index 0000000..ed433e2
--- /dev/null
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCContextAudienceClaimLookupStrategy.java
@@ -0,0 +1,61 @@
+/*
+ * 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.authn.oidc.rp.impl;
+
+import java.util.function.BiFunction;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.ThreadSafe;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import com.nimbusds.jwt.JWTClaimsSet;
+import com.nimbusds.oauth2.sdk.id.ClientID;
+
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.idp.plugin.authn.oidc.rp.context.OpenIDConnectContext;
+
+/**
+ * Looks up the audience from the clientID in the {@link OpenIDConnectContext} inside the auth context.
+ * Returns null if it fails to find the clientID. Used for JWT ID Token audience claims verification.
+ */
+ at ThreadSafe
+public final class OIDCContextAudienceClaimLookupStrategy implements BiFunction<ProfileRequestContext, JWTClaimsSet, String> {
+
+    @Override @Nullable public String apply(@Nonnull final ProfileRequestContext context,
+            @Nonnull final JWTClaimsSet cliams) {
+        
+        final AuthenticationContext authnContext = context.getSubcontext(AuthenticationContext.class);
+        if (authnContext == null) {
+            return null;
+        }
+
+        final OpenIDConnectContext oidcContext = authnContext.getSubcontext(OpenIDConnectContext.class);
+        if (oidcContext == null) {
+            return null;
+        }
+        
+        final ClientID clientObject = oidcContext.getClientID();
+        if (clientObject == null) {
+            return null;
+        }
+        return clientObject.getValue();
+    }
+
+}
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCIssuerClaimLookupStrategy.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCIssuerClaimLookupStrategy.java
new file mode 100644
index 0000000..69364c7
--- /dev/null
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCIssuerClaimLookupStrategy.java
@@ -0,0 +1,68 @@
+/*
+ * 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.authn.oidc.rp.impl;
+
+import java.util.function.BiFunction;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.ThreadSafe;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import com.nimbusds.jwt.JWTClaimsSet;
+import com.nimbusds.oauth2.sdk.id.Issuer;
+import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
+
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.idp.plugin.authn.oidc.rp.context.OpenIDConnectContext;
+
+/** 
+ * Find the issuer from the OIDC metadata inside the {@link OpenIDConnectContext}. Returns null if not found.
+ */
+ at ThreadSafe
+public final class OIDCIssuerClaimLookupStrategy implements BiFunction<ProfileRequestContext, JWTClaimsSet, String> {
+
+    /** {@inheritDoc} */
+    @Override @Nullable public String apply(@Nonnull final ProfileRequestContext context,
+            @Nonnull final JWTClaimsSet cliams) {
+        
+        final AuthenticationContext authnContext = context.getSubcontext(AuthenticationContext.class);
+        if (authnContext == null) {
+            return null;
+        }
+
+        final OpenIDConnectContext oidcContext = authnContext.getSubcontext(OpenIDConnectContext.class);
+        if (oidcContext == null) {
+            return null;
+        }
+        
+        final OIDCProviderMetadata metadata = oidcContext.getoIDCProviderMetadata();
+        if (metadata == null) {
+            return null;
+        }
+        
+        final Issuer issuer = metadata.getIssuer();        
+        if (issuer == null) {
+            return null;
+        }
+        
+        return issuer.getValue();
+    }
+
+}
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ValidateIDTokenAudience.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ValidateIDTokenAudience.java
deleted file mode 100644
index 91cb085..0000000
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ValidateIDTokenAudience.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * 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.authn.oidc.rp.impl;
-
-import java.text.ParseException;
-
-import javax.annotation.Nonnull;
-
-import net.shibboleth.idp.authn.AbstractAuthenticationAction;
-import net.shibboleth.idp.authn.AuthnEventIds;
-import net.shibboleth.idp.authn.context.AuthenticationContext;
-import net.shibboleth.idp.plugin.authn.oidc.rp.context.OpenIDConnectContext;
-
-import org.opensaml.profile.action.ActionSupport;
-import org.opensaml.profile.context.ProfileRequestContext;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-
-/**
- * An action that verifies the Audience (aud) claim in the id_token contains the client_id of this
- * client (as registered at the issuer).  See section 3.1.3.7 of OpenID Connect core 1.0.
- * 
- * <p>If one of the list of audiences is untrusted, the token should be rejected - which is 
- * Currently NOT tested</p>
- * 
- * 
- * @pre <pre>ProfileRequestContext.getSubcontext(AuthenticationContext.class, false) != null</pre>
- * @pre <pre>AuthenticationContext.getSubcontext(OpenIDConnectContext.class, false) != null</pre>
- * @pre <pre>OpenIDConnectContext.getIDToken() != null</pre>
- * @event {@link org.opensaml.profile.action.EventIds#PROCEED_EVENT_ID} 
- * @event {@link net.shibboleth.idp.authn.AuthnEventIds#NO_CREDENTIALS}
- * 
- * @since 4.0.0
- */
-//TODO P.S should some of these functions be delegated to Nimbus IDTokenValidator?
-public class ValidateIDTokenAudience extends AbstractAuthenticationAction {
-
-    /** Class logger. */
-    @Nonnull private final Logger log = LoggerFactory.getLogger(ValidateIDTokenAudience.class);
-
-    /** {@inheritDoc} */
-    @Override
-    protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
-            @Nonnull final AuthenticationContext authenticationContext) {
-        
-
-        final OpenIDConnectContext oidcCtx =
-                authenticationContext.getSubcontext(OpenIDConnectContext.class);
-        if (oidcCtx == null) {
-            log.error("{} Not able to find oidc context", getLogPrefix());
-            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);           
-            return;
-        }
-        if (oidcCtx.getIDToken() == null) {
-            log.error("{} Not able to find id token", getLogPrefix());
-            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);            
-            return;
-        }
-
-        try {
-            //TODO P.S. getClientID could be null, although not after SetOIDCInformation has run.
-            if (!oidcCtx.getIDToken().getJWTClaimsSet().getAudience().contains(oidcCtx.getClientID().getValue())) {
-                log.error("{} client is not the intended audience", getLogPrefix());
-                ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);                
-                return;
-            }
-        } catch (final ParseException e) {
-            log.error("{} Error parsing id token", getLogPrefix());
-            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);            
-            return;
-        }       
-        return;
-    }
-
-}
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ValidateIDTokenAuthenticationTime.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ValidateIDTokenAuthenticationTime.java
deleted file mode 100644
index 260e694..0000000
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ValidateIDTokenAuthenticationTime.java
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * 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.authn.oidc.rp.impl;
-
-import java.text.ParseException;
-import java.util.Date;
-
-import javax.annotation.Nonnull;
-
-import net.shibboleth.idp.authn.AbstractAuthenticationAction;
-import net.shibboleth.idp.authn.AuthnEventIds;
-import net.shibboleth.idp.authn.context.AuthenticationContext;
-import net.shibboleth.idp.plugin.authn.oidc.rp.context.OpenIDConnectContext;
-import net.shibboleth.utilities.java.support.annotation.constraint.NonNegative;
-import net.shibboleth.utilities.java.support.component.ComponentSupport;
-import net.shibboleth.utilities.java.support.logic.Constraint;
-
-import org.joda.time.DateTime;
-import org.opensaml.profile.action.ActionSupport;
-import org.opensaml.profile.context.ProfileRequestContext;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * An action that checks if auth_time (when the End-User authentication took place) stored within the id_token
- * is within a valid expiration window. That is:
- * <ul>
- * <li>The auth_time when the End-User authenticated should not be in the future. More specifically, now plus 
- * some clock skew (default is 3 minutes)</li> * 
- * <li>The authentication and hence id_token has not expired. More specifically, the expiration time 
- * (the auth_time plus some clock skew plus the authnLifetime) should be after the current time.<li> 
- * </ul>
- * 
- * <p>The auth_time is optional, and needs to be requested by a claim, or using the max_age parameter</p>
- * 
- * @pre <pre>ProfileRequestContext.getSubcontext(AuthenticationContext.class, false) != null</pre>
- * @pre <pre>AuthenticationContext.getSubcontext(OpenIDConnectContext.class, false) != null</pre>
- * @pre <pre>OpenIDConnectContext.getIDToken() != null</pre>
- * @event {@link net.shibboleth.idp.authn.AuthnEventIds#NO_CREDENTIALS}
- * @event {@link org.opensaml.profile.action.EventIds#PROCEED_EVENT_ID} 
- * 
- * @since 4.0.0
- */
-//TODO P.S should some of these functions be delegated to Nimbus IDTokenValidator?
-public class ValidateIDTokenAuthenticationTime extends AbstractAuthenticationAction {
-
-    /** Class logger. */
-    @Nonnull private final Logger log = LoggerFactory.getLogger(ValidateIDTokenAuthenticationTime.class);
-
-    /**
-     * Clock skew - milliseconds before a lower time bound, or after an upper time bound, to consider still acceptable
-     * Default value: 3 minutes.
-     */
-    //TODO change these to durations or the JWT verifier
-     @NonNegative private long clockSkew;
-
-    /**
-     * Amount of time in milliseconds for which a forced authentication is valid after it is issued. Default value: 30
-     * seconds.
-     */
-    //TODO P.S is this not just messagelifetime.
-     @NonNegative private long authnLifetime;
-
-    /**
-     * Constructor.
-     */
-    public ValidateIDTokenAuthenticationTime() {
-        super();
-        setClockSkew(60 * 3 * 1000);
-        setAuthnLifetime(30 * 1000);
-    }
-
-    /**
-     * Get the clock skew.
-     * 
-     * @return the clock skew
-     */
-     @NonNegative
-    public long getClockSkew() {
-        return clockSkew;
-    }
-
-    /**
-     * Set the clock skew.
-     * 
-     * @param skew clock skew to set
-     */
-    public void setClockSkew( @NonNegative final long skew) {
-        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-        clockSkew = Constraint.isGreaterThanOrEqual(0, skew, "Clock skew must be greater than or equal to 0");
-    }
-
-    /**
-     * Gets the amount of time, in milliseconds, for which a forced authentication is valid.
-     * 
-     * @return amount of time, in milliseconds, for which a forced authentication is valid
-     */
-     @NonNegative
-    public long getAuthnLifetime() {
-        return authnLifetime;
-    }
-
-    /**
-     * Sets the amount of time, in milliseconds, for which a forced authentication is valid.
-     * 
-     * @param lifetime amount of time, in milliseconds, for which a forced authentication is valid
-     */
-    public synchronized void setAuthnLifetime( @NonNegative final long lifetime) {
-        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-        authnLifetime =
-                Constraint.isGreaterThanOrEqual(0, lifetime, "Authn lifetime must be greater than or equal to 0");
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
-            @Nonnull final AuthenticationContext authenticationContext) {
-        
-
-        if (!authenticationContext.isForceAuthn()) {
-           
-            return;
-        }
-        // If we have forced authentication, we will check for authentication age
-        final OpenIDConnectContext oidcCtx =
-                authenticationContext.getSubcontext(OpenIDConnectContext.class);
-        if (oidcCtx == null) {
-            log.error("{} Not able to find oidc context", getLogPrefix());
-            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);
-            
-            return;
-        }
-        final Date authTimeDate;
-        try {
-            authTimeDate = oidcCtx.getIDToken().getJWTClaimsSet().getDateClaim("auth_time");
-        } catch (final ParseException e) {
-            log.error("{} Error parsing id token", getLogPrefix());
-            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);            
-            return;
-        }
-        if (authTimeDate == null) {
-           // log.error("{} max age set but no auth_time received", getLogPrefix());
-            //ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);        
-            //FIXME WE ARE NOT CHECKING IF WE SET MAX_AGE HERE!!?
-            return;
-        }
-        final DateTime authTime = new DateTime(authTimeDate);
-        final DateTime now = new DateTime();
-        final DateTime latestValid = now.plus(getClockSkew());
-        final DateTime expiration = authTime.plus(getClockSkew() + getAuthnLifetime());
-
-        // Check authentication wasn't performed in the future
-        if (authTime.isAfter(latestValid)) {
-            log.warn("{} Authentication time is not yet valid: time was {}, latest valid is: {}", getLogPrefix(),
-                    authTime, latestValid);
-            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);           
-            return;
-        }
-
-        // Check authentication time has not expired
-        if (expiration.isBefore(now)) {
-            log.warn("{} Authentication time has expired: time was '{}', expired at: '{}', current time: '{}'",
-                    getLogPrefix(), authTime, expiration, now);
-            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);            
-            return;
-        }
-        
-    }
-
-}
\ No newline at end of file
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ValidateIDTokenExpirationTime.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ValidateIDTokenExpirationTime.java
deleted file mode 100644
index b5c69f6..0000000
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ValidateIDTokenExpirationTime.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * 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.authn.oidc.rp.impl;
-
-import java.util.Date;
-
-import javax.annotation.Nonnull;
-
-import net.shibboleth.idp.authn.AbstractAuthenticationAction;
-import net.shibboleth.idp.authn.AuthnEventIds;
-import net.shibboleth.idp.authn.context.AuthenticationContext;
-import net.shibboleth.idp.plugin.authn.oidc.rp.context.OpenIDConnectContext;
-
-import org.opensaml.profile.action.ActionSupport;
-import org.opensaml.profile.context.ProfileRequestContext;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * An action that verifies the expiration time (exp) of an id_token. If the expiration time has past
- * the id_token must not be accepted.
- * 
- * <p>Some clock skew could be specified, but is not in this implementation</p>
- * 
- * <p>Expiration time of the id_token is required. If not present, AuthnEventIds#NO_CREDENTIALS is returned</p>
- * 
- *
- * @event {@link org.opensaml.profile.action.EventIds#PROCEED_EVENT_ID}
- * @event {@link AuthnEventIds#NO_CREDENTIALS}
- * @pre <pre>ProfileRequestContext.getSubcontext(AuthenticationContext.class, false) != null</pre>
- * @pre <pre>AuthenticationContext.getSubcontext(OpenIDConnectContext.class, false) != null</pre>
- * @pre <pre>OpenIdConnectContext.getOidcTokenResponse() != null</pre>
- * 
- * @since 4.0.0
- */
-public class ValidateIDTokenExpirationTime extends AbstractAuthenticationAction {
-
-    /** Class logger. */
-    @Nonnull private final Logger log = LoggerFactory.getLogger(ValidateIDTokenExpirationTime.class);
-
-    /** {@inheritDoc} */
-    @Override
-    protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
-            @Nonnull final AuthenticationContext authenticationContext) {
-        
-
-        final OpenIDConnectContext oidcCtx =
-                authenticationContext.getSubcontext(OpenIDConnectContext.class);
-        if (oidcCtx == null) {
-            log.error("{} Unable to find oidc context", getLogPrefix());
-            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);            
-            return;
-        }
-
-        // The current time MUST be before the time represented by the expiration time of the token
-        final Date currentDate = new Date();
-        try {
-            final Date expDate = oidcCtx.getIDToken().getJWTClaimsSet().getExpirationTime();
-            if (currentDate.after(expDate)) {
-                log.error("{} Current date {} is past exp date {}", getLogPrefix(), currentDate, expDate);
-                ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);                
-                return;
-            }
-        } catch (final java.text.ParseException | NullPointerException e) {
-            log.error("{} Error parsing id token", getLogPrefix(), e);
-            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);            
-            return;
-        }        
-        return;
-    }
-
-}
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ValidateIDTokenIssuer.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ValidateIDTokenIssuer.java
deleted file mode 100644
index 1f21a4b..0000000
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ValidateIDTokenIssuer.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * 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.authn.oidc.rp.impl;
-
-import java.text.ParseException;
-
-import javax.annotation.Nonnull;
-
-import net.shibboleth.idp.authn.AbstractAuthenticationAction;
-import net.shibboleth.idp.authn.AuthnEventIds;
-import net.shibboleth.idp.authn.context.AuthenticationContext;
-import net.shibboleth.idp.plugin.authn.oidc.rp.context.OpenIDConnectContext;
-
-import org.opensaml.profile.action.ActionSupport;
-import org.opensaml.profile.context.ProfileRequestContext;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * An action that verifies the issuer (iss) of the id_token exactly matches that of the configured 
- * OpenID Connect Provider.
- * 
- * @event {@link org.opensaml.profile.action.EventIds#PROCEED_EVENT_ID}
- * @event {@link AuthnEventIds#NO_CREDENTIALS} 
- * @pre <pre>ProfileRequestContext.getSubcontext(AuthenticationContext.class, false) != null</pre>
- * @pre <pre>AuthenticationContext.getSubcontext(OpenIDConnectContext.class, false) != null</pre>
- * @pre <pre>OpenIdConnectContext.getOidcTokenResponse() != null</pre>
- * @pre <pre>OpenIdConnectContext.getoIDCProviderMetadata() != null</pre>
- * 
- * @since 4.0.0
- */
-public class ValidateIDTokenIssuer extends AbstractAuthenticationAction {
-
-    /** Class logger. */
-    @Nonnull private final Logger log = LoggerFactory.getLogger(ValidateIDTokenIssuer.class);
-
-    /** {@inheritDoc} */
-    @Override
-    protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
-            @Nonnull final AuthenticationContext authenticationContext) {
-        
-
-        final OpenIDConnectContext oidcCtx =
-                authenticationContext.getSubcontext(OpenIDConnectContext.class);
-        if (oidcCtx == null) {
-            log.error("{} Unable to find oidc context", getLogPrefix());
-            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);            
-            return;
-        }
-
-        final String issuer = oidcCtx.getoIDCProviderMetadata().getIssuer().getValue();
-
-        try {
-            if (!issuer.equals(oidcCtx.getIDToken().getJWTClaimsSet().getIssuer())) {
-                log.error("{} Issuer mismatch", getLogPrefix());
-                ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);               
-                return;
-            }
-
-        } catch (final ParseException e) {
-            log.error("{} Unable to parse oidc token", getLogPrefix());
-            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);            
-            return;
-        }
-        
-        return;
-    }
-}
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ValidateTokenClaims.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ValidateTokenClaims.java
new file mode 100644
index 0000000..3b4d560
--- /dev/null
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ValidateTokenClaims.java
@@ -0,0 +1,164 @@
+package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
+
+import java.text.ParseException;
+import java.util.function.Consumer;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.action.ActionSupport;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.nimbusds.jwt.JWT;
+import com.nimbusds.jwt.JWTClaimsSet;
+
+import net.shibboleth.idp.authn.AuthnEventIds;
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.idp.plugin.authn.oidc.rp.AbstractOIDCAuthenticationAction;
+import net.shibboleth.idp.plugin.authn.oidc.rp.OIDCRPException;
+import net.shibboleth.idp.plugin.authn.oidc.rp.context.OpenIDConnectContext;
+import net.shibboleth.oidc.jwt.claims.JWTClaimsValidation;
+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;
+
+/**
+ * Action that validates the claims of the id_token using the supplied 
+ * {@link JWTClaimsValidation claims validator}. The verifier <b>must</b> be thread-safe and validate, at
+ * minimum the claims set against the OpenID Connect core 1.0 section 3.1.3.7 specification. 
+ * 
+ * TODO: check these conditions
+ * 
+ * @pre
+ * 
+ *      <pre>
+ *      ProfileRequestContext.getSubcontext(AuthenticationContext.class, false) != null
+ *      </pre>
+ * 
+ * @pre
+ * 
+ *      <pre>
+ *      AuthenticationContext.getSubcontext(DuoOIDCAuthenticationContext.class, false) != null
+ *      </pre>
+ * 
+ * @pre
+ * 
+ *      <pre>
+ *      DuoOIDCAuthenticationContext.getAuthToken() != null
+ *      </pre>
+ * @pre
+ * 
+ *      <pre>
+ *      DuoOIDCAuthenticationContext.getIntegration() != null
+ *      </pre>
+ * 
+ * @event {@link org.opensaml.profile.action.EventIds#PROCEED_EVENT_ID}
+ * @event {@link net.shibboleth.idp.authn.AuthnEventIds#AUTHN_EXCEPTION}
+ * @event {@link net.shibboleth.idp.authn.AuthnEventIds#NO_CREDENTIALS}
+ */
+public class ValidateTokenClaims extends AbstractOIDCAuthenticationAction {
+    
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(ValidateTokenClaims.class);
+    
+    /** A cleanup hook to execute after either a successful or unsuccessful claims validation. */
+    @Nullable private Consumer<ProfileRequestContext> cleanupHook;
+    
+    /** The parsed claimset. */
+    @Nullable private JWTClaimsSet claimsSet;
+    
+    /** The JWT claims validator used to verify the claimsset.*/
+    @NonnullAfterInit private JWTClaimsValidation claimsValidator;
+    
+    /** {@inheritDoc} */
+    @Override protected void doInitialize() throws ComponentInitializationException {
+        super.doInitialize();
+
+        if (claimsValidator ==  null) {
+            throw new ComponentInitializationException("JWT ClaimSet Validator cannot be null");
+        }
+    }
+
+    /**
+     * Set the cleanup hook to execute after either a successful or unsuccessful claims validation.
+     * 
+     * @param hook cleanup hook
+     * 
+     */
+    public void setCleanupHook(@Nullable final Consumer<ProfileRequestContext> hook) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+        
+        cleanupHook = hook;
+    }
+    
+    /**
+     * Set the JWT claims verifier to use.
+     * 
+     * @param validator the claims validator.
+     */
+    public void setClaimsValidator(
+            @Nonnull final JWTClaimsValidation validator) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+        
+        claimsValidator = Constraint.isNotNull(validator, "Claims validator cannot be null");
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext,
+            @Nonnull final AuthenticationContext authenticationContext,
+            @Nonnull final OpenIDConnectContext oidcContext) {
+
+        final JWT token = oidcContext.getIDToken();
+        if (token == null) {
+            log.error("{} id_token token is not available", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.INVALID_AUTHN_CTX);
+            return false;
+        }
+        try {
+            //parse the claimset here, so parsing only has to happen once, and we fail fast on error (e.g. bad JSON)
+            claimsSet = token.getJWTClaimsSet();
+            if (claimsSet == null) {
+                throw new OIDCRPException("JWT ClaimsSet is null");
+            }
+        } catch (final ParseException | OIDCRPException e) {
+            log.error("{} Claimset of id_token is not available", getLogPrefix(),e);
+            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.INVALID_AUTHN_CTX);
+            return false;
+        }        
+        return true;
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
+            @Nonnull final AuthenticationContext authenticationContext,
+            @Nonnull final OpenIDConnectContext oidcContext) {
+
+        log.debug("{} Validating token claims for subject '{}'",getLogPrefix(),claimsSet.getSubject());
+         
+        try {
+            claimsValidator.validate(claimsSet,profileRequestContext);
+            if (cleanupHook != null) {
+                cleanupHook.accept(profileRequestContext);
+            }
+        } catch (final JWTValidationException e) {
+            log.error("{} Token verification failed for subject '{}'", getLogPrefix(),claimsSet.getSubject(),e);
+            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);
+            if (cleanupHook != null) {
+                cleanupHook.accept(profileRequestContext);
+            }
+            return;
+        }
+        //fine.
+        log.debug("{} Token claims are valid for subject '{}'",getLogPrefix(),claimsSet.getSubject());
+    }
+    
+
+}
diff --git a/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-beans.xml b/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-beans.xml
index cb9040b..953fd9d 100644
--- a/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-beans.xml
+++ b/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-beans.xml
@@ -51,21 +51,6 @@
         class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.ValidateOIDCAuthenticationResponse"
         p:httpServletRequest-ref="shibboleth.HttpServletRequest" />
 
-    <bean id="ValidateIDTokenACR" class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.ValidateIDTokenACR" />
-
-    <bean id="ValidateIDTokenAudience" class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.ValidateIDTokenAudience" />
-
-    <bean id="ValidateIDTokenAuthenticationTime"
-        class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.ValidateIDTokenAuthenticationTime" />
-
-    <bean id="ValidateIDTokenAuthorizedParty"
-        class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.ValidateIDTokenAuthorizedParty" />
-
-    <bean id="ValidateIDTokenExpirationTime"
-        class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.ValidateIDTokenExpirationTime" />
-
-    <bean id="ValidateIDTokenIssuer" class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.ValidateIDTokenIssuer" />
-
     <bean id="ValidateIDTokenSignature"
         class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.ValidateIDTokenSignature" />
 
@@ -73,7 +58,91 @@
 
     <bean id="GetOIDCTokenResponse" class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.GetOIDCTokenResponse"
         p:httpServletRequest-ref="shibboleth.HttpServletRequest" />
+        
+    <bean id="ValidateTokenClaims" scope="prototype"
+	   class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.ValidateTokenClaims"
+	   p:cleanupHook="#{getObject('shibboleth.authn.oidc.rp.jwt.claims.CleanUpHook') 
+	       ?: getObject('shibboleth.authn.oidc.rp.jwt.claims.DefaultCleanupHook')}"
+	   p:claimsValidator="#{getObject('shibboleth.authn.oidc.rp.DuoTokenClaimsVerifier') 
+           ?: getObject('shibboleth.authn.oidc.rp.DefaultDuoTokenClaimsVerifier')}" />
+    
+    <!-- TODO ensure these claims are correct in the general OIDC case. -->
+     <!-- OIDC claims verification Other claim verifications e.g. ACR and AZP-->
+    <bean id="shibboleth.authn.oidc.rp.DefaultDuoTokenClaimsVerifier"
+        class="net.shibboleth.oidc.security.jwt.claims.impl.ChainingJWTClaimsValidation">
+        <property name="claimValidators">
+            <list>
+            <bean id="requiredClaimsValidator" 
+                class="net.shibboleth.oidc.security.jwt.claims.impl.RequiredClaimsValidator"
+                p:requiredClaims="#{getObject('shibboleth.authn.oidc.rp.RequiredOIDCClaims') ?: 
+                                getObject('shibboleth.authn.oidc.rp.DefaultRequiredOIDCClaims')}"/>
+            <bean id="issuerClaimValidator" 
+                class="net.shibboleth.oidc.security.jwt.claims.impl.ExactMatchClaimsValidator"
+                p:claimName="#{T(net.shibboleth.oidc.security.jwt.claims.impl.JWTClaims).ISSUER_CLAIM.claimName}"
+                p:valueToMatchLookupStrategy="#{getObject('shibboleth.authn.oidc.rp.jwt.IssuerLookupStrategy') ?: 
+                                getObject('shibboleth.authn.oidc.rp.jwt.DefaultIssuerLookupStrategy')}"/>              
+            <bean id="audienceClaimValidator" 
+                class="net.shibboleth.oidc.security.jwt.claims.impl.AudienceClaimsValidator"
+                p:audienceLookupStrategy="#{getObject('shibboleth.authn.oidc.rp.jwt.AudienceLookupStrategy') ?: 
+                                getObject('shibboleth.authn.oidc.rp.jwt.DefaultAudienceLookupStrategy')}"/>
+            <bean id="notBeforeClaimValidator" 
+                class="net.shibboleth.oidc.security.jwt.claims.impl.NotBeforeClaimsValidator"
+                p:clockSkew="%{idp.duo.oidc.jwt.verifier.clockSkew:PT60S}"/>
+            <bean id="expiryClaimValidator" 
+                class="net.shibboleth.oidc.security.jwt.claims.impl.ExpiryClaimsValidator"
+                p:clockSkew="%{idp.duo.oidc.jwt.verifier.clockSkew:PT60S}"/>
+             <bean id="issuedAtClaimValidator" 
+                class="net.shibboleth.oidc.security.jwt.claims.impl.IssuedAtClaimsValidator"
+                p:iatWindow="%{idp.duo.oidc.jwt.verifier.iatWindow:PT60S}"/>
+            
+             <bean id="authenticationTimeClaimValidator" 
+                class="net.shibboleth.oidc.security.jwt.claims.impl.AuthenticationTimeClaimsValidator"
+                p:authnLifetime="%{idp.duo.oidc.jwt.verifier.authLifetime:PT60S}"
+                p:clockSkew="%{idp.duo.oidc.jwt.verifier.clockSkew:PT60S}"
+                p:activationCondition="#{getObject('shibboleth.authn.oidc.rp.jwt.AuthTimeActivationCondition') ?: 
+                                getObject('shibboleth.authn.oidc.rp.jwt.DefaultAuthTimeActivationCondition')}"
+                p:requested="#{getObject('shibboleth.authn.oidc.rp.jwt.AuthTimeRequested') ?: 
+                                getObject('shibboleth.authn.oidc.rp.jwt.DefaultAuthTimeRequested')}"/>
+             <!-- <bean id="nonceClaimValidator" 
+                class="net.shibboleth.oidc.security.jwt.claims.impl.ExactMatchClaimsValidator"
+                p:claimName="nonce"
+                p:valueToMatchLookupStrategy="#{getObject('shibboleth.authn.oidc.rp.jwt.NonceLookupStrategy') ?: 
+                                getObject('shibboleth.authn.oidc.rp.jwt.DefaultNonceLookupStrategy')}"
+                p:activationCondition="#{getObject('shibboleth.authn.oidc.rp.jwt.NonceActivationCondition') ?: 
+                                getObject('shibboleth.authn.oidc.rp.jwt.DefaultNonceActivationCondition')}"/> -->
+             <bean id="functionClaimValidator"
+                class="net.shibboleth.oidc.security.jwt.claims.impl.FunctionClaimsValidator"
+                p:validator="#{getObject('shibboleth.authn.oidc.rp.ExtendedClaimsValidator')}"/>
+            </list>
+        </property>    
+    </bean> 
     
+    <bean id="shibboleth.authn.oidc.rp.jwt.DefaultAuthTimeActivationCondition"
+        class="net.shibboleth.oidc.security.jwt.claims.impl.ForcedAuthenticationActivationCondition"/>
 
+    <bean id="shibboleth.authn.oidc.rp.jwt.DefaultAuthTimeRequested"
+        class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.OIDCAuthenticationTimeRequested"/>
+    
+    <bean id="shibboleth.authn.oidc.rp.jwt.DefaultNonceActivationCondition"
+        class="net.shibboleth.oidc.security.jwt.claims.impl.NonceValidationActiviationCondition"/> 
+    
+    <bean id="shibboleth.authn.oidc.rp.jwt.DefaultIssuerLookupStrategy"
+        class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.OIDCIssuerClaimLookupStrategy"/>
+ <!--        
+    <bean id="shibboleth.authn.oidc.rp.jwt.DefaultNonceLookupStrategy"
+        class="net.shibboleth.idp.plugin.authn.duo.impl.DuoNonceClaimLookupStrategy"/>    
+     -->
+    <bean id="shibboleth.authn.oidc.rp.jwt.DefaultAudienceLookupStrategy" 
+        class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.OIDCContextAudienceClaimLookupStrategy"/>    
+     
+    <!-- These represent the default set of id_token claims which are **required** by OIDC
+    https://openid.net/specs/openid-connect-core-1_0.html#IDToken -->   
+    <util:set id="shibboleth.authn.oidc.rp.DefaultRequiredOIDCClaims">
+        <value>iss</value>
+        <value>sub</value>
+        <value>aud</value>
+        <value>exp</value>
+        <value>iat</value>
+    </util:set>
     
 </beans>
diff --git a/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-flow.xml b/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-flow.xml
index eaa7e9b..76ec7df 100644
--- a/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-flow.xml
+++ b/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-flow.xml
@@ -37,13 +37,8 @@
     </action-state>
 
     <action-state id="ValidateOIDCTokenResponse">
-        <evaluate expression="ValidateIDTokenACR" />
-        <evaluate expression="ValidateIDTokenAudience" />
-        <evaluate expression="ValidateIDTokenAuthorizedParty" />
-        <evaluate expression="ValidateIDTokenExpirationTime" />
-        <evaluate expression="ValidateIDTokenIssuer" />
         <evaluate expression="ValidateIDTokenSignature" />
-        <evaluate expression="ValidateIDTokenAuthenticationTime" />
+        <evaluate expression="ValidateTokenClaims" />        
         <evaluate expression="'proceed'" />
         <transition on="proceed" to="SetPrincipal" />
     </action-state>

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


More information about the commits mailing list