[java-idp-oidc] branch main updated: JOIDC-128 - Support OAuth authorization requests

Henri Mikkonen henri.mikkonen at iki.fi
Mon Oct 3 15:01:20 UTC 2022


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

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

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

The following commit(s) were added to refs/heads/main by this push:
     new 81cce0a5 JOIDC-128 - Support OAuth authorization requests
81cce0a5 is described below

commit 81cce0a57cbf12100b6e9443e871710b59f141a5
Author: Henri Mikkonen <henri.mikkonen at iki.fi>
AuthorDate: Mon Oct 3 17:57:41 2022 +0300

    JOIDC-128 - Support OAuth authorization requests
    
    https://shibboleth.atlassian.net/browse/JOIDC-128
    
    Authorize-flow now contains a decision-state before decoding: if the requested scope contains
    ‘openid’, the request is considered as OIDC authentication request. OAuth authorization request
    otherwise.
    
    Moved the following actions into oauth2.profile.impl package: DecryptRequestObject,
    SetAuthorizationCodeToResponseContext, SetRequestObjectToResponseContext, ValidateCodeChallenge,
    ValidateRedirectURI, ValidateRequestObject, ValidateResponseType, ValidateScope.
    
    Also updated various lookup functions to be compatible with OAuth authorization requests if not
    only related to OIDC.
---
 ...bstractAuthenticationRequestLookupFunction.java |  47 +--
 ...bstractAuthorizationRequestLookupFunction.java} |  35 +-
 .../navigate/DefaultClientIDLookupFunction.java    |   7 +-
 .../DefaultRequestAudienceLookupFunction.java      |   9 +-
 .../DefaultRequestCodeChallengeLookupFunction.java |  11 +-
 ...ltRequestCodeChallengeMethodLookupFunction.java |  11 +-
 .../DefaultRequestRedirectURILookupFunction.java   |   9 +-
 .../DefaultRequestResponseModeLookupFunction.java  |   9 +-
 .../DefaultRequestResponseTypeLookupFunction.java  |   9 +-
 .../DefaultRequestStateLookupFunction.java         |   9 +-
 .../DefaultRequestedPromptLookupFunction.java      |  11 +-
 .../DefaultRequestedScopeLookupFunction.java       |  11 +-
 .../DefaultValidResponseTypesLookupFunction.java   |  54 +++
 ...actAuthenticationRequestLookupFunctionTest.java |   9 +
 ...actAuthorizationRequestLookupFunctionTest.java} |  23 +-
 .../impl/OAuth2AuthorizationRequestDecoder.java    |  80 ++++
 .../AbstractOAuthAuthorizationRequestAction.java   |  49 +++
 .../AbstractOAuthAuthorizationResponseAction.java  |  97 +++++
 .../profile/impl/DecryptRequestObject.java         |   4 +-
 .../SetAuthorizationCodeToResponseContext.java     |   6 +-
 .../impl/SetRequestObjectToResponseContext.java    |  20 +-
 .../profile/impl/ValidateCodeChallenge.java        |   4 +-
 .../profile/impl/ValidateRedirectURI.java          |   4 +-
 .../profile/impl/ValidateRequestObject.java        |   8 +-
 .../oauth2/profile/impl/ValidateResponseType.java  | 101 +++++
 .../{ => oauth2}/profile/impl/ValidateScope.java   |  37 +-
 .../FormOutboundAuthenticationResponseMessage.java |  28 +-
 .../oidc/op/profile/impl/ValidateResponseType.java |  53 ---
 .../idp/flows/oidc/authorize/authorize-beans.xml   |  42 +-
 .../idp/flows/oidc/authorize/authorize-flow.xml    |  20 +-
 .../idp/flows/oidc/token/token-beans.xml           |   4 +-
 .../idp/flows/oidc/userinfo/userinfo-beans.xml     |   2 +-
 .../profile/impl/DecryptRequestObjectTest.java     |   2 +-
 .../SetAuthorizationCodeToResponseContextTest.java |   3 +-
 .../SetRequestObjectToResponseContextTest.java     |   3 +-
 .../profile/impl/ValidateCodeChallengeTest.java    |   3 +-
 .../profile/impl/ValidateRedirectURITest.java      |   3 +-
 .../profile/impl/ValidateRequestObjectTest.java    |   2 +-
 .../profile/impl/ValidateResponseTypeTest.java     |   3 +-
 .../profile/impl/ValidateScopeTest.java            |   9 +-
 .../oidc/op/profile/flow/AuthorizeFlowTest.java    | 432 ++++++++++++++++-----
 .../src/test/resources/conf/attribute-filter.xml   |  15 +
 42 files changed, 983 insertions(+), 315 deletions(-)

diff --git a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/AbstractAuthenticationRequestLookupFunction.java b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/AbstractAuthenticationRequestLookupFunction.java
index 76dc1111..81cf4f1d 100644
--- a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/AbstractAuthenticationRequestLookupFunction.java
+++ b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/AbstractAuthenticationRequestLookupFunction.java
@@ -23,59 +23,38 @@ import javax.annotation.Nullable;
 import org.opensaml.messaging.context.navigate.ContextDataLookupFunction;
 import org.opensaml.profile.context.ProfileRequestContext;
 
-import com.nimbusds.jwt.JWT;
+import com.nimbusds.oauth2.sdk.AuthorizationRequest;
 import com.nimbusds.openid.connect.sdk.AuthenticationRequest;
 
-import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCAuthenticationResponseContext;
-
 /**
  * A Abstract function extended by lookups searching fields from authentication request.
  * 
  * @param <T> type of lookup result to return.
  */
 public abstract class AbstractAuthenticationRequestLookupFunction<T>
+        extends AbstractAuthorizationRequestLookupFunction<T>
         implements ContextDataLookupFunction<ProfileRequestContext,T> {
 
-    /** Request. */
-    @Nullable private JWT requestObject;
-
+    public AbstractAuthenticationRequestLookupFunction() {
+        super(AuthenticationRequest.class);
+    }
 
     /**
      * Implemented to perform the actual lookup.
      * 
-     * @param req authentication request to perform the lookup from.
+     * @param req auhtorization request to perform the lookup from.
      * @return lookup value.
      */
-    @Nullable protected abstract T doLookup(@Nonnull AuthenticationRequest req);
-
-    /** {@inheritDoc} */
-    @Nullable
-    public T apply(@Nullable final ProfileRequestContext input) {
-        if (input == null || input.getInboundMessageContext() == null) {
-            return null;
-        }
-        final Object message = input.getInboundMessageContext().getMessage();
-        if (message == null || !(message instanceof AuthenticationRequest)) {
-            return null;
-        }
-        if (input.getOutboundMessageContext() != null) {
-            final OIDCAuthenticationResponseContext ctx =
-                    input.getOutboundMessageContext().getSubcontext(OIDCAuthenticationResponseContext.class, false);
-            if (ctx != null) {
-                requestObject = ctx.getRequestObject();
-            }
-        }
-        
-        return doLookup((AuthenticationRequest) message);
+    @Nullable protected T doLookup(@Nonnull final AuthorizationRequest req) {
+        return doLookup((AuthenticationRequest) req);
     }
 
     /**
-     * Gets the request object.
+     * Implemented to perform the actual lookup from OIDC authentication request.
      * 
-     * @return the request object.
+     * @param req authentication request to perform the lookup from.
+     * @return lookup value.
      */
-    @Nullable public JWT getRequestObject() {
-        return requestObject;
-    }
-    
+    @Nullable protected abstract T doLookup(@Nonnull final AuthenticationRequest req);
+
 }
\ No newline at end of file
diff --git a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/AbstractAuthenticationRequestLookupFunction.java b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/AbstractAuthorizationRequestLookupFunction.java
similarity index 67%
copy from idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/AbstractAuthenticationRequestLookupFunction.java
copy to idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/AbstractAuthorizationRequestLookupFunction.java
index 76dc1111..f6c5aae5 100644
--- a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/AbstractAuthenticationRequestLookupFunction.java
+++ b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/AbstractAuthorizationRequestLookupFunction.java
@@ -24,29 +24,50 @@ import org.opensaml.messaging.context.navigate.ContextDataLookupFunction;
 import org.opensaml.profile.context.ProfileRequestContext;
 
 import com.nimbusds.jwt.JWT;
-import com.nimbusds.openid.connect.sdk.AuthenticationRequest;
+import com.nimbusds.oauth2.sdk.AuthorizationRequest;
 
 import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCAuthenticationResponseContext;
+import net.shibboleth.utilities.java.support.logic.Constraint;
 
 /**
- * A Abstract function extended by lookups searching fields from authentication request.
+ * A Abstract function extended by lookups searching fields from authorization request.
  * 
  * @param <T> type of lookup result to return.
+ * 
+ * @since 3.3.0
  */
-public abstract class AbstractAuthenticationRequestLookupFunction<T>
+public abstract class AbstractAuthorizationRequestLookupFunction<T>
         implements ContextDataLookupFunction<ProfileRequestContext,T> {
 
     /** Request. */
     @Nullable private JWT requestObject;
 
+    /** The request message class to verify that the actual request is an instance of. */
+    @Nonnull private final Class<? extends AuthorizationRequest> messageClass;
+
+    /**
+     * Constructor.
+     */
+    public AbstractAuthorizationRequestLookupFunction() {
+        this(AuthorizationRequest.class);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param clazz request message class to verify that the actual request is an instance of.
+     */
+    protected AbstractAuthorizationRequestLookupFunction(@Nonnull final Class<? extends AuthorizationRequest> clazz) {
+        messageClass = Constraint.isNotNull(clazz, "Message class cannot be null");
+    }
 
     /**
      * Implemented to perform the actual lookup.
      * 
-     * @param req authentication request to perform the lookup from.
+     * @param req authorization request to perform the lookup from.
      * @return lookup value.
      */
-    @Nullable protected abstract T doLookup(@Nonnull AuthenticationRequest req);
+    @Nullable protected abstract T doLookup(@Nonnull final AuthorizationRequest req);
 
     /** {@inheritDoc} */
     @Nullable
@@ -55,7 +76,7 @@ public abstract class AbstractAuthenticationRequestLookupFunction<T>
             return null;
         }
         final Object message = input.getInboundMessageContext().getMessage();
-        if (message == null || !(message instanceof AuthenticationRequest)) {
+        if (message == null || !(messageClass.isInstance(message))) {
             return null;
         }
         if (input.getOutboundMessageContext() != null) {
@@ -66,7 +87,7 @@ public abstract class AbstractAuthenticationRequestLookupFunction<T>
             }
         }
         
-        return doLookup((AuthenticationRequest) message);
+        return doLookup((AuthorizationRequest) message);
     }
 
     /**
diff --git a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultClientIDLookupFunction.java b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultClientIDLookupFunction.java
index 93b48670..9c52f900 100644
--- a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultClientIDLookupFunction.java
+++ b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultClientIDLookupFunction.java
@@ -21,8 +21,9 @@ import javax.annotation.Nullable;
 
 import org.opensaml.messaging.context.MessageContext;
 import org.opensaml.messaging.context.navigate.ContextDataLookupFunction;
+
+import com.nimbusds.oauth2.sdk.AuthorizationRequest;
 import com.nimbusds.oauth2.sdk.id.ClientID;
-import com.nimbusds.openid.connect.sdk.AuthenticationRequest;
 
 /**
  * A function that returns client id of the authentication request via a lookup function. This default lookup locates
@@ -36,9 +37,9 @@ public class DefaultClientIDLookupFunction implements ContextDataLookupFunction<
             return null;
         }
         final Object message = input.getMessage();
-        if (!(message instanceof AuthenticationRequest)) {
+        if (!(message instanceof AuthorizationRequest)) {
             return null;
         }
-        return ((AuthenticationRequest) message).getClientID();
+        return ((AuthorizationRequest) message).getClientID();
     }
 }
\ No newline at end of file
diff --git a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultRequestAudienceLookupFunction.java b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultRequestAudienceLookupFunction.java
index b8b72b50..c70e0665 100644
--- a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultRequestAudienceLookupFunction.java
+++ b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultRequestAudienceLookupFunction.java
@@ -28,20 +28,21 @@ import javax.annotation.Nullable;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import com.nimbusds.openid.connect.sdk.AuthenticationRequest;
+
+import com.nimbusds.oauth2.sdk.AuthorizationRequest;
 
 /**
- * A function that returns resource value of the authentication request.
+ * A function that returns resource value of the authorization request.
  * 
  * @since 3.2.0
  */
-public class DefaultRequestAudienceLookupFunction extends AbstractAuthenticationRequestLookupFunction<List<String>> {
+public class DefaultRequestAudienceLookupFunction extends AbstractAuthorizationRequestLookupFunction<List<String>> {
 
     /** Class logger. */
     @Nonnull private Logger log = LoggerFactory.getLogger(DefaultRequestAudienceLookupFunction.class);
 
     /** {@inheritDoc} */
-    @Nullable protected List<String> doLookup(@Nonnull final AuthenticationRequest req) {
+    @Nullable protected List<String> doLookup(@Nonnull final AuthorizationRequest req) {
         try {
             if (getRequestObject() != null && getRequestObject().getJWTClaimsSet().getClaim("resource") != null) {
                 final Object resource = getRequestObject().getJWTClaimsSet().getClaim("resource");
diff --git a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultRequestCodeChallengeLookupFunction.java b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultRequestCodeChallengeLookupFunction.java
index 6abf656d..f076c8a0 100644
--- a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultRequestCodeChallengeLookupFunction.java
+++ b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultRequestCodeChallengeLookupFunction.java
@@ -23,21 +23,22 @@ import javax.annotation.Nullable;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import com.nimbusds.openid.connect.sdk.AuthenticationRequest;
+
+import com.nimbusds.oauth2.sdk.AuthorizationRequest;
 
 /**
- * A function that returns code challenge value of the authentication request via a lookup function. This default lookup
- * locates code challenge from oidc authentication request if available. If information is not available, null is
+ * A function that returns code challenge value of the authorization request via a lookup function. This default lookup
+ * locates code challenge from OAuth authorization request if available. If information is not available, null is
  * returned. If there is code_challenge in request object it is used instead of code_challenge parameter.
  */
-public class DefaultRequestCodeChallengeLookupFunction extends AbstractAuthenticationRequestLookupFunction<String> {
+public class DefaultRequestCodeChallengeLookupFunction extends AbstractAuthorizationRequestLookupFunction<String> {
 
     /** Class logger. */
     @Nonnull
     private Logger log = LoggerFactory.getLogger(DefaultRequestCodeChallengeLookupFunction.class);
 
     /** {@inheritDoc} */
-    @Nullable protected String doLookup(@Nonnull final AuthenticationRequest req) {
+    @Nullable protected String doLookup(@Nonnull final AuthorizationRequest req) {
         try {
             if (getRequestObject() != null && getRequestObject().getJWTClaimsSet().getClaim("code_challenge") != null) {
                 final Object codeChallenge = getRequestObject().getJWTClaimsSet().getClaim("code_challenge");
diff --git a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultRequestCodeChallengeMethodLookupFunction.java b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultRequestCodeChallengeMethodLookupFunction.java
index 5c347971..bb01adef 100644
--- a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultRequestCodeChallengeMethodLookupFunction.java
+++ b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultRequestCodeChallengeMethodLookupFunction.java
@@ -21,16 +21,17 @@ import java.text.ParseException;
 import javax.annotation.Nonnull;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import com.nimbusds.openid.connect.sdk.AuthenticationRequest;
+
+import com.nimbusds.oauth2.sdk.AuthorizationRequest;
 
 /**
- * A function that returns code challenge method value of the authentication request via a lookup function. This default
- * lookup locates code challenge method from oidc authentication request if available. If information is not available,
+ * A function that returns code challenge method value of the authorization request via a lookup function. This default
+ * lookup locates code challenge method from OAuth authorization request if available. If information is not available,
  * null is returned. If there is code_challenge_method in request object it is used instead of code_challenge_method
  * parameter.
  */
 public class DefaultRequestCodeChallengeMethodLookupFunction
-        extends AbstractAuthenticationRequestLookupFunction<String> {
+        extends AbstractAuthorizationRequestLookupFunction<String> {
 
     /** Class logger. */
     @Nonnull
@@ -38,7 +39,7 @@ public class DefaultRequestCodeChallengeMethodLookupFunction
 
     /** {@inheritDoc} */
     @Override
-    protected String doLookup(@Nonnull final AuthenticationRequest req) {
+    protected String doLookup(@Nonnull final AuthorizationRequest req) {
         try {
             if (getRequestObject() != null &&
                     getRequestObject().getJWTClaimsSet().getClaim("code_challenge_method") != null) {
diff --git a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultRequestRedirectURILookupFunction.java b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultRequestRedirectURILookupFunction.java
index 5240a99e..706e4271 100644
--- a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultRequestRedirectURILookupFunction.java
+++ b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultRequestRedirectURILookupFunction.java
@@ -23,14 +23,15 @@ import java.text.ParseException;
 import javax.annotation.Nonnull;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import com.nimbusds.openid.connect.sdk.AuthenticationRequest;
+
+import com.nimbusds.oauth2.sdk.AuthorizationRequest;
 
 /**
  * A function that returns redirect uri of the authentication request via a lookup function. This default lookup locates
- * redirect uri from oidc authentication request if available. If information is not available, null is returned. If
+ * redirect uri from OAuth authorization request if available. If information is not available, null is returned. If
  * there is redirect uri in request object it is used instead of redirect_uri parameter.
  */
-public class DefaultRequestRedirectURILookupFunction extends AbstractAuthenticationRequestLookupFunction<URI> {
+public class DefaultRequestRedirectURILookupFunction extends AbstractAuthorizationRequestLookupFunction<URI> {
 
     /** Class logger. */
     @Nonnull
@@ -38,7 +39,7 @@ public class DefaultRequestRedirectURILookupFunction extends AbstractAuthenticat
     
     /** {@inheritDoc} */
     @Override
-    protected URI doLookup(@Nonnull final AuthenticationRequest req) {
+    protected URI doLookup(@Nonnull final AuthorizationRequest req) {
         try {
             if (getRequestObject() != null && getRequestObject().getJWTClaimsSet().getClaim("redirect_uri") != null) {
                 final Object redirect = getRequestObject().getJWTClaimsSet().getClaim("redirect_uri");
diff --git a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultRequestResponseModeLookupFunction.java b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultRequestResponseModeLookupFunction.java
index ff6fadd5..96704d46 100644
--- a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultRequestResponseModeLookupFunction.java
+++ b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultRequestResponseModeLookupFunction.java
@@ -22,16 +22,17 @@ import java.text.ParseException;
 import javax.annotation.Nonnull;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+
+import com.nimbusds.oauth2.sdk.AuthorizationRequest;
 import com.nimbusds.oauth2.sdk.ResponseMode;
-import com.nimbusds.openid.connect.sdk.AuthenticationRequest;
 
 /**
  * A function that returns copy of the response mode of the request via a lookup function. This default lookup locates
- * response mode from oidc authentication request if available. If information is not available, null is returned. If
+ * response mode from OAuth authorization request if available. If information is not available, null is returned. If
  * there is response mode in request object it is used instead of response_mode parameter.
  */
 public class DefaultRequestResponseModeLookupFunction
-        extends AbstractAuthenticationRequestLookupFunction<ResponseMode> {
+        extends AbstractAuthorizationRequestLookupFunction<ResponseMode> {
 
     /** Class logger. */
     @Nonnull
@@ -39,7 +40,7 @@ public class DefaultRequestResponseModeLookupFunction
 
     /** {@inheritDoc} */
     @Override
-    protected ResponseMode doLookup(@Nonnull final AuthenticationRequest req) {
+    protected ResponseMode doLookup(@Nonnull final AuthorizationRequest req) {
         try {
             if (getRequestObject() != null && getRequestObject().getJWTClaimsSet().getClaim("response_mode") != null) {
                 final Object rMode = getRequestObject().getJWTClaimsSet().getClaim("response_mode");
diff --git a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultRequestResponseTypeLookupFunction.java b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultRequestResponseTypeLookupFunction.java
index c7cee3c3..af1ece21 100644
--- a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultRequestResponseTypeLookupFunction.java
+++ b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultRequestResponseTypeLookupFunction.java
@@ -21,16 +21,17 @@ import java.text.ParseException;
 import javax.annotation.Nonnull;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+
+import com.nimbusds.oauth2.sdk.AuthorizationRequest;
 import com.nimbusds.oauth2.sdk.ResponseType;
-import com.nimbusds.openid.connect.sdk.AuthenticationRequest;
 
 /**
  * A function that returns copy of response type via a lookup function. This default lookup locates response type from
- * oidc authentication request if available. If information is not available, null is returned. If there is response
+ * OAuth authorization request if available. If information is not available, null is returned. If there is response
  * type in request object it is used instead of response_type parameter.
  */
 public class DefaultRequestResponseTypeLookupFunction
-        extends AbstractAuthenticationRequestLookupFunction<ResponseType> {
+        extends AbstractAuthorizationRequestLookupFunction<ResponseType> {
 
     /** Class logger. */
     @Nonnull
@@ -38,7 +39,7 @@ public class DefaultRequestResponseTypeLookupFunction
 
     /** {@inheritDoc} */
     @Override
-    protected ResponseType doLookup(@Nonnull final AuthenticationRequest req) {
+    protected ResponseType doLookup(@Nonnull final AuthorizationRequest req) {
         try {
             if (getRequestObject() != null && getRequestObject().getJWTClaimsSet().getClaim("response_type") != null) {
                 return ResponseType.parse((String) getRequestObject().getJWTClaimsSet().getClaim("response_type"));
diff --git a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultRequestStateLookupFunction.java b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultRequestStateLookupFunction.java
index 8bdd6cd6..12a67e51 100644
--- a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultRequestStateLookupFunction.java
+++ b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultRequestStateLookupFunction.java
@@ -21,15 +21,16 @@ import java.text.ParseException;
 import javax.annotation.Nonnull;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+
+import com.nimbusds.oauth2.sdk.AuthorizationRequest;
 import com.nimbusds.oauth2.sdk.id.State;
-import com.nimbusds.openid.connect.sdk.AuthenticationRequest;
 
 /**
  * A function that returns copy of the state the request via a lookup function. This default lookup locates state from
- * oidc authentication request if available. If information is not available, null is returned. If there is state in
+ * OAuth authorization request if available. If information is not available, null is returned. If there is state in
  * request object it is used instead of state parameter.
  */
-public class DefaultRequestStateLookupFunction extends AbstractAuthenticationRequestLookupFunction<State> {
+public class DefaultRequestStateLookupFunction extends AbstractAuthorizationRequestLookupFunction<State> {
 
     /** Class logger. */
     @Nonnull
@@ -37,7 +38,7 @@ public class DefaultRequestStateLookupFunction extends AbstractAuthenticationReq
 
     /** {@inheritDoc} */
     @Override
-    protected State doLookup(@Nonnull final AuthenticationRequest req) {
+    protected State doLookup(@Nonnull final AuthorizationRequest req) {
         try {
             if (getRequestObject() != null && getRequestObject().getJWTClaimsSet().getClaim("state") != null) {
                 final Object state = getRequestObject().getJWTClaimsSet().getClaim("state");
diff --git a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultRequestedPromptLookupFunction.java b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultRequestedPromptLookupFunction.java
index d731158e..e3c4bae2 100644
--- a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultRequestedPromptLookupFunction.java
+++ b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultRequestedPromptLookupFunction.java
@@ -21,15 +21,16 @@ import java.text.ParseException;
 import javax.annotation.Nonnull;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import com.nimbusds.openid.connect.sdk.AuthenticationRequest;
+
+import com.nimbusds.oauth2.sdk.AuthorizationRequest;
 import com.nimbusds.openid.connect.sdk.Prompt;
 
 /**
- * A function that returns copy of requested prompt via a lookup function. This default lookup locates prompt from oidc
- * authentication request if available. If information is not available, null is returned. If there is prompt in request
+ * A function that returns copy of requested prompt via a lookup function. This default lookup locates prompt from OAuth
+ * authorization request if available. If information is not available, null is returned. If there is prompt in request
  * object it is used instead of prompt parameter.
  */
-public class DefaultRequestedPromptLookupFunction extends AbstractAuthenticationRequestLookupFunction<Prompt> {
+public class DefaultRequestedPromptLookupFunction extends AbstractAuthorizationRequestLookupFunction<Prompt> {
 
     /** Class logger. */
     @Nonnull
@@ -37,7 +38,7 @@ public class DefaultRequestedPromptLookupFunction extends AbstractAuthentication
 
     /** {@inheritDoc} */
     @Override
-    protected Prompt doLookup(@Nonnull final AuthenticationRequest req) {
+    protected Prompt doLookup(@Nonnull final AuthorizationRequest req) {
         try {
             if (getRequestObject() != null && getRequestObject().getJWTClaimsSet().getClaim("prompt") != null) {
                 return Prompt.parse((String) getRequestObject().getJWTClaimsSet().getClaim("prompt"));
diff --git a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultRequestedScopeLookupFunction.java b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultRequestedScopeLookupFunction.java
index 309281be..c35cc528 100644
--- a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultRequestedScopeLookupFunction.java
+++ b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultRequestedScopeLookupFunction.java
@@ -21,15 +21,16 @@ import java.text.ParseException;
 import javax.annotation.Nonnull;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+
+import com.nimbusds.oauth2.sdk.AuthorizationRequest;
 import com.nimbusds.oauth2.sdk.Scope;
-import com.nimbusds.openid.connect.sdk.AuthenticationRequest;
 
 /**
- * A function that returns copy of requested scopes via a lookup function. This default lookup locates scope from oidc
- * authentication request if available. If information is not available, null is returned. If there is scope in request
+ * A function that returns copy of requested scopes via a lookup function. This default lookup locates scope from OAuth
+ * authorization request if available. If information is not available, null is returned. If there is scope in request
  * object it is used instead of scope parameter.
  */
-public class DefaultRequestedScopeLookupFunction extends AbstractAuthenticationRequestLookupFunction<Scope> {
+public class DefaultRequestedScopeLookupFunction extends AbstractAuthorizationRequestLookupFunction<Scope> {
 
     /** Class logger. */
     @Nonnull
@@ -37,7 +38,7 @@ public class DefaultRequestedScopeLookupFunction extends AbstractAuthenticationR
 
     /** {@inheritDoc} */
     @Override
-    protected Scope doLookup(@Nonnull final AuthenticationRequest req) {
+    protected Scope doLookup(@Nonnull final AuthorizationRequest req) {
         try {
             if (getRequestObject() != null && getRequestObject().getJWTClaimsSet().getClaim("scope") != null) {
                 return Scope.parse((String) getRequestObject().getJWTClaimsSet().getClaim("scope"));
diff --git a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultValidResponseTypesLookupFunction.java b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultValidResponseTypesLookupFunction.java
new file mode 100644
index 00000000..970c561a
--- /dev/null
+++ b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultValidResponseTypesLookupFunction.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.plugin.oidc.op.profile.context.navigate;
+
+import java.util.Set;
+
+import javax.annotation.Nonnull;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.nimbusds.oauth2.sdk.AuthorizationRequest;
+import com.nimbusds.oauth2.sdk.ResponseType;
+import com.nimbusds.openid.connect.sdk.AuthenticationRequest;
+
+/**
+ * A function that returns a set of valid response type for the authorization request. If the authorization request is
+ * valid OIDC authentication request, the set specified by the OIDC core specification is returned. Otherwise the set
+ * consisting of OAuth2 code and implicit (token) flow is returned.
+ * 
+ * @since 3.3.0
+ */
+public class DefaultValidResponseTypesLookupFunction
+        extends AbstractAuthorizationRequestLookupFunction<Set<ResponseType>> {
+
+    /** Class logger. */
+    @Nonnull
+    private Logger log = LoggerFactory.getLogger(DefaultValidResponseTypesLookupFunction.class);
+
+    /** {@inheritDoc} */
+    @Override
+    protected Set<ResponseType> doLookup(@Nonnull final AuthorizationRequest req) {
+        if (req instanceof AuthenticationRequest) {
+            return Set.of(ResponseType.CODE, ResponseType.CODE_IDTOKEN, ResponseType.CODE_IDTOKEN_TOKEN,
+                    ResponseType.CODE_TOKEN, ResponseType.IDTOKEN, ResponseType.IDTOKEN_TOKEN);
+        } else {
+            return Set.of(ResponseType.CODE, ResponseType.TOKEN);
+        }
+    }
+}
\ No newline at end of file
diff --git a/idp-oidc-extension-api/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/AbstractAuthenticationRequestLookupFunctionTest.java b/idp-oidc-extension-api/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/AbstractAuthenticationRequestLookupFunctionTest.java
index f18fc85f..abd6304e 100644
--- a/idp-oidc-extension-api/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/AbstractAuthenticationRequestLookupFunctionTest.java
+++ b/idp-oidc-extension-api/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/AbstractAuthenticationRequestLookupFunctionTest.java
@@ -30,6 +30,7 @@ import org.testng.Assert;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import com.nimbusds.oauth2.sdk.AuthorizationRequest;
 import com.nimbusds.oauth2.sdk.ResponseType;
 import com.nimbusds.oauth2.sdk.Scope;
 import com.nimbusds.oauth2.sdk.id.ClientID;
@@ -65,6 +66,14 @@ public class AbstractAuthenticationRequestLookupFunctionTest {
         Assert.assertEquals("OK", mock.apply(prc));
     }
 
+    @Test
+    public void testAuthorizationRequest() {
+        final AuthorizationRequest req = new AuthorizationRequest.Builder(new ResponseType("code"),
+                new ClientID("000123")).state(new State()).build();
+        msgCtx.setMessage(req);
+        Assert.assertNull(mock.apply(prc));        
+    }
+    
     @Test
     public void testNoInboundCtxts() {
         // No profilecontext
diff --git a/idp-oidc-extension-api/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/AbstractAuthenticationRequestLookupFunctionTest.java b/idp-oidc-extension-api/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/AbstractAuthorizationRequestLookupFunctionTest.java
similarity index 77%
copy from idp-oidc-extension-api/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/AbstractAuthenticationRequestLookupFunctionTest.java
copy to idp-oidc-extension-api/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/AbstractAuthorizationRequestLookupFunctionTest.java
index f18fc85f..a96a8e26 100644
--- a/idp-oidc-extension-api/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/AbstractAuthenticationRequestLookupFunctionTest.java
+++ b/idp-oidc-extension-api/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/AbstractAuthorizationRequestLookupFunctionTest.java
@@ -30,13 +30,14 @@ import org.testng.Assert;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import com.nimbusds.oauth2.sdk.AuthorizationRequest;
 import com.nimbusds.oauth2.sdk.ResponseType;
 import com.nimbusds.oauth2.sdk.Scope;
 import com.nimbusds.oauth2.sdk.id.ClientID;
 import com.nimbusds.oauth2.sdk.id.State;
 import com.nimbusds.openid.connect.sdk.AuthenticationRequest;
 
-public class AbstractAuthenticationRequestLookupFunctionTest {
+public class AbstractAuthorizationRequestLookupFunctionTest {
 
     protected ProfileRequestContext prc;
 
@@ -52,8 +53,8 @@ public class AbstractAuthenticationRequestLookupFunctionTest {
         prc = new WebflowRequestContextProfileRequestContextLookup().apply(requestCtx);
         msgCtx = new MessageContext();
         prc.setInboundMessageContext(msgCtx);
-        AuthenticationRequest req = new AuthenticationRequest.Builder(new ResponseType("code"), new Scope("openid"),
-                new ClientID("000123"), URI.create("https://example.com/callback")).state(new State()).build();
+        final AuthorizationRequest req = new AuthorizationRequest.Builder(new ResponseType("code"),
+                new ClientID("000123")).state(new State()).build();
         msgCtx.setMessage(req);
         prc.setOutboundMessageContext(new MessageContext());
         oidcCtx = new OIDCAuthenticationResponseContext();
@@ -61,10 +62,20 @@ public class AbstractAuthenticationRequestLookupFunctionTest {
     }
 
     @Test
-    public void testOK() {
+    public void testAuthorizationRequest() {
         Assert.assertEquals("OK", mock.apply(prc));
     }
 
+    @Test
+    public void testAuthenticationRequest() {
+        final AuthenticationRequest req = new AuthenticationRequest.Builder(new ResponseType("code"),
+                new Scope("openid"), new ClientID("000123"), URI.create("https://example.com/callback"))
+                .state(new State())
+                .build();
+        msgCtx.setMessage(req);
+        Assert.assertEquals(mock.apply(prc), "OK");
+    }
+    
     @Test
     public void testNoInboundCtxts() {
         // No profilecontext
@@ -78,10 +89,10 @@ public class AbstractAuthenticationRequestLookupFunctionTest {
         Assert.assertNull(mock.apply(prc));
     }
 
-    class MockOKLookupFunction extends AbstractAuthenticationRequestLookupFunction<String> {
+    class MockOKLookupFunction extends AbstractAuthorizationRequestLookupFunction<String> {
 
         @Override
-        protected String doLookup(AuthenticationRequest req) {
+        protected String doLookup(AuthorizationRequest req) {
             return req != null ? new String("OK") : new String("NOK");
         }
 
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/decoding/impl/OAuth2AuthorizationRequestDecoder.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/decoding/impl/OAuth2AuthorizationRequestDecoder.java
new file mode 100644
index 00000000..621e6928
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/decoding/impl/OAuth2AuthorizationRequestDecoder.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.oidc.op.oauth2.decoding.impl;
+
+import java.io.IOException;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.messaging.decoder.MessageDecodingException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.MoreObjects;
+import com.nimbusds.oauth2.sdk.AuthorizationRequest;
+import com.nimbusds.oauth2.sdk.http.HTTPRequest;
+import com.nimbusds.oauth2.sdk.http.ServletUtils;
+
+import net.shibboleth.idp.plugin.oidc.op.decoding.impl.RequestUtil;
+
+/**
+ * Message decoder decoding OAuth2 {@link AuthorizationRequest}s.
+ */
+public class OAuth2AuthorizationRequestDecoder extends BaseOAuth2RequestDecoder<AuthorizationRequest> {
+
+    /** Class logger. */
+    @Nonnull
+    private final Logger log = LoggerFactory.getLogger(OAuth2AuthorizationRequestDecoder.class);
+
+    /** {@inheritDoc} */
+    @Override
+    protected AuthorizationRequest parseMessage() throws MessageDecodingException {
+        try {
+            final HTTPRequest httpReq = ServletUtils.createHTTPRequest(getHttpServletRequest());
+            getProtocolMessageLog().trace("Inbound request {}", RequestUtil.toString(httpReq));
+            return AuthorizationRequest.parse(httpReq);
+        } catch (final com.nimbusds.oauth2.sdk.ParseException | IOException e) {
+            log.error("Unable to decode inbound request: {}", e.getMessage());
+            throw new MessageDecodingException(e);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String getMessageToLog(@Nullable final AuthorizationRequest message) {
+        return message == null ? null : MoreObjects.toStringHelper(this).omitNullValues()
+                .add("clientId", message.getClientID())
+                .add("codeChallenge", message.getCodeChallenge())
+                .add("codeChallengeMethod", message.getCodeChallengeMethod())
+                .add("customParameters", message.getCustomParameters())
+                .add("endpointURI", message.getEndpointURI())
+                .add("prompt", message.getPrompt())
+                .add("redirectionURI", message.getRedirectionURI())
+                .add("requestObject", message.getRequestObject() == null ?
+                        null : message.getRequestObject().serialize())
+                .add("requestURI", message.getRequestURI())
+                .add("resources", message.getResources())
+                .add("responseMode", message.getResponseMode())
+                .add("responseType", message.getResponseType())
+                .add("scope", message.getScope())
+                .add("state", message.getState())
+                .toString();
+    }
+
+}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/AbstractOAuthAuthorizationRequestAction.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/AbstractOAuthAuthorizationRequestAction.java
new file mode 100644
index 00000000..bcc66888
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/AbstractOAuthAuthorizationRequestAction.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl;
+
+import javax.annotation.Nonnull;
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.nimbusds.oauth2.sdk.AuthorizationRequest;
+
+import net.shibboleth.idp.plugin.oidc.op.profile.impl.AbstractOIDCRequestAction;
+
+/**
+ * Abstract class for actions performing actions on {@link AuthorizationRequest} found via
+ * {@link ProfileRequestContext#getInboundMessageContext()} and {@link MessageContext#getMessage()}.
+ */
+public abstract class AbstractOAuthAuthorizationRequestAction extends AbstractOIDCRequestAction<AuthorizationRequest> {
+
+    /** Class logger. */
+    @Nonnull
+    private Logger log = LoggerFactory.getLogger(AbstractOAuthAuthorizationRequestAction.class);
+
+    /**
+     * Returns OAuth authorization request.
+     * 
+     * @return request
+     */
+    public AuthorizationRequest getAuthorizationRequest() {
+        return getRequest();
+    }
+
+}
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/AbstractOAuthAuthorizationResponseAction.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/AbstractOAuthAuthorizationResponseAction.java
new file mode 100644
index 00000000..9a423d42
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/AbstractOAuthAuthorizationResponseAction.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.profile.action.ActionSupport;
+import org.opensaml.profile.action.EventIds;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCAuthenticationResponseContext;
+import net.shibboleth.oidc.metadata.context.OIDCMetadataContext;
+
+/**
+ * 
+ * Abstract class for actions performing actions on {@link OIDCAuthenticationResponseContext} located under
+ * {@link ProfileRequestContext#getOutboundMessageContext()}. Extends baseclass that offers actions on
+ * {@link AuthorizationRequest} found via {@link MessageContext#getMessage()}.
+ */
+public abstract class AbstractOAuthAuthorizationResponseAction extends AbstractOAuthAuthorizationRequestAction {
+
+    /** Class logger. */
+    @Nonnull
+    private Logger log = LoggerFactory.getLogger(AbstractOAuthAuthorizationResponseAction.class);
+
+    /** oidc response context. */
+    @Nonnull
+    private OIDCAuthenticationResponseContext oidcResponseContext;
+
+    /** OIDC Metadata context. */
+    @Nonnull
+    private OIDCMetadataContext oidcMetadataContext;
+
+    /**
+     * Returns oidc response context.
+     * 
+     * @return ctx.
+     */
+    @Nonnull
+    public OIDCAuthenticationResponseContext getOidcResponseContext() {
+        return oidcResponseContext;
+    }
+
+    /**
+     * Returns the OIDC Metadata context.
+     * 
+     * @return The OIDC Metadata context.
+     */
+    public OIDCMetadataContext getMetadataContext() {
+        return oidcMetadataContext;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
+
+        if (!super.doPreExecute(profileRequestContext)) {
+            return false;
+        }
+        final MessageContext outboundMessageCtx = profileRequestContext.getOutboundMessageContext();
+        if (outboundMessageCtx == null) {
+            log.error("{} No outbound message context", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MSG_CTX);
+            return false;
+        }
+        
+        oidcResponseContext = outboundMessageCtx.getSubcontext(OIDCAuthenticationResponseContext.class);
+        if (oidcResponseContext == null) {
+            log.error("{} No OIDC response context", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MSG_CTX);
+            return false;
+        }
+        
+        oidcMetadataContext = profileRequestContext.getInboundMessageContext().getSubcontext(OIDCMetadataContext.class);
+        
+        return true;
+    }
+
+}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/DecryptRequestObject.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/DecryptRequestObject.java
similarity index 98%
rename from idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/DecryptRequestObject.java
rename to idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/DecryptRequestObject.java
index 152ece8c..3b553d18 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/DecryptRequestObject.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/DecryptRequestObject.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package net.shibboleth.idp.plugin.oidc.op.profile.impl;
+package net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl;
 
 import java.security.PrivateKey;
 import java.security.interfaces.ECPrivateKey;
@@ -55,7 +55,7 @@ import net.shibboleth.utilities.java.support.logic.Constraint;
 /**
  * Action decrypts request object if it is encrypted. Decrypted object is updated to response context.
  */
-public class DecryptRequestObject extends AbstractOIDCAuthenticationResponseAction {
+public class DecryptRequestObject extends AbstractOAuthAuthorizationResponseAction {
 
     /** Class logger. */
     @Nonnull private Logger log = LoggerFactory.getLogger(DecryptRequestObject.class);
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetAuthorizationCodeToResponseContext.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/SetAuthorizationCodeToResponseContext.java
similarity index 99%
rename from idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetAuthorizationCodeToResponseContext.java
rename to idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/SetAuthorizationCodeToResponseContext.java
index dbd9fb0c..26a630f2 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetAuthorizationCodeToResponseContext.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/SetAuthorizationCodeToResponseContext.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package net.shibboleth.idp.plugin.oidc.op.profile.impl;
+package net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl;
 
 import java.text.ParseException;
 import java.time.Duration;
@@ -68,7 +68,7 @@ import net.shibboleth.utilities.java.support.security.impl.SecureRandomIdentifie
  * {@link OIDCAuthenticationResponseContext#getAuthorizationCode()} located under
  * {@link ProfileRequestContext#getOutboundMessageContext()}.
  */
-public class SetAuthorizationCodeToResponseContext extends AbstractOIDCAuthenticationResponseAction {
+public class SetAuthorizationCodeToResponseContext extends AbstractOAuthAuthorizationResponseAction {
 
     /** Class logger. */
     @Nonnull private Logger log = LoggerFactory.getLogger(SetAuthorizationCodeToResponseContext.class);
@@ -341,7 +341,7 @@ public class SetAuthorizationCodeToResponseContext extends AbstractOIDCAuthentic
         final Scope scope = responseCtx.getScope();
         final AuthorizeCodeClaimsSet claimsSet = new AuthorizeCodeClaimsSet.Builder()
                 .setJWTID(idGenerator)
-                .setClientID(getAuthenticationRequest().getClientID())
+                .setClientID(getAuthorizationRequest().getClientID())
                 .setIssuer(issuerLookupStrategy.apply(profileRequestContext))
                 .setPrincipal(subjectCtx.getPrincipalName())
                 .setSubject(responseCtx.getSubject())
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetRequestObjectToResponseContext.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/SetRequestObjectToResponseContext.java
similarity index 91%
rename from idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetRequestObjectToResponseContext.java
rename to idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/SetRequestObjectToResponseContext.java
index 69433df5..11f9ebd9 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetRequestObjectToResponseContext.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/SetRequestObjectToResponseContext.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package net.shibboleth.idp.plugin.oidc.op.profile.impl;
+package net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl;
 
 import java.io.IOException;
 import java.net.URI;
@@ -50,7 +50,7 @@ import net.shibboleth.utilities.java.support.logic.Constraint;
  * Action that stored request object to {@link OIDCAuthenticationResponseContext}. The request
  * object may be given by value with request parameter or by reference with request_uri parameter.
  */
-public class SetRequestObjectToResponseContext extends AbstractOIDCAuthenticationResponseAction {
+public class SetRequestObjectToResponseContext extends AbstractOAuthAuthorizationResponseAction {
 
     /** Class logger. */
     @Nonnull private Logger log = LoggerFactory.getLogger(SetRequestObjectToResponseContext.class);
@@ -110,13 +110,13 @@ public class SetRequestObjectToResponseContext extends AbstractOIDCAuthenticatio
             return false;
         }
         
-        if (!getAuthenticationRequest().specifiesRequestObject()) {
+        if (!getAuthorizationRequest().specifiesRequestObject()) {
             log.debug("{} No request_uri or request by value, nothing to do", getLogPrefix());
             return false;
         }
         
-        if (getAuthenticationRequest().getRequestObject() != null
-                && getAuthenticationRequest().getRequestURI() != null) {
+        if (getAuthorizationRequest().getRequestObject() != null
+                && getAuthorizationRequest().getRequestURI() != null) {
             log.error("{} request_uri and request object cannot be both set", getLogPrefix());
             ActionSupport.buildEvent(profileRequestContext, OidcEventIds.REQUEST_OBJECT_AND_URI);
             return false;
@@ -128,8 +128,8 @@ public class SetRequestObjectToResponseContext extends AbstractOIDCAuthenticatio
     /** {@inheritDoc} */
     @Override
     protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
-        if (getAuthenticationRequest().getRequestObject() != null) {
-            getOidcResponseContext().setRequestObject(getAuthenticationRequest().getRequestObject());
+        if (getAuthorizationRequest().getRequestObject() != null) {
+            getOidcResponseContext().setRequestObject(getAuthorizationRequest().getRequestObject());
             log.debug("{} Request object {} by value stored to oidc response context", getLogPrefix(),
                     getOidcResponseContext().getRequestObject().serialize());
             return;
@@ -142,19 +142,19 @@ public class SetRequestObjectToResponseContext extends AbstractOIDCAuthenticatio
             if (metadata != null) {
                 final Set<URI> allowedURIs = metadata.getRequestObjectURIs();
                 if (allowedURIs != null) {
-                    authorized = allowedURIs.contains(getAuthenticationRequest().getRequestURI());
+                    authorized = allowedURIs.contains(getAuthorizationRequest().getRequestURI());
                 }
             }
         }
         
         if (!authorized) {
             log.error("{} Unregistered request URI blocked: {}", getLogPrefix(),
-                    getAuthenticationRequest().getRequestURI());
+                    getAuthorizationRequest().getRequestURI());
             ActionSupport.buildEvent(profileRequestContext, OidcEventIds.INVALID_REQUEST_URI);
             return;
         }
         
-        final HttpGet httpRequest = new HttpGet(getAuthenticationRequest().getRequestURI());
+        final HttpGet httpRequest = new HttpGet(getAuthorizationRequest().getRequestURI());
         final HttpClientContext httpContext = buildHttpContext(httpRequest);
         try {
             final HttpResponse response = httpClient.execute(httpRequest, httpContext);
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateCodeChallenge.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateCodeChallenge.java
similarity index 97%
rename from idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateCodeChallenge.java
rename to idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateCodeChallenge.java
index 29e236b4..47539722 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateCodeChallenge.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateCodeChallenge.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package net.shibboleth.idp.plugin.oidc.op.profile.impl;
+package net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl;
 
 import java.util.function.Function;
 import java.util.function.Predicate;
@@ -39,7 +39,7 @@ import net.shibboleth.utilities.java.support.logic.Constraint;
 /**
  * Validates the presence of PKCE code challenge parameter from the incoming authentication request.
  */
-public class ValidateCodeChallenge  extends AbstractOIDCAuthenticationResponseAction {
+public class ValidateCodeChallenge  extends AbstractOAuthAuthorizationResponseAction {
 
     /** Class logger. */
     @Nonnull private Logger log = LoggerFactory.getLogger(ValidateCodeChallenge.class);
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateRedirectURI.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateRedirectURI.java
similarity index 98%
rename from idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateRedirectURI.java
rename to idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateRedirectURI.java
index efa29d55..21c3e17a 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateRedirectURI.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateRedirectURI.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package net.shibboleth.idp.plugin.oidc.op.profile.impl;
+package net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl;
 
 import java.net.URI;
 import java.util.Set;
@@ -38,7 +38,7 @@ import net.shibboleth.utilities.java.support.logic.Constraint;
 /**
  * Action that validates redirect uri is expected. Validated redirect uri is stored to response context.
  */
-public class ValidateRedirectURI extends AbstractOIDCAuthenticationResponseAction {
+public class ValidateRedirectURI extends AbstractOAuthAuthorizationResponseAction {
 
     /** Class logger. */
     @Nonnull private Logger log = LoggerFactory.getLogger(ValidateRedirectURI.class);
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateRequestObject.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateRequestObject.java
similarity index 97%
rename from idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateRequestObject.java
rename to idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateRequestObject.java
index 51251f29..22cd7d71 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateRequestObject.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateRequestObject.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package net.shibboleth.idp.plugin.oidc.op.profile.impl;
+package net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl;
 
 import java.text.ParseException;
 import java.util.function.Function;
@@ -51,7 +51,7 @@ import net.shibboleth.utilities.java.support.logic.Constraint;
 /**
  * Action validates request object in response context.
  */
-public class ValidateRequestObject extends AbstractOIDCAuthenticationResponseAction {
+public class ValidateRequestObject extends AbstractOAuthAuthorizationResponseAction {
 
     /** Class logger. */
     @Nonnull private Logger log = LoggerFactory.getLogger(ValidateRequestObject.class);
@@ -180,14 +180,14 @@ public class ValidateRequestObject extends AbstractOIDCAuthenticationResponseAct
         try {
             claimsSet = requestObject.getJWTClaimsSet();
             if (claimsSet.getClaims().containsKey("client_id")
-                    && !getAuthenticationRequest().getClientID()
+                    && !getAuthorizationRequest().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;
             }
             if (claimsSet.getClaims().containsKey("response_type")
-                    && !getAuthenticationRequest().getResponseType().equals(new ResponseType(
+                    && !getAuthorizationRequest().getResponseType().equals(new ResponseType(
                             ((String) requestObject.getJWTClaimsSet().getClaim("response_type")).split(" ")))) {
                 log.error("{} response_type in request object not matching response_type request parameter",
                         getLogPrefix());
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateResponseType.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateResponseType.java
new file mode 100644
index 00000000..28398938
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateResponseType.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl;
+
+import java.util.Set;
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.profile.action.ActionSupport;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import com.nimbusds.oauth2.sdk.ResponseType;
+
+import net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.DefaultRequestResponseTypeLookupFunction;
+import net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.DefaultValidResponseTypesLookupFunction;
+import net.shibboleth.oidc.profile.core.OidcEventIds;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+
+/**
+ * An action that validates the requested response_type is (1) registered to the requesting RP and (2) valid for the
+ * protocol used for request.
+ */
+public class ValidateResponseType extends AbstractOAuthAuthorizationResponseAction {
+
+    /** Class logger. */
+    @Nonnull private Logger log = LoggerFactory.getLogger(ValidateResponseType.class);
+
+    /** Lookup strategy for fetching the requested response type. */
+    @NonnullAfterInit private Function<ProfileRequestContext, ResponseType> requestedResponseTypeLookupStrategy;
+
+    /** Lookup strategy for fetching the valid response types matching the request message. */
+    @NonnullAfterInit private Function<ProfileRequestContext, Set<ResponseType>> validResponseTypesLookupStrategy;
+
+    /**
+     * Constructor.
+     */
+    public ValidateResponseType() {
+        requestedResponseTypeLookupStrategy = new DefaultRequestResponseTypeLookupFunction();
+        validResponseTypesLookupStrategy = new DefaultValidResponseTypesLookupFunction();
+    }
+
+    /**
+     * Set the lookup strategy for fetching the requested response type.
+     * 
+     * @param strategy What to set.
+     */
+    public void setRequestedResponseTypeLookupStrategy(
+            @Nonnull final Function<ProfileRequestContext, ResponseType> strategy) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        requestedResponseTypeLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy cannot be null");
+    }
+
+    /**
+     * Set the lookup strategy for fetching the valid response types matching the request message.
+     * 
+     * @param strategy What to set.
+     */
+    public void setValidResponseTypesLookupStrategy(
+            @Nonnull final Function<ProfileRequestContext, Set<ResponseType>> strategy) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        validResponseTypesLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy cannot be null");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
+        final Set<ResponseType> registeredTypes = getMetadataContext() != null
+                    ? getMetadataContext().getClientInformation().getMetadata().getResponseTypes()
+                            : null;
+        final ResponseType requestedType = requestedResponseTypeLookupStrategy.apply(profileRequestContext);
+        if (registeredTypes == null || registeredTypes.isEmpty() || !registeredTypes.contains(requestedType)) {
+            log.warn("{} The response type {} is not registered for this RP", getLogPrefix(), requestedType);
+            ActionSupport.buildEvent(profileRequestContext, OidcEventIds.INVALID_RESPONSE_TYPE);
+        }
+        final Set<ResponseType> validTypes = validResponseTypesLookupStrategy.apply(profileRequestContext);
+        if (validTypes == null || validTypes.isEmpty() || !validTypes.contains(requestedType)) {
+            log.warn("{} The response type {} is not valid for this request", getLogPrefix(), requestedType);
+            ActionSupport.buildEvent(profileRequestContext, OidcEventIds.INVALID_RESPONSE_TYPE);
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateScope.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateScope.java
similarity index 92%
rename from idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateScope.java
rename to idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateScope.java
index 1b801ea1..5b860f85 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateScope.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateScope.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package net.shibboleth.idp.plugin.oidc.op.profile.impl;
+package net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl;
 
 import java.util.Iterator;
 import java.util.function.Function;
@@ -24,6 +24,8 @@ 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.action.EventIds;
 import org.opensaml.profile.context.ProfileRequestContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -47,7 +49,8 @@ import net.shibboleth.utilities.java.support.logic.Constraint;
  * and stores the resulting set in the response context.
  * 
  * <p>Requested scopes come from the inbound message and are possible but optional for both
- * authorization and token requests. They come from lookup functions aware of each message type.</p>
+ * authorization and token requests. They come from lookup functions aware of each message type. If the request
+ * contains 'openid' scope but it's not allowed for this request, an error event published.</p>
  * 
  * <p>Previously granted scopes are stored in the response context's slot for previous authorization
  * grant claims. In the case where no scopes are explicitly requested, we still filter the previous
@@ -60,7 +63,7 @@ import net.shibboleth.utilities.java.support.logic.Constraint;
  * <p>The "offline_access" scope is ignored and stripped for the authentication endpoint unless the
  * response type includes "code".</p>
  */
-public class ValidateScope extends AbstractOIDCAuthenticationResponseAction {
+public class ValidateScope extends AbstractOAuthAuthorizationResponseAction {
 
     /** Class logger. */
     @Nonnull private Logger log = LoggerFactory.getLogger(ValidateScope.class);
@@ -147,14 +150,7 @@ public class ValidateScope extends AbstractOIDCAuthenticationResponseAction {
     protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
         
         final String clientId = relyingPartyIdLookupStrategy.apply(profileRequestContext);
-        
-        // These typically come from metadata but may be supplemented or substituted from elsewhere.
-        final Scope allowedScopes = allowedScopeLookupStrategy.apply(profileRequestContext);
-        if (allowedScopes == null || allowedScopes.isEmpty()) {
-            log.debug("{} No allowed scope for client {}, nothing to do", getLogPrefix(), clientId);
-            return;
-        }
-        
+
         // These come from a previous authorization grant (authz code or access/refresh token).
         Scope previouslyGrantedScopes = null;
         if (getOidcResponseContext().getAuthorizationGrantClaimsSet() != null) {
@@ -170,7 +166,24 @@ public class ValidateScope extends AbstractOIDCAuthenticationResponseAction {
             requestedScopes = previouslyGrantedScopes;
             previouslyGrantedScopes = null;
         }
-        
+
+        // These typically come from metadata but may be supplemented or substituted from elsewhere.
+        final Scope allowedScopes = allowedScopeLookupStrategy.apply(profileRequestContext);
+
+        final String oidcScope = OIDCScopeValue.OPENID.getValue();
+        if (requestedScopes != null && requestedScopes.contains(oidcScope) && 
+                (allowedScopes == null || !allowedScopes.contains(oidcScope))) {
+            log.warn("{} OIDC sequence was requested but no openid scope granted for RP {}", getLogPrefix(),
+                    clientId);
+            ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MESSAGE);
+            return;
+        }
+
+        if (allowedScopes == null || allowedScopes.isEmpty()) {
+            log.debug("{} No allowed scope for client {}, nothing to do", getLogPrefix(), clientId);
+            return;
+        }
+
         boolean reducedRequestedScopes = false;
         
         for (Iterator<Scope.Value> i = requestedScopes.iterator(); i.hasNext();) {
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/FormOutboundAuthenticationResponseMessage.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/FormOutboundAuthenticationResponseMessage.java
index cd425f9a..254e13ae 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/FormOutboundAuthenticationResponseMessage.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/FormOutboundAuthenticationResponseMessage.java
@@ -28,10 +28,14 @@ import org.opensaml.profile.context.ProfileRequestContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.nimbusds.oauth2.sdk.AuthorizationResponse;
+import com.nimbusds.oauth2.sdk.AuthorizationSuccessResponse;
 import com.nimbusds.oauth2.sdk.id.Issuer;
+import com.nimbusds.openid.connect.sdk.AuthenticationRequest;
 import com.nimbusds.openid.connect.sdk.AuthenticationResponse;
 import com.nimbusds.openid.connect.sdk.AuthenticationSuccessResponse;
 
+import net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl.AbstractOAuthAuthorizationResponseAction;
 import net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.DefaultRequestResponseModeLookupFunction;
 import net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.DefaultRequestStateLookupFunction;
 import net.shibboleth.idp.profile.IdPEventIds;
@@ -45,7 +49,7 @@ import net.shibboleth.utilities.java.support.primitive.StringSupport;
  * Action that forms outbound message based on request and response context. Formed message is set to
  * {@link ProfileRequestContext#getOutboundMessageContext()}.
  */
-public class FormOutboundAuthenticationResponseMessage extends AbstractOIDCAuthenticationResponseAction {
+public class FormOutboundAuthenticationResponseMessage extends AbstractOAuthAuthorizationResponseAction {
 
     /** Class logger. */
     @Nonnull private Logger log = LoggerFactory.getLogger(FormOutboundAuthenticationResponseMessage.class);
@@ -120,11 +124,21 @@ public class FormOutboundAuthenticationResponseMessage extends AbstractOIDCAuthe
             ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MESSAGE);
             return;
         }
-        final AuthenticationResponse resp = new AuthenticationSuccessResponse(getOidcResponseContext().getRedirectURI(),
-                getOidcResponseContext().getAuthorizationCode(), getOidcResponseContext().getProcessedToken(),
-                getOidcResponseContext().getAccessToken(),
-                new DefaultRequestStateLookupFunction().apply(profileRequestContext), null, issuer,
-                new DefaultRequestResponseModeLookupFunction().apply(profileRequestContext));
-        profileRequestContext.getOutboundMessageContext().setMessage(resp);
+        if (getAuthorizationRequest() instanceof AuthenticationRequest) {
+            final AuthenticationResponse resp = new AuthenticationSuccessResponse(
+                    getOidcResponseContext().getRedirectURI(),
+                    getOidcResponseContext().getAuthorizationCode(), getOidcResponseContext().getProcessedToken(),
+                    getOidcResponseContext().getAccessToken(),
+                    new DefaultRequestStateLookupFunction().apply(profileRequestContext), null, issuer,
+                    new DefaultRequestResponseModeLookupFunction().apply(profileRequestContext));
+            profileRequestContext.getOutboundMessageContext().setMessage(resp);
+        } else {
+            final AuthorizationResponse resp = new AuthorizationSuccessResponse(
+                    getOidcResponseContext().getRedirectURI(),
+                    getOidcResponseContext().getAuthorizationCode(), getOidcResponseContext().getAccessToken(),
+                    new DefaultRequestStateLookupFunction().apply(profileRequestContext), issuer,
+                    new DefaultRequestResponseModeLookupFunction().apply(profileRequestContext));
+            profileRequestContext.getOutboundMessageContext().setMessage(resp);
+        }
     }
 }
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateResponseType.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateResponseType.java
deleted file mode 100644
index e3055390..00000000
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateResponseType.java
+++ /dev/null
@@ -1,53 +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.oidc.op.profile.impl;
-
-import java.util.Set;
-import javax.annotation.Nonnull;
-
-import org.opensaml.profile.action.ActionSupport;
-import org.opensaml.profile.context.ProfileRequestContext;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import com.nimbusds.oauth2.sdk.ResponseType;
-
-import net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.DefaultRequestResponseTypeLookupFunction;
-import net.shibboleth.oidc.profile.core.OidcEventIds;
-
-/**
- * An action that validates the requested response_type is registered to the requesting RP.
- */
-public class ValidateResponseType extends AbstractOIDCAuthenticationResponseAction {
-
-    /** Class logger. */
-    @Nonnull private Logger log = LoggerFactory.getLogger(ValidateResponseType.class);
-
-    /** {@inheritDoc} */
-    @Override
-    protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
-        final Set<ResponseType> registeredTypes = getMetadataContext() != null
-                    ? getMetadataContext().getClientInformation().getMetadata().getResponseTypes()
-                            : null;
-        final ResponseType requestedType = new DefaultRequestResponseTypeLookupFunction().apply(profileRequestContext);
-        if (registeredTypes == null || registeredTypes.isEmpty() || !registeredTypes.contains(requestedType)) {
-            log.warn("{} The response type {} is not registered for this RP", getLogPrefix(), requestedType);
-            ActionSupport.buildEvent(profileRequestContext, OidcEventIds.INVALID_RESPONSE_TYPE);
-        }
-    }
-
-}
\ 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 20f96972..9c2ca708 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
@@ -14,13 +14,20 @@
     
     <bean id="shibboleth.oidc.loggingId" class="java.lang.String" c:_0="%{idp.service.logging.oidcsso:OIDC.SSO}" />
 
-    <bean id="DecodeMessage" class="org.opensaml.profile.action.impl.DecodeMessage" scope="prototype">
+    <bean id="DecodeOIDCMessage" class="org.opensaml.profile.action.impl.DecodeMessage" scope="prototype">
         <constructor-arg>
             <bean class="net.shibboleth.idp.plugin.oidc.op.decoding.impl.OIDCAuthenticationRequestDecoder"
                 scope="prototype" p:httpServletRequest-ref="shibboleth.HttpServletRequest" />
         </constructor-arg>
     </bean>
 
+    <bean id="DecodeOAuthMessage" class="org.opensaml.profile.action.impl.DecodeMessage" scope="prototype">
+        <constructor-arg>
+            <bean class="net.shibboleth.idp.plugin.oidc.op.oauth2.decoding.impl.OAuth2AuthorizationRequestDecoder"
+                scope="prototype" p:httpServletRequest-ref="shibboleth.HttpServletRequest" />
+        </constructor-arg>
+    </bean>
+
     <bean id="InitializeAuthenticationContext"
         class="net.shibboleth.idp.plugin.oidc.op.profile.impl.InitializeAuthenticationContext" scope="prototype"
         p:loginHintLookupStrategy-ref="%{idp.oidc.LoginHintLookupStrategy:DefaultRequestLoginHintLookupFunction}"/>
@@ -39,7 +46,8 @@
         scope="prototype" />
 
     <bean id="SetRequestedSubjectToResponseContext"
-        class="net.shibboleth.idp.plugin.oidc.op.profile.impl.SetRequestedSubjectToResponseContext" scope="prototype" />
+        class="net.shibboleth.idp.plugin.oidc.op.profile.impl.SetRequestedSubjectToResponseContext" scope="prototype"
+        p:activationCondition-ref="ScopeContainsOpenid"/>
 
     <bean id="SetSessionIdToResponseContext"
         class="net.shibboleth.idp.plugin.oidc.op.profile.impl.SetSessionIdToResponseContext" scope="prototype"
@@ -47,7 +55,8 @@
 
     <bean id="SetRequestedClaimsToResponseContext"
         class="net.shibboleth.idp.plugin.oidc.op.profile.impl.SetRequestedClaimsToResponseContext" scope="prototype"
-        p:transcoderRegistry-ref="shibboleth.AttributeRegistryService" />
+        p:transcoderRegistry-ref="shibboleth.AttributeRegistryService"
+        p:activationCondition-ref="ScopeContainsOpenid"/>
 
     <bean id="PopulatePostAuthnInterceptContext"
             class="net.shibboleth.idp.profile.interceptor.impl.PopulateProfileInterceptorContext" scope="prototype"
@@ -106,14 +115,14 @@
         p:parameterType="#{T(net.shibboleth.idp.plugin.oidc.op.security.impl.OIDCClientInformationSignatureValidationParametersResolver.ParameterType).REQUEST_OBJECT_VALIDATION}" />
 
     <bean id="SetRequestObjectToResponseContext"
-        class="net.shibboleth.idp.plugin.oidc.op.profile.impl.SetRequestObjectToResponseContext" scope="prototype"
+        class="net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl.SetRequestObjectToResponseContext" scope="prototype"
         p:httpClient="#{getObject('shibboleth.oidc.NonBrowser.HttpClient') ?: getObject('shibboleth.InternalHttpClient')}"
         p:httpClientSecurityParameters="#{getObject('shibboleth.oidc.NonBrowser.HttpClientSecurityParameters')}" />
 
-    <bean id="DecryptRequestObject" class="net.shibboleth.idp.plugin.oidc.op.profile.impl.DecryptRequestObject"
+    <bean id="DecryptRequestObject" class="net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl.DecryptRequestObject"
         scope="prototype" />
 
-    <bean id="ValidateRequestObject" class="net.shibboleth.idp.plugin.oidc.op.profile.impl.ValidateRequestObject"
+    <bean id="ValidateRequestObject" class="net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl.ValidateRequestObject"
         scope="prototype"
         p:plainClaimsValidator-ref="shibboleth.oidc.PlainRequestObjectClaimsValidation"
         p:signedClaimsValidator-ref="shibboleth.oidc.SignedRequestObjectClaimsValidation">
@@ -171,16 +180,16 @@
         <ref bean="AudienceClaimsValidator" />
     </util:list>
 
-    <bean id="ValidateRedirectURI" class="net.shibboleth.idp.plugin.oidc.op.profile.impl.ValidateRedirectURI"
+    <bean id="ValidateRedirectURI" class="net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl.ValidateRedirectURI"
         scope="prototype" p:requireRequestedValue="true" />
 
-    <bean id="ValidateResponseType" class="net.shibboleth.idp.plugin.oidc.op.profile.impl.ValidateResponseType"
+    <bean id="ValidateResponseType" class="net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl.ValidateResponseType"
         scope="prototype" />
 
-    <bean id="ValidateCodeChallenge" class="net.shibboleth.idp.plugin.oidc.op.profile.impl.ValidateCodeChallenge"
+    <bean id="ValidateCodeChallenge" class="net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl.ValidateCodeChallenge"
         scope="prototype" />
 
-    <bean id="ValidateScope" class="net.shibboleth.idp.plugin.oidc.op.profile.impl.ValidateScope" scope="prototype"
+    <bean id="ValidateScope" class="net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl.ValidateScope" scope="prototype"
         p:allowedScopeLookupStrategy="#{getObject('shibboleth.oidc.AllowedScopeStrategy') ?: getObject('shibboleth.oidc.DefaultAllowedScopeStrategy')}" />
 
     <bean id="ValidateAudience"
@@ -362,7 +371,8 @@
     </bean>
 
     <bean id="VerifyRequestedSubjectIdentifier"
-        class="net.shibboleth.idp.plugin.oidc.op.profile.impl.VerifyRequestedSubjectIdentifier" scope="prototype" />
+        class="net.shibboleth.idp.plugin.oidc.op.profile.impl.VerifyRequestedSubjectIdentifier" scope="prototype"
+        p:activationCondition-ref="ScopeContainsOpenid"/>
 
     <bean id="SetTokenDeliveryAttributesToResponseContext"
             class="net.shibboleth.idp.plugin.oidc.op.profile.impl.SetTokenDeliveryAttributesToResponseContext" scope="prototype"
@@ -383,7 +393,7 @@
         class="net.shibboleth.idp.plugin.oidc.op.profile.impl.SetConsentToResponseContext" scope="prototype" />
 
     <bean id="SetAuthorizationCodeToResponseContext"
-        class="net.shibboleth.idp.plugin.oidc.op.profile.impl.SetAuthorizationCodeToResponseContext" scope="prototype"
+        class="net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl.SetAuthorizationCodeToResponseContext" scope="prototype"
         p:dataSealer-ref="#{'%{idp.oidc.tokenSealer:shibboleth.oidc.TokenSealer}'.trim()}">
         <property name="identifierGeneratorLookupStrategy">
             <bean class="net.shibboleth.idp.profile.config.navigate.IdentifierGenerationStrategyLookupFunction"
@@ -633,4 +643,12 @@
         </constructor-arg>
     </bean>
 
+    <bean id="ScopeContainsOpenid" parent="shibboleth.Conditions.Expression" p:customObject-ref="shibboleth.HttpServletRequest">
+        <constructor-arg>
+            <value>
+                #custom.getParameter('scope') != null and #custom.getParameter('scope').contains('openid')
+            </value>
+        </constructor-arg>
+    </bean>
+
 </beans>
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/authorize/authorize-flow.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/authorize/authorize-flow.xml
index 51f782e3..33be18f7 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/authorize/authorize-flow.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/authorize/authorize-flow.xml
@@ -8,11 +8,25 @@
         <evaluate expression="PopulateMetricContext" />
         <evaluate expression="FlowStartPopulateAuditContext" />
         <evaluate expression="'proceed'" />
-        <transition on="proceed" to="DecodeMessage" />
+        <transition on="proceed" to="CheckRequestType">
+            <set name="flowScope.oidcAuthnRequest" value="ScopeContainsOpenid.test(opensamlProfileRequestContext)" />
+        </transition>
     </action-state>
 
-    <action-state id="DecodeMessage">
-        <evaluate expression="DecodeMessage" />
+    <decision-state id="CheckRequestType">
+        <if test="oidcAuthnRequest" then="DecodeOIDCMessage" else="DecodeOAuthMessage" />
+    </decision-state>
+
+    <action-state id="DecodeOIDCMessage">
+        <evaluate expression="DecodeOIDCMessage" />
+        <evaluate expression="PostDecodePopulateAuditContext" />
+        <evaluate expression="'proceed'" />
+        <!-- DoMetadataLookup is expected to proceed to SelectConfiguration -->
+        <transition on="proceed" to="DoMetadataLookup" />
+    </action-state>
+
+    <action-state id="DecodeOAuthMessage">
+        <evaluate expression="DecodeOAuthMessage" />
         <evaluate expression="PostDecodePopulateAuditContext" />
         <evaluate expression="'proceed'" />
         <!-- DoMetadataLookup is expected to proceed to SelectConfiguration -->
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-beans.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-beans.xml
index 1ce78d1b..8f5f7222 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-beans.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-beans.xml
@@ -66,7 +66,7 @@
     
     <bean id="ValidatePKCE" class="net.shibboleth.idp.plugin.oidc.op.profile.impl.ValidatePKCE" scope="prototype" />
 
-    <bean id="ValidateRedirectURI" class="net.shibboleth.idp.plugin.oidc.op.profile.impl.ValidateRedirectURI"
+    <bean id="ValidateRedirectURI" class="net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl.ValidateRedirectURI"
         scope="prototype"
         p:activationCondition-ref="AuthorizationCodeGrantCondition"
         p:redirectURILookupStrategy-ref="shibboleth.TokenRequestRedirectURILookupStrategy"
@@ -143,7 +143,7 @@
 
     <!-- Common grant handling.  -->
 
-    <bean id="ValidateScope" class="net.shibboleth.idp.plugin.oidc.op.profile.impl.ValidateScope" scope="prototype"
+    <bean id="ValidateScope" class="net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl.ValidateScope" scope="prototype"
         p:requestedScopeLookupStrategy-ref="TokenRequestScopeLookupStrategy"
         p:allowedScopeLookupStrategy="#{getObject('shibboleth.oidc.AllowedScopeStrategy') ?: getObject('shibboleth.oidc.DefaultAllowedScopeStrategy')}" />
 
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/userinfo/userinfo-beans.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/userinfo/userinfo-beans.xml
index f7c36d64..4af5b42f 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/userinfo/userinfo-beans.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/userinfo/userinfo-beans.xml
@@ -41,7 +41,7 @@
     <bean id="ValidateAccessToken"
         class="net.shibboleth.idp.plugin.oidc.op.userinfo.profile.impl.ValidateAccessToken" scope="prototype" />
         
-    <bean id="ValidateScope" class="net.shibboleth.idp.plugin.oidc.op.profile.impl.ValidateScope" scope="prototype"
+    <bean id="ValidateScope" class="net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl.ValidateScope" scope="prototype"
             p:allowedScopeLookupStrategy="#{getObject('shibboleth.oidc.AllowedScopeStrategy') ?: getObject('shibboleth.oidc.DefaultAllowedScopeStrategy')}">
         <property name="requestedScopeLookupStrategy">
             <null/>
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/DecryptRequestObjectTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/DecryptRequestObjectTest.java
similarity index 99%
rename from idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/DecryptRequestObjectTest.java
rename to idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/DecryptRequestObjectTest.java
index af3f9cde..e218c714 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/DecryptRequestObjectTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/DecryptRequestObjectTest.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package net.shibboleth.idp.plugin.oidc.op.profile.impl;
+package net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl;
 
 import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCAuthenticationResponseContext;
 import net.shibboleth.idp.profile.context.RelyingPartyContext;
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetAuthorizationCodeToResponseContextTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/SetAuthorizationCodeToResponseContextTest.java
similarity index 98%
rename from idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetAuthorizationCodeToResponseContextTest.java
rename to idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/SetAuthorizationCodeToResponseContextTest.java
index 3441dad6..1ccea4c9 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetAuthorizationCodeToResponseContextTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/SetAuthorizationCodeToResponseContextTest.java
@@ -15,11 +15,12 @@
  * limitations under the License.
  */
 
-package net.shibboleth.idp.plugin.oidc.op.profile.impl;
+package net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl;
 
 import net.shibboleth.idp.authn.context.SubjectContext;
 import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCAuthenticationResponseConsentContext;
 import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCAuthenticationResponseTokenClaimsContext;
+import net.shibboleth.idp.plugin.oidc.op.profile.impl.BaseOIDCResponseActionTest;
 import net.shibboleth.idp.plugin.oidc.op.token.support.AuthorizeCodeClaimsSet;
 import net.shibboleth.idp.profile.IdPEventIds;
 import net.shibboleth.idp.profile.context.RelyingPartyContext;
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetRequestObjectToResponseContextTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/SetRequestObjectToResponseContextTest.java
similarity index 97%
rename from idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetRequestObjectToResponseContextTest.java
rename to idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/SetRequestObjectToResponseContextTest.java
index 90cf356a..23cdd3a5 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetRequestObjectToResponseContextTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/SetRequestObjectToResponseContextTest.java
@@ -15,8 +15,9 @@
  * limitations under the License.
  */
 
-package net.shibboleth.idp.plugin.oidc.op.profile.impl;
+package net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl;
 
+import net.shibboleth.idp.plugin.oidc.op.profile.impl.BaseOIDCResponseActionTest;
 import net.shibboleth.idp.profile.testing.ActionTestingSupport;
 import net.shibboleth.oidc.profile.core.OidcEventIds;
 import net.shibboleth.utilities.java.support.httpclient.HttpClientBuilder;
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateCodeChallengeTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateCodeChallengeTest.java
similarity index 98%
rename from idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateCodeChallengeTest.java
rename to idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateCodeChallengeTest.java
index 8983c4bc..3dc086ff 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateCodeChallengeTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateCodeChallengeTest.java
@@ -15,10 +15,11 @@
  * limitations under the License.
  */
 
-package net.shibboleth.idp.plugin.oidc.op.profile.impl;
+package net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl;
 
 import java.net.URISyntaxException;
 
+import net.shibboleth.idp.plugin.oidc.op.profile.impl.BaseOIDCResponseActionTest;
 import net.shibboleth.idp.profile.testing.ActionTestingSupport;
 import net.shibboleth.oidc.profile.core.OidcEventIds;
 import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateRedirectURITest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateRedirectURITest.java
similarity index 97%
rename from idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateRedirectURITest.java
rename to idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateRedirectURITest.java
index 344774c2..00bf129f 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateRedirectURITest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateRedirectURITest.java
@@ -15,12 +15,13 @@
  * limitations under the License.
  */
 
-package net.shibboleth.idp.plugin.oidc.op.profile.impl;
+package net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl;
 
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.Set;
 
+import net.shibboleth.idp.plugin.oidc.op.profile.impl.BaseOIDCResponseActionTest;
 import net.shibboleth.idp.profile.testing.ActionTestingSupport;
 import net.shibboleth.oidc.metadata.context.OIDCMetadataContext;
 import net.shibboleth.oidc.profile.core.OidcEventIds;
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateRequestObjectTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateRequestObjectTest.java
similarity index 99%
rename from idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateRequestObjectTest.java
rename to idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateRequestObjectTest.java
index 5c2d7420..0ba44ab9 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateRequestObjectTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateRequestObjectTest.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package net.shibboleth.idp.plugin.oidc.op.profile.impl;
+package net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl;
 
 import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCAuthenticationResponseContext;
 import net.shibboleth.idp.profile.context.navigate.WebflowRequestContextProfileRequestContextLookup;
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateResponseTypeTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateResponseTypeTest.java
similarity index 96%
rename from idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateResponseTypeTest.java
rename to idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateResponseTypeTest.java
index 3138d21d..3c22c2a2 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateResponseTypeTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateResponseTypeTest.java
@@ -15,13 +15,14 @@
  * limitations under the License.
  */
 
-package net.shibboleth.idp.plugin.oidc.op.profile.impl;
+package net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl;
 
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.HashSet;
 import java.util.Set;
 
+import net.shibboleth.idp.plugin.oidc.op.profile.impl.BaseOIDCResponseActionTest;
 import net.shibboleth.idp.profile.testing.ActionTestingSupport;
 import net.shibboleth.oidc.metadata.context.OIDCMetadataContext;
 import net.shibboleth.oidc.profile.core.OidcEventIds;
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateScopeTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateScopeTest.java
similarity index 97%
rename from idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateScopeTest.java
rename to idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateScopeTest.java
index 2cd22f34..eb36d64d 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateScopeTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateScopeTest.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package net.shibboleth.idp.plugin.oidc.op.profile.impl;
+package net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl;
 
 import java.net.URI;
 import java.net.URISyntaxException;
@@ -23,11 +23,13 @@ import java.time.Instant;
 
 import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCAuthenticationResponseTokenClaimsContext;
 import net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.TokenRequestScopeLookupFunction;
+import net.shibboleth.idp.plugin.oidc.op.profile.impl.BaseOIDCResponseActionTest;
 import net.shibboleth.idp.plugin.oidc.op.token.support.AuthorizeCodeClaimsSet;
 import net.shibboleth.idp.profile.testing.ActionTestingSupport;
 import net.shibboleth.oidc.metadata.context.OIDCMetadataContext;
 import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
 
+import org.opensaml.profile.action.EventIds;
 import org.springframework.webflow.execution.Event;
 import org.testng.Assert;
 import org.testng.annotations.BeforeMethod;
@@ -113,11 +115,8 @@ public class ValidateScopeTest extends BaseOIDCResponseActionTest {
     public void testAuthnNoScopes() throws ComponentInitializationException {
         metaData.setScope(null);
         final Event event = action.execute(requestCtx);
-        ActionTestingSupport.assertProceedEvent(event);
+        ActionTestingSupport.assertEvent(event, EventIds.INVALID_MESSAGE);
         Assert.assertNull(respCtx.getScope());
-        final OIDCAuthenticationResponseTokenClaimsContext tokenClaimsCtx =
-                respCtx.getSubcontext(OIDCAuthenticationResponseTokenClaimsContext.class);
-        Assert.assertNotNull(tokenClaimsCtx);
     }
 
     /**
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/AuthorizeFlowTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/AuthorizeFlowTest.java
index 16c84fa4..fbc73fec 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/AuthorizeFlowTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/AuthorizeFlowTest.java
@@ -18,6 +18,8 @@
 package net.shibboleth.idp.plugin.oidc.op.profile.flow;
 
 import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
 import java.text.ParseException;
 import java.time.Duration;
 import java.time.Instant;
@@ -39,6 +41,8 @@ import com.nimbusds.jwt.JWT;
 import com.nimbusds.jwt.JWTClaimsSet;
 import com.nimbusds.jwt.PlainJWT;
 import com.nimbusds.jwt.SignedJWT;
+import com.nimbusds.oauth2.sdk.AuthorizationResponse;
+import com.nimbusds.oauth2.sdk.AuthorizationSuccessResponse;
 import com.nimbusds.oauth2.sdk.Response;
 import com.nimbusds.oauth2.sdk.Scope;
 import com.nimbusds.openid.connect.sdk.AuthenticationErrorResponse;
@@ -52,6 +56,7 @@ import net.shibboleth.idp.plugin.oidc.op.token.support.AccessTokenClaimsSet;
 import net.shibboleth.idp.plugin.oidc.op.token.support.AuthorizeCodeClaimsSet;
 import net.shibboleth.idp.session.SessionException;
 import net.shibboleth.oidc.profile.core.OidcError;
+import net.shibboleth.utilities.java.support.collection.Pair;
 import net.shibboleth.utilities.java.support.security.DataSealerException;
 
 /**
@@ -85,9 +90,11 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
 
     @Test
     public void testWithAuthorizationCodeFlow() throws IOException, SessionException {
+        setRequestParameters(List.of(new Pair<>("client_id", "mockClientId"),
+                new Pair<>("response_type", "code"),
+                new Pair<>("scope", "openid profile"),
+                new Pair<>("redirect_uri", redirectUri)));
         request.setMethod("GET");
-        request.setQueryString("client_id=mockClientId&response_type=code&scope=openid%20profile&redirect_uri="
-                + redirectUri);
         storeMetadata(storageService, clientId, clientSecret, scope, redirectUri);
 
         initializeThreadLocals();
@@ -106,8 +113,11 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     @Test
     public void testWithAuthorizationCodeFlowAndResource() throws IOException, SessionException {
         request.setMethod("GET");
-        request.setQueryString("client_id=mockClientId&response_type=code&scope=openid%20profile&resource=" + resource +
-                "&redirect_uri=" + redirectUri);
+        setRequestParameters(List.of(new Pair<>("client_id", "mockClientId"),
+                new Pair<>("response_type", "code"),
+                new Pair<>("scope", "openid profile"),
+                new Pair<>("redirect_uri", redirectUri),
+                new Pair<>("resource", resource)));
         storeMetadata(storageService, clientId, clientSecret, scope, redirectUri);
 
         initializeThreadLocals();
@@ -126,8 +136,10 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     @Test
     public void testWithAuthorizationCodeFlowIssInResponse() throws IOException, SessionException {
         request.setMethod("GET");
-        request.setQueryString("client_id=mockClientIdIssInResponse&response_type=code&scope=openid%20profile&redirect_uri="
-                + redirectUri);
+        setRequestParameters(List.of(new Pair<>("client_id", "mockClientIdIssInResponse"),
+                new Pair<>("response_type", "code"),
+                new Pair<>("scope", "openid profile"),
+                new Pair<>("redirect_uri", redirectUri)));
         storeMetadata(storageService, clientIdIssInResponse, clientSecret, scope, redirectUri);
 
         initializeThreadLocals();
@@ -147,20 +159,29 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     @Test
     public void testWithAuthorizationCodeFlowNoOpenid() throws IOException, SessionException {
         request.setMethod("GET");
-        request.setQueryString("client_id=mockClientId&response_type=code&scope=profile&redirect_uri="
-                + redirectUri);
+        setRequestParameters(List.of(new Pair<>("client_id", "mockClientId"),
+                new Pair<>("response_type", "code"),
+                new Pair<>("scope", "profile"),
+                new Pair<>("redirect_uri", redirectUri)));
         storeMetadata(storageService, clientId, clientSecret, scope, redirectUri);
 
         initializeThreadLocals();
         
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
-        Assert.assertEquals("ErrorView", result.getOutcome().getId());
+        final AuthorizationResponse responseMessage = parseSuccessResponse(result, AuthorizationResponse.class);
+        final AuthorizationSuccessResponse successResponse = responseMessage.toSuccessResponse();
+        Assert.assertEquals(successResponse.getRedirectionURI().toString(), redirectUri);
+        Assert.assertNull(successResponse.getAccessToken());
+        Assert.assertNotNull(successResponse.getAuthorizationCode());
+        Assert.assertNull(successResponse.getIssuer());
     }
 
     @Test
     public void testWithAuthorizationCodeFlowNoRedirectURI() throws IOException, SessionException {
         request.setMethod("GET");
-        request.setQueryString("client_id=mockClientId&response_type=code&scope=openid%20profile");
+        setRequestParameters(List.of(new Pair<>("client_id", "mockClientId"),
+                new Pair<>("response_type", "code"),
+                new Pair<>("scope", "openid profile")));
         storeMetadata(storageService, clientId, clientSecret, scope, redirectUri);
 
         initializeThreadLocals();
@@ -172,8 +193,10 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     @Test
     public void testWithImplicitFlowNoNonce() throws IOException, SessionException {
         request.setMethod("GET");
-        request.setQueryString("client_id=mockClientId&response_type=id_token&scope=openid%20profile&redirect_uri="
-                + redirectUri);
+        setRequestParameters(List.of(new Pair<>("client_id", "mockClientId"),
+                new Pair<>("response_type", "id_token"),
+                new Pair<>("scope", "openid profile"),
+                new Pair<>("redirect_uri", redirectUri)));
         storeMetadata(storageService, clientId, clientSecret, scope, redirectUri);
 
         initializeThreadLocals();
@@ -185,8 +208,11 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     @Test
     public void testWithImplicitFlow() throws IOException, SessionException {
         request.setMethod("GET");
-        request.setQueryString("client_id=mockClientId&response_type=id_token&scope=openid%20profile&redirect_uri="
-                + redirectUri + "&nonce=idhas3h23hi13h1o2i32");
+        setRequestParameters(List.of(new Pair<>("client_id", "mockClientId"),
+                new Pair<>("response_type", "id_token"),
+                new Pair<>("scope", "openid profile"),
+                new Pair<>("redirect_uri", redirectUri),
+                new Pair<>("nonce", "idhas3h23hi13h1o2i32")));
         storeMetadata(storageService, clientId, clientSecret, scope, redirectUri);
 
         initializeThreadLocals();
@@ -205,8 +231,11 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     @Test
     public void testWithImplicitFlowIssInResponse() throws IOException, SessionException {
         request.setMethod("GET");
-        request.setQueryString("client_id=mockClientIdIssInResponse&response_type=id_token&scope=openid%20profile&redirect_uri="
-                + redirectUri + "&nonce=idhas3h23hi13h1o2i32");
+        setRequestParameters(List.of(new Pair<>("client_id", "mockClientIdIssInResponse"),
+                new Pair<>("response_type", "id_token"),
+                new Pair<>("scope", "openid profile"),
+                new Pair<>("redirect_uri", redirectUri),
+                new Pair<>("nonce", "idhas3h23hi13h1o2i32")));
         storeMetadata(storageService, clientIdIssInResponse, clientSecret, scope, redirectUri);
 
         initializeThreadLocals();
@@ -226,8 +255,12 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     @Test
     public void testWithImplicitFlowAndResource() throws IOException, SessionException {
         request.setMethod("GET");
-        request.setQueryString("client_id=mockClientId&response_type=id_token&scope=openid%20profile&resource=" + resource
-                + "&redirect_uri=" + redirectUri + "&nonce=idhas3h23hi13h1o2i32");
+        setRequestParameters(List.of(new Pair<>("client_id", "mockClientId"),
+                new Pair<>("response_type", "id_token"),
+                new Pair<>("scope", "openid profile"),
+                new Pair<>("redirect_uri", redirectUri),
+                new Pair<>("resource", resource),
+                new Pair<>("nonce", "idhas3h23hi13h1o2i32")));
         storeMetadata(storageService, clientId, clientSecret, scope, redirectUri);
 
         initializeThreadLocals();
@@ -246,8 +279,11 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     @Test
     public void testWithImplicitTokenFlow() throws IOException, SessionException, DataSealerException, ParseException {
         request.setMethod("GET");
-        request.setQueryString("client_id=mockClientId&response_type=id_token+token&scope=openid%20profile"
-                + "&redirect_uri="+ redirectUri + "&nonce=idhas3h23hi13h1o2i32");
+        setRequestParameters(List.of(new Pair<>("client_id", "mockClientId"),
+                new Pair<>("response_type", "id_token token"),
+                new Pair<>("scope", "openid profile"),
+                new Pair<>("redirect_uri", redirectUri),
+                new Pair<>("nonce", "idhas3h23hi13h1o2i32")));
         storeMetadata(storageService, clientId, clientSecret, scope, redirectUri);
 
         initializeThreadLocals();
@@ -274,8 +310,11 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     @Test
     public void testWithImplicitTokenFlowIssInResponse() throws IOException, SessionException, DataSealerException, ParseException {
         request.setMethod("GET");
-        request.setQueryString("client_id=mockClientIdIssInResponse&response_type=id_token+token&scope=openid%20profile"
-                + "&redirect_uri="+ redirectUri + "&nonce=idhas3h23hi13h1o2i32");
+        setRequestParameters(List.of(new Pair<>("client_id", "mockClientIdIssInResponse"),
+                new Pair<>("response_type", "id_token token"),
+                new Pair<>("scope", "openid profile"),
+                new Pair<>("redirect_uri", redirectUri),
+                new Pair<>("nonce", "idhas3h23hi13h1o2i32")));
         storeMetadata(storageService, clientIdIssInResponse, clientSecret, scope, redirectUri);
 
         initializeThreadLocals();
@@ -303,8 +342,12 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     @Test
     public void testWithImplicitTokenFlowAndResource() throws IOException, SessionException, ParseException {
         request.setMethod("GET");
-        request.setQueryString("client_id=mockClientId&response_type=id_token+token&scope=openid%20profile"
-                + "&resource=" + resource + "&redirect_uri="+ redirectUri + "&nonce=idhas3h23hi13h1o2i32");
+        setRequestParameters(List.of(new Pair<>("client_id", "mockClientId"),
+                new Pair<>("response_type", "id_token token"),
+                new Pair<>("scope", "openid profile"),
+                new Pair<>("redirect_uri", redirectUri),
+                new Pair<>("resource", resource),
+                new Pair<>("nonce", "idhas3h23hi13h1o2i32")));
         storeMetadata(storageService, clientId, clientSecret, scope, redirectUri);
 
         initializeThreadLocals();
@@ -330,8 +373,10 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     @Test
     public void testWithImplicitTokenFlowNoNonce() throws IOException, SessionException {
         request.setMethod("GET");
-        request.setQueryString("client_id=mockClientId&response_type=id_token+token&scope=openid%20profile"
-                + "&redirect_uri="+ redirectUri);
+        setRequestParameters(List.of(new Pair<>("client_id", "mockClientId"),
+                new Pair<>("response_type", "id_token token"),
+                new Pair<>("scope", "openid profile"),
+                new Pair<>("redirect_uri", redirectUri)));
         storeMetadata(storageService, clientId, clientSecret, scope, redirectUri);
 
         initializeThreadLocals();
@@ -341,22 +386,47 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     }
 
     @Test
-    public void testWithImplicitFlowNoOpenIdScope() throws IOException, SessionException {
+    public void testWithImplicitOidcFlowNoOpenIdScopeRequested() throws IOException, SessionException {
         request.setMethod("GET");
-        request.setQueryString("client_id=mockClientId&response_type=id_token&scope=profile&redirect_uri="
-                + redirectUri + "&nonce=idhas3h23hi13h1o2i32");
+        setRequestParameters(List.of(new Pair<>("client_id", "mockClientId"),
+                new Pair<>("response_type", "id_token token"),
+                new Pair<>("scope", "profile"),
+                new Pair<>("redirect_uri", redirectUri),
+                new Pair<>("nonce", "idhas3h23hi13h1o2i32")));
         storeMetadata(storageService, clientId, clientSecret, scope, redirectUri);
 
         initializeThreadLocals();
         
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
-        Assert.assertEquals("ErrorView", result.getOutcome().getId());
+        assertErrorCode(result, "unsupported_response_type");
+        assertErrorDescriptionContains(result, "Unsupported response");
+        assertErrorResponseWithNoIssuer(result);
+    }
+
+    @Test
+    public void testWithImplicitOidcFlowNoOpenIdScopeRegistered() throws IOException, SessionException {
+        request.setMethod("GET");
+        setRequestParameters(List.of(new Pair<>("client_id", "mockClientId"),
+                new Pair<>("response_type", "id_token token"),
+                new Pair<>("scope", "openid profile"),
+                new Pair<>("redirect_uri", redirectUri),
+                new Pair<>("nonce", "idhas3h23hi13h1o2i32")));
+        storeMetadata(storageService, clientId, clientSecret, Scope.parse("profile"), redirectUri);
+
+        initializeThreadLocals();
+        
+        final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+        assertErrorCode(result, "invalid_request");
+        assertErrorDescriptionContains(result, "InvalidMessage");
+        assertErrorResponseWithNoIssuer(result);
     }
 
     @Test
     public void testWithImplicitFlowNoRedirectURI() throws IOException, SessionException {
         request.setMethod("GET");
-        request.setQueryString("client_id=mockClientId&response_type=id_token&scope=openid%20profile");
+        setRequestParameters(List.of(new Pair<>("client_id", "mockClientId"),
+                new Pair<>("response_type", "id_token token"),
+                new Pair<>("scope", "openid profile")));
         storeMetadata(storageService, clientId, clientSecret, scope, redirectUri);
 
         initializeThreadLocals();
@@ -368,8 +438,11 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     @Test
     public void testWithHybridIdTokenFlow() throws IOException, SessionException {
         request.setMethod("GET");
-        request.setQueryString("client_id=mockClientId&response_type=code+id_token&scope=openid%20profile"
-                + "&redirect_uri=" + redirectUri + "&nonce=idhas3h23hi13h1o2i32");
+        setRequestParameters(List.of(new Pair<>("client_id", "mockClientId"),
+                new Pair<>("response_type", "code id_token"),
+                new Pair<>("scope", "openid profile"),
+                new Pair<>("redirect_uri", redirectUri),
+                new Pair<>("nonce", "idhas3h23hi13h1o2i32")));
         storeMetadata(storageService, clientId, clientSecret, scope, redirectUri);
 
         initializeThreadLocals();
@@ -391,8 +464,11 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     @Test
     public void testWithHybridIdTokenFlowIssInResponse() throws IOException, SessionException {
         request.setMethod("GET");
-        request.setQueryString("client_id=mockClientIdIssInResponse&response_type=code+id_token&scope=openid%20profile"
-                + "&redirect_uri=" + redirectUri + "&nonce=idhas3h23hi13h1o2i32");
+        setRequestParameters(List.of(new Pair<>("client_id", "mockClientIdIssInResponse"),
+                new Pair<>("response_type", "code id_token"),
+                new Pair<>("scope", "openid profile"),
+                new Pair<>("redirect_uri", redirectUri),
+                new Pair<>("nonce", "idhas3h23hi13h1o2i32")));
         storeMetadata(storageService, clientIdIssInResponse, clientSecret, scope, redirectUri);
 
         initializeThreadLocals();
@@ -415,8 +491,12 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     @Test
     public void testWithHybridIdTokenFlowAndResource() throws IOException, SessionException {
         request.setMethod("GET");
-        request.setQueryString("client_id=mockClientId&response_type=code+id_token&scope=openid%20profile"
-                + "&resource=" + resource + "&redirect_uri=" + redirectUri + "&nonce=idhas3h23hi13h1o2i32");
+        setRequestParameters(List.of(new Pair<>("client_id", "mockClientId"),
+                new Pair<>("response_type", "code id_token"),
+                new Pair<>("scope", "openid profile"),
+                new Pair<>("resource", resource),
+                new Pair<>("redirect_uri", redirectUri),
+                new Pair<>("nonce", "idhas3h23hi13h1o2i32")));
         storeMetadata(storageService, clientId, clientSecret, scope, redirectUri);
 
         initializeThreadLocals();
@@ -437,8 +517,10 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     @Test
     public void testWithHybridIdTokenFlowNoNonce() throws IOException, SessionException {
         request.setMethod("GET");
-        request.setQueryString("client_id=mockClientId&response_type=code+id_token&scope=openid%20profile"
-                + "&redirect_uri=" + redirectUri);
+        setRequestParameters(List.of(new Pair<>("client_id", "mockClientId"),
+                new Pair<>("response_type", "code id_token"),
+                new Pair<>("scope", "openid profile"),
+                new Pair<>("redirect_uri", redirectUri)));
         storeMetadata(storageService, clientId, clientSecret, scope, redirectUri);
 
         initializeThreadLocals();
@@ -450,8 +532,10 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     @Test
     public void testWithHybridTokenFlow() throws IOException, SessionException, ParseException, DataSealerException {
         request.setMethod("GET");
-        request.setQueryString("client_id=mockClientId&response_type=code+token&scope=openid%20profile"
-                + "&redirect_uri="+ redirectUri);
+        setRequestParameters(List.of(new Pair<>("client_id", "mockClientId"),
+                new Pair<>("response_type", "code token"),
+                new Pair<>("scope", "openid profile"),
+                new Pair<>("redirect_uri", redirectUri)));
         storeMetadata(storageService, clientId, clientSecret, scope, redirectUri);
 
         initializeThreadLocals();
@@ -476,11 +560,48 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
         Assert.assertNull(successResponse.getIssuer());
     }
 
+    @Test
+    public void testWithHybridTokenFlowNoOpenidScopeRequested() throws IOException, SessionException, ParseException, DataSealerException {
+        request.setMethod("GET");
+        setRequestParameters(List.of(new Pair<>("client_id", "mockClientId"),
+                new Pair<>("response_type", "code token"),
+                new Pair<>("scope", "profile"),
+                new Pair<>("redirect_uri", redirectUri)));
+        storeMetadata(storageService, clientId, clientSecret, scope, redirectUri);
+
+        initializeThreadLocals();
+        
+        final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+        assertErrorCode(result, "unsupported_response_type");
+        assertErrorDescriptionContains(result, "Unsupported response");
+        assertErrorResponseWithNoIssuer(result);
+    }
+
+    @Test
+    public void testWithHybridTokenFlowNoOpenidScopeRegistered() throws IOException, SessionException, ParseException, DataSealerException {
+        request.setMethod("GET");
+        setRequestParameters(List.of(new Pair<>("client_id", "mockClientId"),
+                new Pair<>("response_type", "code token"),
+                new Pair<>("scope", "openid profile"),
+                new Pair<>("redirect_uri", redirectUri)));
+        storeMetadata(storageService, clientId, clientSecret, Scope.parse("profile"), redirectUri);
+
+        initializeThreadLocals();
+        
+        final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+        assertErrorCode(result, "invalid_request");
+        assertErrorDescriptionContains(result, "InvalidMessage");
+        assertErrorResponseWithNoIssuer(result);
+    }
+
     @Test
     public void testWithHybridTokenFlowAndResource() throws IOException, SessionException, ParseException {
         request.setMethod("GET");
-        request.setQueryString("client_id=mockClientId&response_type=code+token&scope=openid%20profile"
-                + "&resource=" + resource + "&redirect_uri="+ redirectUri);
+        setRequestParameters(List.of(new Pair<>("client_id", "mockClientId"),
+                new Pair<>("response_type", "code token"),
+                new Pair<>("scope", "openid profile"),
+                new Pair<>("resource", resource),
+                new Pair<>("redirect_uri", redirectUri)));
         storeMetadata(storageService, clientId, clientSecret, scope, redirectUri);
 
         initializeThreadLocals();
@@ -507,8 +628,11 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     @Test
     public void testWithHybridIdTokenTokenFlow() throws IOException, SessionException, ParseException, DataSealerException {
         request.setMethod("GET");
-        request.setQueryString("client_id=mockClientId&response_type=code+id_token+token&scope=openid%20profile"
-                + "&redirect_uri="+ redirectUri + "&nonce=idhas3h23hi13h1o2i32");
+        setRequestParameters(List.of(new Pair<>("client_id", "mockClientId"),
+                new Pair<>("response_type", "code id_token token"),
+                new Pair<>("scope", "openid profile"),
+                new Pair<>("redirect_uri", redirectUri),
+                new Pair<>("nonce", "idhas3h23hi13h1o2i32")));
         storeMetadata(storageService, clientId, clientSecret, scope, redirectUri);
 
         initializeThreadLocals();
@@ -532,12 +656,51 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
         Assert.assertNull(token.getClaimsSet().getStringClaim("eduPersonScopedAffiliation"));
     }
 
+    @Test
+    public void testWithHybridIdTokenTokenFlowNoOpenidScopeRequested() throws IOException, SessionException, ParseException, DataSealerException {
+        request.setMethod("GET");
+        setRequestParameters(List.of(new Pair<>("client_id", "mockClientId"),
+                new Pair<>("response_type", "code id_token token"),
+                new Pair<>("scope", "profile"),
+                new Pair<>("redirect_uri", redirectUri),
+                new Pair<>("nonce", "idhas3h23hi13h1o2i32")));
+        storeMetadata(storageService, clientId, clientSecret, scope, redirectUri);
+
+        initializeThreadLocals();
+        
+        final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+        assertErrorCode(result, "unsupported_response_type");
+        assertErrorDescriptionContains(result, "Unsupported response");
+        assertErrorResponseWithNoIssuer(result);
+    }
+
+    @Test
+    public void testWithHybridIdTokenTokenFlowNoOpenidScopeRegistered() throws IOException, SessionException, ParseException, DataSealerException {
+        request.setMethod("GET");
+        setRequestParameters(List.of(new Pair<>("client_id", "mockClientId"),
+                new Pair<>("response_type", "code id_token token"),
+                new Pair<>("scope", "openid profile"),
+                new Pair<>("redirect_uri", redirectUri),
+                new Pair<>("nonce", "idhas3h23hi13h1o2i32")));
+        storeMetadata(storageService, clientId, clientSecret, Scope.parse("profile"), redirectUri);
+
+        initializeThreadLocals();
+        
+        final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+        assertErrorCode(result, "invalid_request");
+        assertErrorDescriptionContains(result, "InvalidMessage");
+        assertErrorResponseWithNoIssuer(result);
+    }
+
     @Test
     public void testWithHybridIdTokenTokenFlowWithCustomTokenClaim() throws IOException, SessionException,
         ParseException, DataSealerException, com.nimbusds.oauth2.sdk.ParseException {
         request.setMethod("GET");
-        request.setQueryString("client_id=mockClientIdCustomTokens&response_type=code+id_token+token&scope=openid%20profile"
-                + "&redirect_uri="+ redirectUri + "&nonce=idhas3h23hi13h1o2i32");
+        setRequestParameters(List.of(new Pair<>("client_id", "mockClientIdCustomTokens"),
+                new Pair<>("response_type", "code id_token token"),
+                new Pair<>("scope", "openid profile"),
+                new Pair<>("redirect_uri", redirectUri),
+                new Pair<>("nonce", "idhas3h23hi13h1o2i32")));
         storeMetadata(storageService, clientIdCustomTokens, clientSecret, scope, redirectUri);
 
         initializeThreadLocals();
@@ -574,8 +737,12 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     @Test
     public void testWithHybridIdTokenTokenFlowAndResource() throws IOException, SessionException, ParseException {
         request.setMethod("GET");
-        request.setQueryString("client_id=mockClientId&response_type=code+id_token+token&scope=openid%20profile"
-                + "&resource=" + resource + "&redirect_uri="+ redirectUri + "&nonce=idhas3h23hi13h1o2i32");
+        setRequestParameters(List.of(new Pair<>("client_id", "mockClientId"),
+                new Pair<>("response_type", "code id_token token"),
+                new Pair<>("scope", "openid profile"),
+                new Pair<>("resource", resource),
+                new Pair<>("redirect_uri", redirectUri),
+                new Pair<>("nonce", "idhas3h23hi13h1o2i32")));
         storeMetadata(storageService, clientId, clientSecret, scope, redirectUri);
 
         initializeThreadLocals();
@@ -603,8 +770,10 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     @Test
     public void testWithHybridIdTokenTokenFlowNoNonce() throws IOException, SessionException {
         request.setMethod("GET");
-        request.setQueryString("client_id=mockClientId&response_type=code+id_token+token&scope=openid%20profile"
-                + "&redirect_uri="+ redirectUri);
+        setRequestParameters(List.of(new Pair<>("client_id", "mockClientId"),
+                new Pair<>("response_type", "code id_token token"),
+                new Pair<>("scope", "openid profile"),
+                new Pair<>("redirect_uri", redirectUri)));
         storeMetadata(storageService, clientId, clientSecret, scope, redirectUri);
 
         initializeThreadLocals();
@@ -616,8 +785,10 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     @Test
     public void testWithAuthorizationCodeFlowUnforcedPKCE() throws IOException, SessionException {
         request.setMethod("GET");
-        request.setQueryString("client_id=mockClientIdPKCEPlainUnforced&response_type=code&scope=openid%20profile&redirect_uri="
-                + redirectUri);
+        setRequestParameters(List.of(new Pair<>("client_id", "mockClientIdPKCEPlainUnforced"),
+                new Pair<>("response_type", "code"),
+                new Pair<>("scope", "openid profile"),
+                new Pair<>("redirect_uri", redirectUri)));
         storeMetadata(storageService, "mockClientIdPKCEPlainUnforced", clientSecret, scope, redirectUri);
 
         initializeThreadLocals();
@@ -635,8 +806,10 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     @Test
     public void testWithAuthorizationCodeFlowForcedPlainPKCEMissingChallenge() throws IOException, SessionException {
         request.setMethod("GET");
-        request.setQueryString("client_id=mockClientIdPKCEPlain&response_type=code&scope=openid%20profile&redirect_uri="
-                + redirectUri);
+        setRequestParameters(List.of(new Pair<>("client_id", "mockClientIdPKCEPlain"),
+                new Pair<>("response_type", "code"),
+                new Pair<>("scope", "openid profile"),
+                new Pair<>("redirect_uri", redirectUri)));
         storeMetadata(storageService, "mockClientIdPKCEPlain", clientSecret, scope, redirectUri);
 
         initializeThreadLocals();
@@ -649,8 +822,12 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     @Test
     public void testWithAuthorizationCodeFlowForcedPlainPKCEUnknownChallenge() throws IOException, SessionException {
         request.setMethod("GET");
-        request.setQueryString("client_id=mockClientIdPKCEPlain&response_type=code&scope=openid%20profile&redirect_uri="
-                + redirectUri + "&code_challenge=osdfojsfod&code_challenge_method=unsupported");
+        setRequestParameters(List.of(new Pair<>("client_id", "mockClientIdPKCEPlain"),
+                new Pair<>("response_type", "code"),
+                new Pair<>("scope", "openid profile"),
+                new Pair<>("redirect_uri", redirectUri),
+                new Pair<>("code_challenge", "osdfojsfod"),
+                new Pair<>("code_challenge_method", "unsupported")));
         storeMetadata(storageService, "mockClientIdPKCEPlain", clientSecret, scope, redirectUri);
 
         initializeThreadLocals();
@@ -663,8 +840,12 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     @Test
     public void testWithAuthorizationCodeFlowForcedPlainPKCEValidChallenge() throws IOException, SessionException {
         request.setMethod("GET");
-        request.setQueryString("client_id=mockClientIdPKCEPlain&response_type=code&scope=openid%20profile&redirect_uri="
-                + redirectUri + "&code_challenge=osdfojsfod&code_challenge_method=plain");
+        setRequestParameters(List.of(new Pair<>("client_id", "mockClientIdPKCEPlain"),
+                new Pair<>("response_type", "code"),
+                new Pair<>("scope", "openid profile"),
+                new Pair<>("redirect_uri", redirectUri),
+                new Pair<>("code_challenge", "osdfojsfod"),
+                new Pair<>("code_challenge_method", "plain")));
         storeMetadata(storageService, "mockClientIdPKCEPlain", clientSecret, scope, redirectUri);
 
         initializeThreadLocals();
@@ -682,8 +863,12 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     @Test
     public void testWithAuthorizationCodeFlowForcedS256PKCEPlainChallenge() throws IOException, SessionException {
         request.setMethod("GET");
-        request.setQueryString("client_id=mockClientIdPKCES256&response_type=code&scope=openid%20profile&redirect_uri="
-                + redirectUri + "&code_challenge=osdfojsfod&code_challenge_method=plain");
+        setRequestParameters(List.of(new Pair<>("client_id", "mockClientIdPKCES256"),
+                new Pair<>("response_type", "code"),
+                new Pair<>("scope", "openid profile"),
+                new Pair<>("redirect_uri", redirectUri),
+                new Pair<>("code_challenge", "osdfojsfod"),
+                new Pair<>("code_challenge_method", "plain")));
         storeMetadata(storageService, "mockClientIdPKCES256", clientSecret, scope, redirectUri);
 
         initializeThreadLocals();
@@ -696,8 +881,12 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     @Test
     public void testWithAuthorizationCodeFlowForcedS256PKCEUnknownChallenge() throws IOException, SessionException {
         request.setMethod("GET");
-        request.setQueryString("client_id=mockClientIdPKCES256&response_type=code&scope=openid%20profile&redirect_uri="
-                + redirectUri + "&code_challenge=osdfojsfod&code_challenge_method=unknown");
+        setRequestParameters(List.of(new Pair<>("client_id", "mockClientIdPKCES256"),
+                new Pair<>("response_type", "code"),
+                new Pair<>("scope", "openid profile"),
+                new Pair<>("redirect_uri", redirectUri),
+                new Pair<>("code_challenge", "osdfojsfod"),
+                new Pair<>("code_challenge_method", "unknown")));
         storeMetadata(storageService, "mockClientIdPKCES256", clientSecret, scope, redirectUri);
 
         initializeThreadLocals();
@@ -710,8 +899,12 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     @Test
     public void testWithAuthorizationCodeFlowForcedS256PKCEValidChallenge() throws IOException, SessionException {
         request.setMethod("GET");
-        request.setQueryString("client_id=mockClientIdPKCES256&response_type=code&scope=openid%20profile&redirect_uri="
-                + redirectUri + "&code_challenge=osdfojsfod&code_challenge_method=S256");
+        setRequestParameters(List.of(new Pair<>("client_id", "mockClientIdPKCES256"),
+                new Pair<>("response_type", "code"),
+                new Pair<>("scope", "openid profile"),
+                new Pair<>("redirect_uri", redirectUri),
+                new Pair<>("code_challenge", "osdfojsfod"),
+                new Pair<>("code_challenge_method", "S256")));
         storeMetadata(storageService, "mockClientIdPKCES256", clientSecret, scope, redirectUri);
 
         initializeThreadLocals();
@@ -727,45 +920,47 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     }
 
     @Test
-    public void testWithAuthorizationCodeFlowNoScopes() throws IOException, SessionException {
+    public void testWithAuthorizationCodeFlowNoScopesRegistered() throws IOException, SessionException {
         request.setMethod("GET");
-        request.setQueryString("client_id=mockClientId&response_type=code&scope=openid%20profile&redirect_uri="
-                + redirectUri);
+        setRequestParameters(List.of(new Pair<>("client_id", "mockClientId"),
+                new Pair<>("response_type", "code"),
+                new Pair<>("scope", "openid profile"),
+                new Pair<>("redirect_uri", redirectUri)));
         storeMetadata(storageService, clientId, clientSecret, null, redirectUri);
 
         initializeThreadLocals();
 
-        // TODO: Speculation this should fail more explicitly if openid scope isn't valid.
-        
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
         assertErrorCode(result, "invalid_request");
-        assertErrorDescriptionContains(result, "InvalidSubject");
+        assertErrorDescriptionContains(result, "InvalidMessage");
         assertErrorResponseWithNoIssuer(result);
     }
 
     @Test
-    public void testWithAuthorizationCodeFlowNoScopesIssInResponse() throws IOException, SessionException {
+    public void testWithAuthorizationCodeFlowNoScopesRegisteredIssInResponse() throws IOException, SessionException {
         request.setMethod("GET");
-        request.setQueryString("client_id=mockClientIdIssInResponse&response_type=code&scope=openid%20profile&redirect_uri="
-                + redirectUri);
+        setRequestParameters(List.of(new Pair<>("client_id", "mockClientIdIssInResponse"),
+                new Pair<>("response_type", "code"),
+                new Pair<>("scope", "openid profile"),
+                new Pair<>("redirect_uri", redirectUri)));
         storeMetadata(storageService, clientIdIssInResponse, clientSecret, null, redirectUri);
 
         initializeThreadLocals();
-
-        // TODO: Speculation this should fail more explicitly if openid scope isn't valid.
         
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
         assertErrorCode(result, "invalid_request");
-        assertErrorDescriptionContains(result, "InvalidSubject");
+        assertErrorDescriptionContains(result, "InvalidMessage");
         assertErrorResponseWithIssuer(result);
     }
 
     @Test
     public void testWithAuthorizationCodeFlowWithIDTokenClaims() throws IOException, SessionException, DataSealerException, ParseException {
         request.setMethod("GET");
-        request.setQueryString("client_id=mockClientId&response_type=code&scope=openid%20profile"
-                + "&claims=%7B%22id_token%22%3A%7B%22email%22%3A%7B%22essential%22%3Atrue%7D%7D%7D"
-                + "&redirect_uri=" + redirectUri);
+        setRequestParameters(List.of(new Pair<>("client_id", "mockClientId"),
+                new Pair<>("response_type", "code"),
+                new Pair<>("scope", "openid profile"),
+                new Pair<>("claims", "{\"id_token\":{\"email\":{\"essential\":true}}}"),
+                new Pair<>("redirect_uri", redirectUri)));
         storeMetadata(storageService, clientId, clientSecret, scope, redirectUri);
 
         initializeThreadLocals();
@@ -794,9 +989,11 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     @Test
     public void testWithAuthorizationCodeFlowWithUIClaims() throws IOException, SessionException, DataSealerException, ParseException {
         request.setMethod("GET");
-        request.setQueryString("client_id=mockClientId&response_type=code&scope=openid%20profile"
-                + "&claims=%7B%22userinfo%22%3A%7B%22email%22%3A%7B%22essential%22%3Atrue%7D%7D%7D"
-                + "&redirect_uri=" + redirectUri);
+        setRequestParameters(List.of(new Pair<>("client_id", "mockClientId"),
+                new Pair<>("response_type", "code"),
+                new Pair<>("scope", "openid profile"),
+                new Pair<>("claims", "{\"userinfo\":{\"email\":{\"essential\":true}}}"),
+                new Pair<>("redirect_uri", redirectUri)));
         storeMetadata(storageService, clientId, clientSecret, scope, redirectUri);
 
         initializeThreadLocals();
@@ -825,9 +1022,11 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     @Test
     public void testWithAuthorizationCodeFlowWithCustomClaimInCode() throws IOException, SessionException, DataSealerException, ParseException {
         request.setMethod("GET");
-        request.setQueryString("client_id=mockClientIdCustomTokens&response_type=code&scope=openid%20profile"
-                + "&claims=%7B%22userinfo%22%3A%7B%22email%22%3A%7B%22essential%22%3Atrue%7D%7D%7D"
-                + "&redirect_uri=" + redirectUri);
+        setRequestParameters(List.of(new Pair<>("client_id", "mockClientIdCustomTokens"),
+                new Pair<>("response_type", "code"),
+                new Pair<>("scope", "openid profile"),
+                new Pair<>("claims", "{\"userinfo\":{\"email\":{\"essential\":true}}}"),
+                new Pair<>("redirect_uri", redirectUri)));
         storeMetadata(storageService, clientIdCustomTokens, clientSecret, scope, redirectUri);
 
         initializeThreadLocals();
@@ -852,8 +1051,10 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     @Test
     public void testWithAuthorizationCodeFlowUsingSAMLMetadata() throws IOException, SessionException {
         request.setMethod("GET");
-        request.setQueryString("client_id=mockSamlClientId&response_type=code&scope=openid%20profile&redirect_uri="
-                + redirectUri);
+        setRequestParameters(List.of(new Pair<>("client_id", "mockSamlClientId"),
+                new Pair<>("response_type", "code"),
+                new Pair<>("scope", "openid profile"),
+                new Pair<>("redirect_uri", redirectUri)));
         storeMetadata(storageService, clientId, clientSecret, scope, redirectUri);
 
         initializeThreadLocals();
@@ -872,8 +1073,10 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     @Test
     public void testWithAuthorizationCodeFlowUsingUntrustedRP() throws IOException, SessionException {
         request.setMethod("GET");
-        request.setQueryString("client_id=notTrusted&response_type=code&scope=openid%20profile&redirect_uri="
-                + redirectUri);
+        setRequestParameters(List.of(new Pair<>("client_id", "notTrusted"),
+                new Pair<>("response_type", "code"),
+                new Pair<>("scope", "openid profile"),
+                new Pair<>("redirect_uri", redirectUri)));
         initializeThreadLocals();
 
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
@@ -904,8 +1107,11 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
                 .build();
         final PlainJWT requestObject = new PlainJWT(ro);
         request.setMethod("GET");
-        request.setQueryString("client_id=mockClientId&response_type=code&scope=openid%20profile&redirect_uri="
-                + "https://invalid.org/cb&request=" + requestObject.serialize());
+        setRequestParameters(List.of(new Pair<>("client_id", "mockClientId"),
+                new Pair<>("response_type", "code"),
+                new Pair<>("scope", "openid profile"),
+                new Pair<>("redirect_uri", "https://invalid.org/cb"),
+                new Pair<>("request", requestObject.serialize())));
         storeMetadata(storageService, clientId, clientSecret, scope, redirectUri);
 
         initializeThreadLocals();
@@ -953,8 +1159,11 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
         final PlainJWT requestObject = new PlainJWT(ro);
 
         request.setMethod("GET");
-        request.setQueryString("client_id=mockClientId&response_type=code&scope=openid%20profile&redirect_uri="
-                + "https://invalid.org/cb&request=" + requestObject.serialize());
+        setRequestParameters(List.of(new Pair<>("client_id", "mockClientId"),
+                new Pair<>("response_type", "code"),
+                new Pair<>("scope", "openid profile"),
+                new Pair<>("redirect_uri", "https://invalid.org/cb"),
+                new Pair<>("request", requestObject.serialize())));
         storeMetadata(storageService, clientId, clientSecret, scope, redirectUri);
 
         initializeThreadLocals();
@@ -1036,8 +1245,11 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
                 .build();
         final SignedJWT requestObject = createSecretJWT(ro, clientSecret);
         request.setMethod("GET");
-        request.setQueryString("client_id=mockClientId&response_type=code&scope=openid%20profile&redirect_uri="
-                + "https://invalid.org/cb&request=" + requestObject.serialize());
+        setRequestParameters(List.of(new Pair<>("client_id", "mockClientId"),
+                new Pair<>("response_type", "code"),
+                new Pair<>("scope", "openid profile"),
+                new Pair<>("redirect_uri", "https://invalid.org/cb"),
+                new Pair<>("request", requestObject.serialize())));
         storeMetadata(storageService, clientId, clientSecret, scope, redirectUri);
 
         initializeThreadLocals();
@@ -1084,8 +1296,11 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
         final JWTClaimsSet ro = JWTClaimsSet.parse(payload);
         final SignedJWT requestObject = createSecretJWT(ro, clientSecret);
         request.setMethod("GET");
-        request.setQueryString("client_id=mockClientId&response_type=code&scope=openid%20profile&redirect_uri="
-                + redirectUri + "&request=" + requestObject.serialize());
+        setRequestParameters(List.of(new Pair<>("client_id", "mockClientId"),
+                new Pair<>("response_type", "code"),
+                new Pair<>("scope", "openid profile"),
+                new Pair<>("redirect_uri", redirectUri),
+                new Pair<>("request", requestObject.serialize())));
         storeMetadata(storageService, clientId, clientSecret, scope, redirectUri);
 
         initializeThreadLocals();
@@ -1122,8 +1337,12 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
 
     protected void assertRequestObjectError(final JWT requestObject) throws IOException {
         request.setMethod("GET");
-        request.setQueryString("client_id=mockClientId&response_type=code&scope=openid%20profile&redirect_uri="
-                + redirectUri + "&request=" + requestObject.serialize());
+        setRequestParameters(List.of(new Pair<>("client_id", "mockClientId"),
+                new Pair<>("response_type", "code"),
+                new Pair<>("scope", "openid profile"),
+                new Pair<>("redirect_uri", redirectUri),
+                new Pair<>("request", requestObject.serialize())));
+
         storeMetadata(storageService, clientId, clientSecret, scope, redirectUri);
         initializeThreadLocals();
         
@@ -1194,6 +1413,19 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
             return null;
         }
     }
+    
+    protected void setRequestParameters(final List<Pair<String, String>> pairs) {
+        final StringBuffer query = new StringBuffer();
+        for (final Pair<String, String> pair : pairs) {
+            request.addParameter(pair.getFirst(), pair.getSecond());
+            try {
+                query.append(pair.getFirst() + "=" + URLEncoder.encode(pair.getSecond(), "UTF-8") + "&");
+            } catch (UnsupportedEncodingException e) {
+                Assert.fail(e.getMessage());
+            }
+        }
+        request.setQueryString(query.toString());
+    }
 
     @AfterMethod
     public void removeMetadata() throws IOException {
diff --git a/idp-oidc-extension-impl/src/test/resources/conf/attribute-filter.xml b/idp-oidc-extension-impl/src/test/resources/conf/attribute-filter.xml
index 840261ae..6ad3c07e 100644
--- a/idp-oidc-extension-impl/src/test/resources/conf/attribute-filter.xml
+++ b/idp-oidc-extension-impl/src/test/resources/conf/attribute-filter.xml
@@ -109,6 +109,18 @@
         <AttributeRule attributeID="eduPersonScopedAffiliation" permitAny="true" />
     </AttributeFilterPolicy>
 
+    <AttributeFilterPolicy id="OAUTH_SUBJECT">
+        <PolicyRequirementRule xsi:type="AND">
+            <Rule xsi:type="NOT">
+                <Rule xsi:type="oidc:OIDCScope" value="openid" />
+            </Rule>
+            <Rule xsi:type="Profile" value="http://shibboleth.net/ns/profiles/oidc/sso/browser" />
+        </PolicyRequirementRule>
+        <AttributeRule attributeID="subject">
+            <PermitValueRule xsi:type="ANY" />
+        </AttributeRule>
+    </AttributeFilterPolicy>
+
     <AttributeFilterPolicy id="OPENID_SCOPE">
         <PolicyRequirementRule xsi:type="oidc:OIDCScope" value="openid" />
         <AttributeRule attributeID="subject">
@@ -125,6 +137,9 @@
 
     <AttributeFilterPolicy id="OPENID_SCOPE_PROFILE">
         <PolicyRequirementRule xsi:type="oidc:OIDCScope" value="profile" />
+        <AttributeRule attributeID="subject">
+            <PermitValueRule xsi:type="ANY" />
+        </AttributeRule>
         <AttributeRule attributeID="name">
             <PermitValueRule xsi:type="ANY" />
         </AttributeRule>

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


More information about the commits mailing list