[java-idp-oidc] 09/35: JOIDC-200 - Support for OAuth2 Pushed Authorization Requests (PAR)

Henri Mikkonen henri.mikkonen at iki.fi
Thu Oct 24 14:01:34 UTC 2024


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

hjmikkon pushed a commit to branch dev/JOIDC-222
in repository java-idp-oidc.

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

commit 515e3a0fc3f4e5e0d59fd79c5e02fe89e5b8be7d
Author: Henri Mikkonen <henri.mikkonen at iki.fi>
AuthorDate: Fri Sep 13 13:04:58 2024 +0300

    JOIDC-200 - Support for OAuth2 Pushed Authorization Requests (PAR)
    
    https://shibboleth.atlassian.net/browse/JOIDC-200
    
    - Changed OIDCAuthenticationResponseContext.requestObjectValidated into requestObjectFromPar to be more explicit
      - The purpose and use of the flag is meant for signalling that the request object is built by OP/AS itself in PAR, and thus doesn't need to be validated in the same way as client-provided request object
    - Modified ValidateRequestObject to compare client_id values also with PAR cases
---
 .../context/OIDCAuthenticationResponseContext.java | 22 +++---
 .../impl/SetRequestObjectToResponseContext.java    |  2 +-
 .../oauth2/profile/impl/ValidateRequestObject.java | 81 ++++++++++++----------
 .../idp/flows/oidc/authorize/authorize-beans.xml   |  4 +-
 4 files changed, 59 insertions(+), 50 deletions(-)

diff --git a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/messaging/context/OIDCAuthenticationResponseContext.java b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/messaging/context/OIDCAuthenticationResponseContext.java
index 3a9d5680..565a4d76 100644
--- a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/messaging/context/OIDCAuthenticationResponseContext.java
+++ b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/messaging/context/OIDCAuthenticationResponseContext.java
@@ -128,10 +128,10 @@ public class OIDCAuthenticationResponseContext extends BaseContext {
     /** Session identifier. */
     @Nullable private String sessionId;
 
-    /** Whether request object has already been validated. */
-    private boolean requestObjectValidated = false;
+    /** Whether request object has been provided from the PAR endooint. */
+    private boolean requestObjectFromPar = false;
 
-    /** Whether request object valiadtion has failed. */
+    /** Whether request object validation has failed. */
     private boolean requestObjectFailure = false;
 
     /** DPoP Proof JWK thumbprint. */
@@ -570,25 +570,25 @@ public class OIDCAuthenticationResponseContext extends BaseContext {
     }
 
     /**
-     * Get whether request object has already been validated.
+     * Get whether request object has been provided from the PAR endooint.
      * 
-     * @return true if validated, false if not
+     * @return true if from the PAR endpoint, false if not
      * 
      * @since 4.2.0
      */
-    public boolean isRequestObjectValidated() {
-        return requestObjectValidated;
+    public boolean isRequestObjectFromPar() {
+        return requestObjectFromPar;
     }
 
     /**
-     * Set whether request object has already been validated.
+     * Set whether request object has been provided from the PAR endooint.
      * 
-     * @param flag true if validated, false if not
+     * @param flag true if from the PAR endpoint, false if not
      * 
      * @since 4.2.0
      */
-    public void setRequestObjectValidated(final boolean flag) {
-        requestObjectValidated = flag;
+    public void setRequestObjectFromPar(final boolean flag) {
+        requestObjectFromPar = flag;
     }
 
     /**
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/SetRequestObjectToResponseContext.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/SetRequestObjectToResponseContext.java
index 5ca78f97..c6153249 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/SetRequestObjectToResponseContext.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/SetRequestObjectToResponseContext.java
@@ -297,7 +297,7 @@ public class SetRequestObjectToResponseContext extends AbstractOAuthAuthorizatio
                     final JWTClaimsSet jwtClaimsSet = JWTClaimsSet.parse(claimsSet);
                     oidcResponseContext.setRequestObject(new PlainJWT(jwtClaimsSet));
                     log.debug("{} Request object by PAR reference stored to oidc response context", getLogPrefix());
-                    oidcResponseContext.setRequestObjectValidated(true);
+                    oidcResponseContext.setRequestObjectFromPar(true);
                     return;
                 } catch (final ParseException e) {
                     log.error("{} Could not build JWT from the request_uri claims set", getLogPrefix(), e);
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateRequestObject.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateRequestObject.java
index 16df6fb2..874eac2b 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateRequestObject.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateRequestObject.java
@@ -128,10 +128,6 @@ public class ValidateRequestObject extends AbstractOAuthAuthorizationResponseAct
             log.debug("{} No request object, nothing to do", getLogPrefix());
             return false;
         }
-        if (oidcResponseContext.isRequestObjectValidated()) {
-            log.debug("{} Request object is already validated, nothing to do", getLogPrefix());
-            return false;
-        }
         return true;
     }
     
@@ -143,8 +139,40 @@ public class ValidateRequestObject extends AbstractOAuthAuthorizationResponseAct
         final OIDCAuthenticationResponseContext oidcResponseContext = getOidcResponseContext();
         assert oidcResponseContext != null;
 
+        final JWTClaimsSet claimsSet;
+        try {
+            assert requestObject != null;
+            claimsSet = requestObject.getJWTClaimsSet();
+        } catch (final ParseException e) {
+            log.error("{} Unable to parse request object {}", getLogPrefix(), e.getMessage());
+            oidcResponseContext.setRequestObjectFailure(true);
+            ActionSupport.buildEvent(profileRequestContext, OidcEventIds.INVALID_REQUEST_OBJECT);
+            return;
+        }
+        assert claimsSet != null;
+
+        final AuthorizationRequest authorizationRequest = getAuthorizationRequest();
+        if (authorizationRequest == null) {
+            ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MSG_CTX);
+            log.error("{} Could not resolve AuthorizationRequest message from request", getLogPrefix());
+            return;
+        }
+
+        if (claimsSet.getClaims().containsKey("client_id")
+                && !authorizationRequest.getClientID()
+                .equals(new ClientID((String) claimsSet.getClaim("client_id")))) {
+            log.error("{} client_id in request object not matching client_id request parameter", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, OidcEventIds.INVALID_REQUEST_OBJECT);
+            return;
+        }
+
         // We let "none" to be used only if nothing else has been registered.
         if (requestObject instanceof PlainJWT) {
+            if (oidcResponseContext.isRequestObjectFromPar()) {
+                log.debug("{} Request object is built by the PAR endpoint and thus already validated",
+                        getLogPrefix());
+                return;
+            }
             if (getMetadataContext() == null) {
                 log.error("{} Request object unsigned, no client metadata", getLogPrefix());
                 oidcResponseContext.setRequestObjectFailure(true);
@@ -168,32 +196,15 @@ public class ValidateRequestObject extends AbstractOAuthAuthorizationResponseAct
             }
         }
 
-        final JWTClaimsSet claimsSet;
-        // Validate still client_id and response_type values
-        try {
-            assert requestObject != null;
-            claimsSet = requestObject.getJWTClaimsSet();
-            final AuthorizationRequest authorizationRequest = getAuthorizationRequest();
-            if (authorizationRequest == null) {
-                ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MSG_CTX);
-                log.error("{} Could not resolve AuthorizationRequest message from request", getLogPrefix());
+        // Validate still response_type values
+        if (!parEndpointLogic && authorizationRequest instanceof AuthenticationRequest authenticationRequest) {
+            final ResponseType requestedType = authenticationRequest.getResponseType();
+            if (requestedType == null) {
+                log.error("{} mandatory response_type is missing from the request", getLogPrefix());
+                ActionSupport.buildEvent(profileRequestContext, OidcEventIds.INVALID_RESPONSE_TYPE);
                 return;
             }
-            if (claimsSet.getClaims().containsKey("client_id")
-                    && !authorizationRequest.getClientID()
-                            .equals(new ClientID((String) claimsSet.getClaim("client_id")))) {
-                log.error("{} client_id in request object not matching client_id request parameter", getLogPrefix());
-                oidcResponseContext.setRequestObjectFailure(true);
-                ActionSupport.buildEvent(profileRequestContext, OidcEventIds.INVALID_REQUEST_OBJECT);
-                return;
-            }
-            if (!parEndpointLogic && authorizationRequest instanceof AuthenticationRequest authenticationRequest) {
-                final ResponseType requestedType = authenticationRequest.getResponseType();
-                if (requestedType == null) {
-                    log.error("{} mandatory response_type is missing from the request", getLogPrefix());
-                    ActionSupport.buildEvent(profileRequestContext, OidcEventIds.INVALID_RESPONSE_TYPE);
-                    return;
-                }
+            try {
                 if (claimsSet.getClaims().containsKey("response_type")
                     && !requestedType.equals(new ResponseType(claimsSet.getStringClaim("response_type").split(" ")))) {
                     log.error("{} response_type in request object not matching response_type request parameter",
@@ -202,12 +213,12 @@ public class ValidateRequestObject extends AbstractOAuthAuthorizationResponseAct
                     ActionSupport.buildEvent(profileRequestContext, OidcEventIds.INVALID_REQUEST_OBJECT);
                     return;
                 }
+            } catch (final ParseException e) {
+                log.error("{} Unable to parse response_type from request object {}", getLogPrefix(), e.getMessage());
+                oidcResponseContext.setRequestObjectFailure(true);
+                ActionSupport.buildEvent(profileRequestContext, OidcEventIds.INVALID_REQUEST_OBJECT);
+                return;
             }
-        } catch (final ParseException e) {
-            log.error("{} Unable to parse request object {}", getLogPrefix(), e.getMessage());
-            oidcResponseContext.setRequestObjectFailure(true);
-            ActionSupport.buildEvent(profileRequestContext, OidcEventIds.INVALID_REQUEST_OBJECT);
-            return;
         }
 
         try {
@@ -223,10 +234,8 @@ public class ValidateRequestObject extends AbstractOAuthAuthorizationResponseAct
             ActionSupport.buildEvent(profileRequestContext, OidcEventIds.INVALID_REQUEST_OBJECT);
             return;
         }
+    }
 
-        oidcResponseContext.setRequestObjectValidated(true);
-    }    
-    
     // Checkstyle: CyclomaticComplexity ON
 
 }
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/authorize/authorize-beans.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/authorize/authorize-beans.xml
index 9acaba3c..447498ce 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/authorize/authorize-beans.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/authorize/authorize-beans.xml
@@ -321,8 +321,8 @@
                                                 c:expression="#input.ensureOutboundMessageContext().ensureSubcontext(T(net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCAuthenticationResponseContext)).getRequestObject() != null" />
                                         </constructor-arg>
                                         <constructor-arg>
-                                            <bean id="RequestObjectNotVerified" parent="shibboleth.Conditions.Expression"
-                                                c:expression="#input.ensureOutboundMessageContext().ensureSubcontext(T(net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCAuthenticationResponseContext)).isRequestObjectValidated() == false" />
+                                            <bean id="RequestObjectNotFromPar" parent="shibboleth.Conditions.Expression"
+                                                c:expression="#input.ensureOutboundMessageContext().ensureSubcontext(T(net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCAuthenticationResponseContext)).isRequestObjectFromPar() == false" />
                                         </constructor-arg>
                                     </bean>
                                 </constructor-arg>

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


More information about the commits mailing list