[java-idp-plugin-oidc-rp] branch main updated: Add nonce id_token validation if nonce present in authentication request

Phil Smart philip.smart at jisc.ac.uk
Thu Feb 3 14:01:25 UTC 2022


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=e56b159d48b0ef135eb0073abdd7a17ad38573d3

The following commit(s) were added to refs/heads/main by this push:
     new e56b159  Add nonce id_token validation if nonce present in authentication request
e56b159 is described below

commit e56b159d48b0ef135eb0073abdd7a17ad38573d3
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Thu Feb 3 14:01:20 2022 +0000

    Add nonce id_token validation if nonce present in authentication request
---
 ...henticationRequestNonceClaimLookupStrategy.java | 81 ++++++++++++++++++++++
 .../impl/NonceValidationActivationCondition.java   | 80 +++++++++++++++++++++
 .../OIDCContextAudienceClaimLookupStrategy.java    | 62 -----------------
 .../rp/impl/OIDCIssuerClaimLookupStrategy.java     | 68 ------------------
 .../oidc-relying-party-authn-beans.xml             | 45 ++++++------
 .../plugin/authn/oidc/rp/impl/OIDCRPFlowTest.java  |  3 +
 6 files changed, 188 insertions(+), 151 deletions(-)

diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AuthenticationRequestNonceClaimLookupStrategy.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AuthenticationRequestNonceClaimLookupStrategy.java
new file mode 100644
index 0000000..7f38e72
--- /dev/null
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AuthenticationRequestNonceClaimLookupStrategy.java
@@ -0,0 +1,81 @@
+/*
+ * 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 java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.ThreadSafe;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import com.nimbusds.jwt.JWTClaimsSet;
+
+import net.shibboleth.idp.authn.duo.context.DuoAuthenticationContext;
+import net.shibboleth.oidc.profile.core.OIDCAuthenticationRequest;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+
+/** 
+ * Find the nonce from the {@link DuoAuthenticationContext}. Returns null if not found.
+ */
+ at ThreadSafe
+public final class AuthenticationRequestNonceClaimLookupStrategy 
+                        implements BiFunction<ProfileRequestContext,JWTClaimsSet, String> {
+    
+    /** The strategy used to lookup the authentication request.*/
+    @Nonnull 
+    private final Function<ProfileRequestContext, OIDCAuthenticationRequest> authenticationRequestLookupStrategy; 
+    
+    /**
+     * 
+     * Constructor.
+     *
+     * @param strategy the lookup strategy to use
+     */
+    public AuthenticationRequestNonceClaimLookupStrategy(
+            @Nonnull final Function<ProfileRequestContext, OIDCAuthenticationRequest> strategy) {
+        authenticationRequestLookupStrategy = 
+                Constraint.isNotNull(strategy, "Authentication request lookup strategy can not be null");
+    }
+    
+    /** Constructor.*/
+    public AuthenticationRequestNonceClaimLookupStrategy() {
+        authenticationRequestLookupStrategy = prc -> {
+            final MessageContext messageContext = prc.getOutboundMessageContext();
+            if (messageContext != null && 
+                    messageContext.getMessage() instanceof OIDCAuthenticationRequest) {
+                return (OIDCAuthenticationRequest)messageContext.getMessage();
+            }
+            return null;
+        };
+    }
+
+    @Override @Nullable public String apply(@Nonnull final ProfileRequestContext context,
+            @Nonnull final JWTClaimsSet cliams) {
+        
+        final OIDCAuthenticationRequest request = authenticationRequestLookupStrategy.apply(context);
+        if (request == null) {
+            return null;
+        }
+        return request.getNonce().getValue();
+    }
+
+}
diff --git a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/NonceValidationActivationCondition.java b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/NonceValidationActivationCondition.java
new file mode 100644
index 0000000..5329631
--- /dev/null
+++ b/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/NonceValidationActivationCondition.java
@@ -0,0 +1,80 @@
+/*
+ * 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.BiPredicate;
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.concurrent.ThreadSafe;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import com.nimbusds.jwt.JWTClaimsSet;
+
+import net.shibboleth.oidc.profile.core.OIDCAuthenticationRequest;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+
+/**
+ * Determines if nonce validation should occur. True iff a nonce is present in the authentication request, 
+ * false otherwise see section 3.1.3.7 of the OpenID Connect core 1.0 specification.
+ */
+ at ThreadSafe
+public class NonceValidationActivationCondition implements BiPredicate<ProfileRequestContext, JWTClaimsSet>{
+    
+    /** The strategy used to lookup the authentication request.*/
+    @Nonnull 
+    private final Function<ProfileRequestContext, OIDCAuthenticationRequest> authenticationRequestLookupStrategy; 
+    
+    /**
+     * 
+     * Constructor.
+     *
+     * @param strategy the lookup strategy to use
+     */
+    public NonceValidationActivationCondition(
+            @Nonnull final Function<ProfileRequestContext, OIDCAuthenticationRequest> strategy) {
+        authenticationRequestLookupStrategy = 
+                Constraint.isNotNull(strategy, "Authentication request lookup strategy can not be null");
+    }
+    
+    /** Constructor.*/
+    public NonceValidationActivationCondition() {
+        // default is to pull out of the outbound message context
+        authenticationRequestLookupStrategy = prc -> {
+            final MessageContext messageContext = prc.getOutboundMessageContext();
+            if (messageContext != null && 
+                    messageContext.getMessage() instanceof OIDCAuthenticationRequest) {
+                return (OIDCAuthenticationRequest)messageContext.getMessage();
+            }
+            return null;
+        };
+    }
+
+    @Override
+    public boolean test(@Nonnull final ProfileRequestContext context, @Nonnull final JWTClaimsSet claims) {
+
+        final OIDCAuthenticationRequest request = authenticationRequestLookupStrategy.apply(context);
+        if (request == null) {
+            return false;
+        }
+        return request.getNonce() != null;
+    }
+
+}
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
deleted file mode 100644
index c9069cc..0000000
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCContextAudienceClaimLookupStrategy.java
+++ /dev/null
@@ -1,62 +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.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
deleted file mode 100644
index 69364c7..0000000
--- a/idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCIssuerClaimLookupStrategy.java
+++ /dev/null
@@ -1,68 +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.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/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-beans.xml b/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-beans.xml
index e78d544..a8f2a01 100644
--- a/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-beans.xml
+++ b/idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-authn-beans.xml
@@ -366,18 +366,35 @@
         </property>
     </bean>
     
+    <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="shibboleth.authn.oidc.rp.jwt.DefaultNonceActivationCondition" 
+        class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.NonceValidationActivationCondition"/>
+        
+    <bean id="shibboleth.authn.oidc.rp.jwt.DefaultNonceLookupStrategy" 
+        class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.AuthenticationRequestNonceClaimLookupStrategy"/>
+    
+    
     <bean id="OIDCMetadataContextChildLookup"
         class="org.opensaml.messaging.context.navigate.ChildContextLookup"
         c:type="#{ T(net.shibboleth.oidc.metadata.context.OIDCMetadataContext) }" />
     
     <util:list id="ClaimsValidators" value-type="net.shibboleth.oidc.jwt.claims.ClaimsValidator">
-        <ref bean="ExpiryClaimsValidator" />
-        <ref bean="NotBeforeClaimsValidator" />
+        <ref bean="IssuerClaimsValidator" /> <!-- TODO prevent: if it contains additional audiences not trusted by the Client. -->
+        <ref bean="AudienceClaimsValidator" /> 
         <ref bean="AzpClaimRequiredValidator"/>
-        <ref bean="AzpClaimsValidator"/>
+        <ref bean="AzpClaimsValidator"/>       
+        <ref bean="ExpiryClaimsValidator" />
         <ref bean="IssuedAtClaimsValidator" /> 
-        <ref bean="IssuerClaimsValidator" /> <!-- TODO prevent: if it contains additional audiences not trusted by the Client. -->
-        <ref bean="AudienceClaimsValidator" />
+        <ref bean="NotBeforeClaimsValidator" />
+        <ref bean="nonceClaimValidator" />
+        <!-- missing ACR? nonce, and auth_time, access_token at_hash. -->
     </util:list> 
     
    
@@ -396,7 +413,7 @@
     </bean>
     
     <!-- Populate RP UI info from metadata? -->
-    <util:constant id="shibboleth.authn.oidc.rp.populateUIInfo" static-field="java.lang.Boolean.FALSE" />
+
 
     <bean id="ValidateIDTokenSignature"
         class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.ValidateIDTokenSignature" />
@@ -407,21 +424,7 @@
 
     <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.NonceValidationActivationCondition"/> 
-    
-    <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 -->   
diff --git a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowTest.java b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowTest.java
index c78dd11..ca6bd64 100644
--- a/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowTest.java
+++ b/idp-oidc-rp-impl/src/test/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/OIDCRPFlowTest.java
@@ -65,6 +65,7 @@ import com.nimbusds.oauth2.sdk.ResponseType;
 import com.nimbusds.oauth2.sdk.id.ClientID;
 import com.nimbusds.oauth2.sdk.id.State;
 import com.nimbusds.openid.connect.sdk.AuthenticationResponseParser;
+import com.nimbusds.openid.connect.sdk.Nonce;
 import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
 import com.nimbusds.openid.connect.sdk.rp.OIDCClientInformation;
 
@@ -282,6 +283,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
                 .issuer("https://op.example.com")
                 .audience(List.of("demo_rp","demo_rp2"))
                 .subject("jdoe")
+                .claim("nonce", "abadnonce")
                 .claim("azp", "demo_rp")
                 .expirationTime(Date.from(Instant.now().plusSeconds(120)))
                 .build();
@@ -370,6 +372,7 @@ public class OIDCRPFlowTest extends AbstractAuthnXmlFlowExecutionTests {
         final MessageContext outMsgCtx = new MessageContext();
         final OIDCAuthenticationRequest request = new OIDCAuthenticationRequest(new ClientID("https://op.example.com"));
         request.setState(new State("8df98fd63a53fa5b5433d6f8754bca5d.65317332"));
+        request.setNonce(new Nonce("abadnonce"));
         outMsgCtx.setMessage(request);
         
         final OIDCPeerEntityContext peerCtx = new OIDCPeerEntityContext();

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


More information about the commits mailing list