[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