[java-idp-oidc] branch main updated: Add more unit tests related to scope validation.

Scott Cantor cantor.2 at osu.edu
Wed Jan 5 17:02:37 UTC 2022


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

scantor 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=70084dd92c873ff65cae6f3e76ef95215033031a

The following commit(s) were added to refs/heads/main by this push:
     new 70084dd9 Add more unit tests related to scope validation.
70084dd9 is described below

commit 70084dd92c873ff65cae6f3e76ef95215033031a
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Wed Jan 5 12:02:34 2022 -0500

    Add more unit tests related to scope validation.
---
 .../profile/impl/SetSubjectToResponseContext.java  |  3 +-
 .../plugin/oidc/op/profile/impl/ValidateScope.java | 13 ++-
 .../oidc/op/profile/flow/AbstractOidcFlowTest.java | 41 ++++-----
 .../oidc/op/profile/flow/AuthorizeFlowTest.java    | 96 +++++++++++++---------
 .../op/profile/flow/IntrospectionFlowTest.java     | 53 ++++++------
 .../oidc/op/profile/flow/RevocationFlowTest.java   | 21 +++--
 .../plugin/oidc/op/profile/flow/TokenFlowTest.java | 53 ++++++++++--
 .../plugin/oidc/op/profile/flow/UserInfoTest.java  | 20 +++--
 .../profile/impl/BaseOIDCResponseActionTest.java   |  2 +-
 .../impl/SetSubjectToResponseContextTest.java      |  4 +-
 .../oidc/op/profile/impl/ValidateScopeTest.java    | 16 +++-
 11 files changed, 206 insertions(+), 116 deletions(-)

diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetSubjectToResponseContext.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetSubjectToResponseContext.java
index f3ab901f..a0b728a4 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetSubjectToResponseContext.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetSubjectToResponseContext.java
@@ -22,6 +22,7 @@ import java.util.function.Function;
 import javax.annotation.Nonnull;
 
 import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCAuthenticationResponseContext;
+import net.shibboleth.idp.plugin.oidc.op.profile.OidcEventIds;
 import net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.TokenRequestSubjectLookupFunction;
 import net.shibboleth.utilities.java.support.component.ComponentSupport;
 import net.shibboleth.utilities.java.support.logic.Constraint;
@@ -84,7 +85,7 @@ public class SetSubjectToResponseContext extends AbstractOIDCResponseAction {
         final String subject = subjectLookupStrategy.apply(profileRequestContext);
         if (subject == null) {
             log.error("{} Subject may not be null", getLogPrefix());
-            ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
+            ActionSupport.buildEvent(profileRequestContext, OidcEventIds.INVALID_SUBJECT);
             return;
         }
         getOidcResponseContext().setSubject(subject);
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/profile/impl/ValidateScope.java
index 95830de9..931efe44 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/profile/impl/ValidateScope.java
@@ -40,14 +40,21 @@ import net.shibboleth.utilities.java.support.component.ComponentSupport;
 import net.shibboleth.utilities.java.support.logic.Constraint;
 
 /**
- * Action that validates requested scopes are registered ones and stores the resulting set in the
- * response context.
+ * Action that validates requested and previously granted scopes are also registered in client metadata
+ * 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>
+ * 
+ * <p>Previously granted scopes are stored in the response context's slow for previous authorization
+ * grant claims. In the case where no scopes are explicitly requested, we still filter the previous
+ * grants against the metadata.</p>
  * 
  * <p>Explicitly requested scopes are also filtered against, and override, any scopes previously
  * validated as part of an authorization grant claim set. If this occurs, any grant-borne claims
  * are removed because the association to specific scopes is gone by this point.</p>
  * 
- * <p>The "offline_access" scope is ignored in authentication endpoint validation unless the
+ * <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 {
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/AbstractOidcFlowTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/AbstractOidcFlowTest.java
index 7feb922d..c159f32d 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/AbstractOidcFlowTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/AbstractOidcFlowTest.java
@@ -76,8 +76,8 @@ public abstract class AbstractOidcFlowTest extends AbstractFlowTest {
     
     private String flowId;
     
-    protected AbstractOidcFlowTest(final String flowId) {
-        this.flowId = flowId;
+    protected AbstractOidcFlowTest(final String id) {
+        flowId = id;
     }
     
     /**
@@ -160,29 +160,31 @@ public abstract class AbstractOidcFlowTest extends AbstractFlowTest {
     }
     
     protected void storeMetadata(final StorageService storageService, final String clientId, final String secret,
-            final String... redirectUri) throws IOException {
-        storeMetadata(storageService, clientId, secret, null, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, null,
-                null, redirectUri);
+            final Scope scope, final String... redirectUri) throws IOException {
+        storeMetadata(storageService, clientId, secret, scope, null, ClientAuthenticationMethod.CLIENT_SECRET_BASIC,
+                null, null, redirectUri);
     }
 
+// Checkstyle: ParameterNumber OFF    
     protected void storeMetadata(final StorageService storageService, final String clientId, final String secret,
-            final JWSAlgorithm tokenEndpointSigAlg, final ClientAuthenticationMethod tokenEndpointMethod,
-            final String... redirectUri)
-            throws IOException {
-        storeMetadata(storageService, clientId, secret, tokenEndpointSigAlg, tokenEndpointMethod, null, null, redirectUri);
+            final Scope scope, final JWSAlgorithm tokenEndpointSigAlg,
+            final ClientAuthenticationMethod tokenEndpointMethod, final String... redirectUri) throws IOException {
+        storeMetadata(storageService, clientId, secret, scope, tokenEndpointSigAlg, tokenEndpointMethod, null, null,
+                redirectUri);
     }
 
     protected void storeMetadata(final StorageService storageService, final String clientId, final String secret,
-            final JWSAlgorithm tokenEndpointSigAlg, final ClientAuthenticationMethod tokenEndpointMethod,
-            final JWSAlgorithm userInfoSigAlg, final String... redirectUri)
-            throws IOException {
-        storeMetadata(storageService, clientId, secret, tokenEndpointSigAlg, tokenEndpointMethod, userInfoSigAlg, null, redirectUri);
+            final Scope scope, final JWSAlgorithm tokenEndpointSigAlg,
+            final ClientAuthenticationMethod tokenEndpointMethod, final JWSAlgorithm userInfoSigAlg,
+            final String... redirectUri) throws IOException {
+        storeMetadata(storageService, clientId, secret, scope, tokenEndpointSigAlg, tokenEndpointMethod, userInfoSigAlg,
+                null, redirectUri);
     }
     
     protected void storeMetadata(final StorageService storageService, final String clientId, final String secret,
-            final JWSAlgorithm tokenEndpointSigAlg, final ClientAuthenticationMethod tokenEndpointMethod,
-            final JWSAlgorithm userInfoSigAlg, final RSAPublicKey publicKey, final String... redirectUri)
-            throws IOException {
+            final Scope scope, final JWSAlgorithm tokenEndpointSigAlg,
+            final ClientAuthenticationMethod tokenEndpointMethod, final JWSAlgorithm userInfoSigAlg,
+            final RSAPublicKey publicKey, final String... redirectUri) throws IOException {
         final OIDCClientMetadata metadata = new OIDCClientMetadata();
         metadata.setGrantTypes(new HashSet<GrantType>(Arrays.asList(GrantType.AUTHORIZATION_CODE,
                 GrantType.REFRESH_TOKEN)));
@@ -198,7 +200,7 @@ public abstract class AbstractOidcFlowTest extends AbstractFlowTest {
         responseTypes.add(new ResponseType("code"));
         metadata.setResponseTypes(responseTypes);
         metadata.setRedirectionURIs(uris);
-        metadata.setScope(Scope.parse("openid profile email"));
+        metadata.setScope(scope);
         metadata.setTokenEndpointAuthJWSAlg(tokenEndpointSigAlg);
         metadata.setTokenEndpointAuthMethod(tokenEndpointMethod);
         metadata.setUserInfoJWSAlg(userInfoSigAlg);
@@ -207,8 +209,8 @@ public abstract class AbstractOidcFlowTest extends AbstractFlowTest {
             information = new OIDCClientInformation(new ClientID(clientId), new Date(),
                 metadata, new Secret(secret));
         } else {
-            RSAKey rsaKey = new RSAKey.Builder(publicKey).build();
-            JWKSet jwkSet = new JWKSet(rsaKey);
+            final RSAKey rsaKey = new RSAKey.Builder(publicKey).build();
+            final JWKSet jwkSet = new JWKSet(rsaKey);
             metadata.setJWKSet(jwkSet);
             information = new OIDCClientInformation(new ClientID(clientId), metadata);
         }
@@ -216,6 +218,7 @@ public abstract class AbstractOidcFlowTest extends AbstractFlowTest {
                 information.toJSONObject().toJSONString(), System.currentTimeMillis() + (60 * 60 * 1000));
         
     }
+// Checkstyle: ParameterNumber ON
     
     protected void removeMetadata(final StorageService storageService, final String clientId) throws IOException {
         storageService.delete(BaseStorageServiceClientInformationComponent.CONTEXT_NAME, clientId);
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 441c60dc..01bf40b5 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
@@ -36,7 +36,9 @@ 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.ErrorResponse;
 import com.nimbusds.oauth2.sdk.ParseException;
+import com.nimbusds.oauth2.sdk.Scope;
 import com.nimbusds.openid.connect.sdk.AuthenticationResponse;
 import com.nimbusds.openid.connect.sdk.AuthenticationSuccessResponse;
 
@@ -52,6 +54,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     String redirectUri = "https://example.org/cb";
     String clientId = "mockClientId";
     String clientSecret = "mockClientSecretmockClientSecretmockClientSecretmockClientSecretmockClientSecret";
+    Scope scope = Scope.parse("openid profile email");
     
     @Autowired
     @Qualifier("shibboleth.StorageService")
@@ -71,32 +74,48 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
         request.setMethod("GET");
         request.setQueryString("client_id=mockClientId&response_type=code&scope=openid%20profile&redirect_uri="
                 + redirectUri);
-        storeMetadata(storageService, clientId, clientSecret, redirectUri);
+        storeMetadata(storageService, clientId, clientSecret, scope, redirectUri);
 
         initializeThreadLocals();
         
-        FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
-        AuthenticationResponse responseMessage = parseSuccessResponse(result, AuthenticationResponse.class);
-        AuthenticationSuccessResponse successResponse = responseMessage.toSuccessResponse();
+        final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+        final AuthenticationResponse responseMessage = parseSuccessResponse(result, AuthenticationResponse.class);
+        final AuthenticationSuccessResponse successResponse = responseMessage.toSuccessResponse();
         Assert.assertEquals(successResponse.getRedirectionURI().toString(), redirectUri);
         Assert.assertNull(successResponse.getIDToken());
         Assert.assertNull(successResponse.getAccessToken());
         Assert.assertNotNull(successResponse.getAuthorizationCode());
     }
 
+    @Test
+    public void testWithAuthorizationCodeFlowNoScopes() throws IOException, ParseException, SessionException {
+        request.setMethod("GET");
+        request.setQueryString("client_id=mockClientId&response_type=code&scope=openid%20profile&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");
+    }
+    
     @Test
     public void testWithAuthorizationCodeFlowWithIDTokenClaims() throws IOException, ParseException, SessionException {
         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);
-        storeMetadata(storageService, clientId, clientSecret, redirectUri);
+        storeMetadata(storageService, clientId, clientSecret, scope, redirectUri);
 
         initializeThreadLocals();
         
-        FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
-        AuthenticationResponse responseMessage = parseSuccessResponse(result, AuthenticationResponse.class);
-        AuthenticationSuccessResponse successResponse = responseMessage.toSuccessResponse();
+        final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+        final AuthenticationResponse responseMessage = parseSuccessResponse(result, AuthenticationResponse.class);
+        final AuthenticationSuccessResponse successResponse = responseMessage.toSuccessResponse();
         Assert.assertEquals(successResponse.getRedirectionURI().toString(), redirectUri);
         Assert.assertNull(successResponse.getIDToken());
         Assert.assertNull(successResponse.getAccessToken());
@@ -109,13 +128,13 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
         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);
-        storeMetadata(storageService, clientId, clientSecret, redirectUri);
+        storeMetadata(storageService, clientId, clientSecret, scope, redirectUri);
 
         initializeThreadLocals();
 
-        FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
-        AuthenticationResponse responseMessage = parseSuccessResponse(result, AuthenticationResponse.class);
-        AuthenticationSuccessResponse successResponse = responseMessage.toSuccessResponse();
+        final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+        final AuthenticationResponse responseMessage = parseSuccessResponse(result, AuthenticationResponse.class);
+        final AuthenticationSuccessResponse successResponse = responseMessage.toSuccessResponse();
         Assert.assertEquals(successResponse.getRedirectionURI().toString(), redirectUri);
         Assert.assertNull(successResponse.getIDToken());
         Assert.assertNull(successResponse.getAccessToken());
@@ -127,13 +146,13 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
         request.setMethod("GET");
         request.setQueryString("client_id=mockSamlClientId&response_type=code&scope=openid%20profile&redirect_uri="
                 + redirectUri);
-        storeMetadata(storageService, clientId, clientSecret, redirectUri);
+        storeMetadata(storageService, clientId, clientSecret, scope, redirectUri);
 
         initializeThreadLocals();
 
-        FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
-        AuthenticationResponse responseMessage = parseSuccessResponse(result, AuthenticationResponse.class);
-        AuthenticationSuccessResponse successResponse = responseMessage.toSuccessResponse();
+        final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+        final AuthenticationResponse responseMessage = parseSuccessResponse(result, AuthenticationResponse.class);
+        final AuthenticationSuccessResponse successResponse = responseMessage.toSuccessResponse();
         Assert.assertEquals(successResponse.getRedirectionURI().toString(), redirectUri);
         Assert.assertNull(successResponse.getIDToken());
         Assert.assertNull(successResponse.getAccessToken());
@@ -147,14 +166,14 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
                 + redirectUri);
         initializeThreadLocals();
 
-        FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+        final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
         assertFlowExecutionResult(result, FLOW_ID);
         Assert.assertEquals(result.getOutcome().getId(), "ErrorView");
     }
 
     @Test
     public void testWithPlainReqObjectExpired() throws IOException, ParseException, SessionException {
-        JWTClaimsSet ro = new JWTClaimsSet.Builder()
+        final JWTClaimsSet ro = new JWTClaimsSet.Builder()
                 .expirationTime(Date.from(Instant.now().minus(Duration.ofMinutes(5))))
                 .build();
         assertRequestObjectError(new PlainJWT(ro));
@@ -162,29 +181,28 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
 
     @Test
     public void testWithPlainReqObjectNbfInFuture() throws IOException, ParseException, SessionException {
-        JWTClaimsSet ro = new JWTClaimsSet.Builder()
+        final JWTClaimsSet ro = new JWTClaimsSet.Builder()
                 .notBeforeTime(Date.from(Instant.now().plus(Duration.ofMinutes(5))))
                 .build();
         assertRequestObjectError(new PlainJWT(ro));
     }
 
     @Test
-    public void testWithPlainReqObjectOverwriteRedirectUri() throws IOException, ParseException,
-            SessionException {
-        JWTClaimsSet ro = new JWTClaimsSet.Builder()
+    public void testWithPlainReqObjectOverwriteRedirectUri() throws IOException, ParseException, SessionException {
+        final JWTClaimsSet ro = new JWTClaimsSet.Builder()
                 .claim("redirect_uri", redirectUri)
                 .build();
-        PlainJWT requestObject = new PlainJWT(ro);
+        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());
-        storeMetadata(storageService, clientId, clientSecret, redirectUri);
+        storeMetadata(storageService, clientId, clientSecret, scope, redirectUri);
 
         initializeThreadLocals();
 
-        FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
-        AuthenticationResponse responseMessage = parseSuccessResponse(result, AuthenticationResponse.class);
-        AuthenticationSuccessResponse successResponse = responseMessage.toSuccessResponse();
+        final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+        final AuthenticationResponse responseMessage = parseSuccessResponse(result, AuthenticationResponse.class);
+        final AuthenticationSuccessResponse successResponse = responseMessage.toSuccessResponse();
         Assert.assertEquals(successResponse.getRedirectionURI().toString(), redirectUri);
         Assert.assertNull(successResponse.getIDToken());
         Assert.assertNull(successResponse.getAccessToken());
@@ -194,7 +212,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     @Test
     public void testWithSignedReqObjectNoIssuer() throws IOException, ParseException, SessionException,
             JOSEException {
-        JWTClaimsSet ro = new JWTClaimsSet.Builder()
+        final JWTClaimsSet ro = new JWTClaimsSet.Builder()
                 .audience("https://op.example.org")
                 .build();
         assertRequestObjectError(createSecretJWT(ro, clientSecret));
@@ -203,7 +221,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     @Test
     public void testWithSignedReqObjectNoAudience() throws IOException, ParseException, SessionException,
             JOSEException {
-        JWTClaimsSet ro = new JWTClaimsSet.Builder()
+        final JWTClaimsSet ro = new JWTClaimsSet.Builder()
                 .issuer(clientId)
                 .build();
         assertRequestObjectError(createSecretJWT(ro, clientSecret));
@@ -212,7 +230,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     @Test
     public void testWithSignedReqObjectWrongIssuer() throws IOException, ParseException, SessionException,
             JOSEException {
-        JWTClaimsSet ro = new JWTClaimsSet.Builder()
+        final JWTClaimsSet ro = new JWTClaimsSet.Builder()
                 .audience("https://op.example.org")
                 .issuer("invalid")
                 .build();
@@ -222,7 +240,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     @Test
     public void testWithSignedReqObjectWrongAudience() throws IOException, ParseException, SessionException,
             JOSEException {
-        JWTClaimsSet ro = new JWTClaimsSet.Builder()
+        final JWTClaimsSet ro = new JWTClaimsSet.Builder()
                 .audience("https://invalid.org")
                 .issuer(clientId)
                 .build();
@@ -232,22 +250,22 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     @Test
     public void testWithSignedReqObjectOverwriteRedirectUri() throws IOException, ParseException,
             SessionException, JOSEException {
-        JWTClaimsSet ro = new JWTClaimsSet.Builder()
+        final JWTClaimsSet ro = new JWTClaimsSet.Builder()
                 .audience("https://op.example.org")
                 .issuer(clientId)
                 .claim("redirect_uri", redirectUri)
                 .build();
-        SignedJWT requestObject = createSecretJWT(ro, clientSecret);
+        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());
-        storeMetadata(storageService, clientId, clientSecret, redirectUri);
+        storeMetadata(storageService, clientId, clientSecret, scope, redirectUri);
 
         initializeThreadLocals();
 
-        FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
-        AuthenticationResponse responseMessage = parseSuccessResponse(result, AuthenticationResponse.class);
-        AuthenticationSuccessResponse successResponse = responseMessage.toSuccessResponse();
+        final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+        final AuthenticationResponse responseMessage = parseSuccessResponse(result, AuthenticationResponse.class);
+        final AuthenticationSuccessResponse successResponse = responseMessage.toSuccessResponse();
         Assert.assertEquals(successResponse.getRedirectionURI().toString(), redirectUri);
         Assert.assertNull(successResponse.getIDToken());
         Assert.assertNull(successResponse.getAccessToken());
@@ -258,10 +276,10 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
         request.setMethod("GET");
         request.setQueryString("client_id=mockClientId&response_type=code&scope=openid%20profile&redirect_uri="
                 + redirectUri + "&request=" + requestObject.serialize());
-        storeMetadata(storageService, clientId, clientSecret, redirectUri);
+        storeMetadata(storageService, clientId, clientSecret, scope, redirectUri);
         initializeThreadLocals();
         
-        FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+        final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
         assertFlowExecutionResult(result, FLOW_ID);
         Assert.assertEquals(result.getOutcome().getId(), "ErrorView");        
     }
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/IntrospectionFlowTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/IntrospectionFlowTest.java
index b897750c..7156f0ad 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/IntrospectionFlowTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/IntrospectionFlowTest.java
@@ -60,6 +60,8 @@ public class IntrospectionFlowTest extends AbstractOidcClientAuthenticationFlowT
     String clientIdSaml = "mockSamlClientId";
     String clientSecretSaml = "mockClientSecretmockClientSecretmockClientSecret";
 
+    Scope scope = Scope.parse("openid profile email");
+    
     @Autowired
     @Qualifier("shibboleth.StorageService")
     StorageService storageService;
@@ -69,14 +71,13 @@ public class IntrospectionFlowTest extends AbstractOidcClientAuthenticationFlowT
     }
 
     @BeforeMethod
-    public void setup() throws IOException, NoSuchAlgorithmException, URISyntaxException, DataSealerException,
-            ComponentInitializationException {
+    public void setup() throws IOException {
         removeMetadata(storageService, clientId);
     }
 
     @Test
-    public void testUntrustedClient() throws IOException, NoSuchAlgorithmException, URISyntaxException,
-            DataSealerException, ComponentInitializationException {
+    public void testUntrustedClient() throws NoSuchAlgorithmException, URISyntaxException, DataSealerException,
+            ComponentInitializationException {
         setBasicAuth(clientId, clientSecret);
         setHttpFormRequest("POST", Collections.singletonMap("token",
                 super.buildToken(clientId, "sub", Scope.parse("openid")).toJSONObject().getAsString("access_token")));
@@ -87,7 +88,7 @@ public class IntrospectionFlowTest extends AbstractOidcClientAuthenticationFlowT
     @Test
     public void testInvalidMessage() throws IOException, NoSuchAlgorithmException, URISyntaxException,
             DataSealerException, ComponentInitializationException {
-        storeMetadata(storageService, clientId, clientSecret);
+        storeMetadata(storageService, clientId, clientSecret, scope);
         setBasicAuth(clientId, clientSecret);
         setHttpFormRequest("POST", Collections.singletonMap("token_not",
                 super.buildToken(clientId, "sub", Scope.parse("openid")).toJSONObject().getAsString("access_token")));
@@ -98,12 +99,13 @@ public class IntrospectionFlowTest extends AbstractOidcClientAuthenticationFlowT
     @Test
     public void testSuccess() throws IOException, NoSuchAlgorithmException, URISyntaxException, DataSealerException,
             ComponentInitializationException {
-        storeMetadata(storageService, clientId, clientSecret);
+        storeMetadata(storageService, clientId, clientSecret, scope);
         setBasicAuth(clientId, clientSecret);
         setHttpFormRequest("POST", Collections.singletonMap("token",
                 super.buildToken(clientId, "sub", Scope.parse("openid")).toJSONObject().getAsString("access_token")));
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
-        TokenIntrospectionSuccessResponse resp = parseSuccessResponse(result, TokenIntrospectionSuccessResponse.class);
+        final TokenIntrospectionSuccessResponse resp =
+                parseSuccessResponse(result, TokenIntrospectionSuccessResponse.class);
         Assert.assertEquals(resp.getClientID().getValue(), clientId);
         Assert.assertTrue(resp.isActive());
     }
@@ -115,7 +117,8 @@ public class IntrospectionFlowTest extends AbstractOidcClientAuthenticationFlowT
         setHttpFormRequest("POST", Collections.singletonMap("token",
                 super.buildToken(clientIdSaml, "sub", Scope.parse("openid")).toJSONObject().getAsString("access_token")));
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
-        TokenIntrospectionSuccessResponse resp = parseSuccessResponse(result, TokenIntrospectionSuccessResponse.class);
+        TokenIntrospectionSuccessResponse resp =
+                parseSuccessResponse(result, TokenIntrospectionSuccessResponse.class);
         Assert.assertEquals(resp.getClientID().getValue(), clientIdSaml);
         Assert.assertTrue(resp.isActive());
     }
@@ -123,13 +126,14 @@ public class IntrospectionFlowTest extends AbstractOidcClientAuthenticationFlowT
     @Test
     public void testSuccessWithLegacyToken() throws IOException, NoSuchAlgorithmException, URISyntaxException,
             DataSealerException, ComponentInitializationException {
-        storeMetadata(storageService, clientId, clientSecret);
+        storeMetadata(storageService, clientId, clientSecret, scope);
         setBasicAuth(clientId, clientSecret);
         setHttpFormRequest("POST", Collections.singletonMap("token",
                 super.buildLegacyToken(clientId, "sub",
                         Scope.parse("openid")).toJSONObject().getAsString("access_token")));
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
-        TokenIntrospectionSuccessResponse resp = parseSuccessResponse(result, TokenIntrospectionSuccessResponse.class);
+        final TokenIntrospectionSuccessResponse resp =
+                parseSuccessResponse(result, TokenIntrospectionSuccessResponse.class);
         Assert.assertEquals(resp.getClientID().getValue(), clientId);
         Assert.assertTrue(resp.isActive());
     }
@@ -137,13 +141,14 @@ public class IntrospectionFlowTest extends AbstractOidcClientAuthenticationFlowT
     @Test
     public void testSuccessWithLegacyConsentToken() throws IOException, NoSuchAlgorithmException, URISyntaxException,
             DataSealerException, ComponentInitializationException {
-        storeMetadata(storageService, clientId, clientSecret);
+        storeMetadata(storageService, clientId, clientSecret, scope);
         setBasicAuth(clientId, clientSecret);
         setHttpFormRequest("POST", Collections.singletonMap("token",
                 super.buildLegacyToken(clientId, "sub", Scope.parse("openid"),
                         "mail").toJSONObject().getAsString("access_token")));
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
-        TokenIntrospectionSuccessResponse resp = parseSuccessResponse(result, TokenIntrospectionSuccessResponse.class);
+        final TokenIntrospectionSuccessResponse resp =
+                parseSuccessResponse(result, TokenIntrospectionSuccessResponse.class);
         Assert.assertEquals(resp.getClientID().getValue(), clientId);
         Assert.assertTrue(resp.isActive());
     }
@@ -151,11 +156,12 @@ public class IntrospectionFlowTest extends AbstractOidcClientAuthenticationFlowT
     @Test
     public void testUnidentifiedToken() throws IOException, NoSuchAlgorithmException, URISyntaxException,
             DataSealerException, ComponentInitializationException {
-        storeMetadata(storageService, clientId, clientSecret);
+        storeMetadata(storageService, clientId, clientSecret, scope);
         setBasicAuth(clientId, clientSecret);
         setHttpFormRequest("POST", Collections.singletonMap("token", "unknowntoken"));
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
-        TokenIntrospectionSuccessResponse resp = parseSuccessResponse(result, TokenIntrospectionSuccessResponse.class);
+        final TokenIntrospectionSuccessResponse resp =
+                parseSuccessResponse(result, TokenIntrospectionSuccessResponse.class);
         Assert.assertNull(resp.getClientID());
         Assert.assertFalse(resp.isActive());
     }
@@ -163,37 +169,38 @@ public class IntrospectionFlowTest extends AbstractOidcClientAuthenticationFlowT
     @Test
     public void testFailedAuthentication() throws IOException, NoSuchAlgorithmException, URISyntaxException,
             DataSealerException, ComponentInitializationException {
-        storeMetadata(storageService, clientId, clientSecret);
+        storeMetadata(storageService, clientId, clientSecret, scope);
         setBasicAuth(clientId, clientSecret + "X");
         setHttpFormRequest("POST", Collections.singletonMap("token",
                 super.buildToken(clientId, "sub", Scope.parse("openid")).toJSONObject().getAsString("access_token")));
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
-        TokenIntrospectionErrorResponse resp = (TokenIntrospectionErrorResponse) parseErrorResponse(result);
+        final TokenIntrospectionErrorResponse resp = (TokenIntrospectionErrorResponse) parseErrorResponse(result);
         Assert.assertEquals(resp.getErrorObject().getCode(), OAuth2Error.INVALID_CLIENT_CODE);
     }
     
     protected FlowExecutionResult launchWithJwtAuthentication(final SignedJWT jwt, final JWSAlgorithm algorithm,
             final ClientAuthenticationMethod method) throws Exception {
         if (ClientAuthenticationMethod.CLIENT_SECRET_JWT.equals(method)) {
-            storeMetadata(storageService, clientId, clientSecret, algorithm, method);
+            storeMetadata(storageService, clientId, clientSecret, scope, algorithm, method);
         } else {
-            storeMetadata(storageService, clientId, null, algorithm, method, null, rsaPublicKey);
+            storeMetadata(storageService, clientId, null, scope, algorithm, method, null, rsaPublicKey);
         }
-        final String accessToken =  super.buildToken(clientId, "sub", Scope.parse("openid")).toJSONObject().getAsString("access_token");
-        Map<String, String> requestParameters = createRequestParameters(accessToken, clientId);
+        final String accessToken = 
+                super.buildToken(clientId, "sub", Scope.parse("openid")).toJSONObject().getAsString("access_token");
+        final Map<String, String> requestParameters = createRequestParameters(accessToken, clientId);
         populateClientAssertionParams(requestParameters, jwt);
         setHttpFormRequest("POST", requestParameters);
         return flowExecutor.launchExecution(FLOW_ID, null, externalContext);
     }
     
-    protected Map<String, String> createRequestParameters(final String token, final String clientId) {
-        final Map<String, String> result = new HashMap<>();
+    protected Map<String,String> createRequestParameters(final String token, final String clientId) {
+        final Map<String,String> result = new HashMap<>();
         result.put("token", token);
         result.put("client_id", clientId);
         return result;
     }
 
-    protected Pair<String, String> getErrorDetaisForJWTValidation() {
+    protected Pair<String,String> getErrorDetaisForJWTValidation() {
         return new Pair<>("invalid_client", "Client authentication failed");
     }
 
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/RevocationFlowTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/RevocationFlowTest.java
index f458edb7..f2658e05 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/RevocationFlowTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/RevocationFlowTest.java
@@ -57,6 +57,8 @@ public class RevocationFlowTest extends AbstractOidcClientAuthenticationFlowTest
 
     String clientIdSaml = "mockSamlClientId";
     String clientSecretSaml = "mockClientSecretmockClientSecretmockClientSecret";
+
+    Scope scope = Scope.parse("openid profile email");
     
     @Autowired
     @Qualifier("shibboleth.StorageService")
@@ -87,13 +89,13 @@ public class RevocationFlowTest extends AbstractOidcClientAuthenticationFlowTest
         setBasicAuth(clientId, clientSecret);
         setHttpFormRequest("POST", Collections.singletonMap("token", super.buildToken(clientId, "sub", 
                 Scope.parse("openid")).toJSONObject().getAsString("access_token")));
-        storeMetadata(storageService, clientId, clientSecret);
+        storeMetadata(storageService, clientId, clientSecret, scope);
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
         parseSuccessResponse(result, OAuth2RevocationSuccessResponse.class);
     }
 
     @Test
-    public void testSuccessWithSamlMetadata() throws IOException, NoSuchAlgorithmException, URISyntaxException,
+    public void testSuccessWithSamlMetadata() throws NoSuchAlgorithmException, URISyntaxException,
         DataSealerException, ComponentInitializationException {
         setBasicAuth(clientIdSaml, clientSecretSaml);
         setHttpFormRequest("POST", Collections.singletonMap("token", super.buildToken(clientIdSaml, "sub", 
@@ -108,7 +110,7 @@ public class RevocationFlowTest extends AbstractOidcClientAuthenticationFlowTest
         setBasicAuth(clientId, clientSecret);
         setHttpFormRequest("POST", Collections.singletonMap("token", super.buildLegacyToken(clientId, "sub",
                 Scope.parse("openid")).toJSONObject().getAsString("access_token")));
-        storeMetadata(storageService, clientId, clientSecret);
+        storeMetadata(storageService, clientId, clientSecret, scope);
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
         parseSuccessResponse(result, OAuth2RevocationSuccessResponse.class);
     }
@@ -119,7 +121,7 @@ public class RevocationFlowTest extends AbstractOidcClientAuthenticationFlowTest
         setBasicAuth(clientId, clientSecret);
         setHttpFormRequest("POST", Collections.singletonMap("token", super.buildLegacyToken(clientId, "sub",
                 Scope.parse("openid"), "mail").toJSONObject().getAsString("access_token")));
-        storeMetadata(storageService, clientId, clientSecret);
+        storeMetadata(storageService, clientId, clientSecret, scope);
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
         parseSuccessResponse(result, OAuth2RevocationSuccessResponse.class);
     }
@@ -127,12 +129,13 @@ public class RevocationFlowTest extends AbstractOidcClientAuthenticationFlowTest
     protected FlowExecutionResult launchWithJwtAuthentication(final SignedJWT jwt, final JWSAlgorithm algorithm,
             final ClientAuthenticationMethod method) throws Exception {
         if (ClientAuthenticationMethod.CLIENT_SECRET_JWT.equals(method)) {
-            storeMetadata(storageService, clientId, clientSecret, algorithm, method);
+            storeMetadata(storageService, clientId, clientSecret, scope, algorithm, method);
         } else {
-            storeMetadata(storageService, clientId, null, algorithm, method, null, rsaPublicKey);
+            storeMetadata(storageService, clientId, null, scope, algorithm, method, null, rsaPublicKey);
         }
-        final String accessToken =  super.buildToken(clientId, "sub", Scope.parse("openid")).toJSONObject().getAsString("access_token");
-        Map<String, String> requestParameters = new HashMap<>();
+        final String accessToken =
+                super.buildToken(clientId, "sub", Scope.parse("openid")).toJSONObject().getAsString("access_token");
+        final Map<String,String> requestParameters = new HashMap<>();
         requestParameters.put("token", accessToken);
         populateClientAssertionParams(requestParameters, jwt);
         setHttpFormRequest("POST", requestParameters);
@@ -150,7 +153,7 @@ public class RevocationFlowTest extends AbstractOidcClientAuthenticationFlowTest
         setBasicAuth(clientId, clientSecret + "X");
         setHttpFormRequest("POST", Collections.singletonMap("token", super.buildLegacyToken(clientId, "sub",
                 Scope.parse("openid"), "mail").toJSONObject().getAsString("access_token")));
-        storeMetadata(storageService, clientId, clientSecret);
+        storeMetadata(storageService, clientId, clientSecret, scope);
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
         assertErrorCode(result, OAuth2Error.INVALID_CLIENT_CODE);
     }
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/TokenFlowTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/TokenFlowTest.java
index 677ff1dd..2e2127d9 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/TokenFlowTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/TokenFlowTest.java
@@ -70,6 +70,8 @@ public class TokenFlowTest extends AbstractOidcClientAuthenticationFlowTest {
     String clientIdPkcePlainUnforced = "mockClientIdPKCEPlainUnforced";
     String clientIdPkceS256 = "mockClientIdPKCES256";
     String codeVerifier = "9234567812345678123456781234567812345678123456781234567812345678";
+
+    Scope scope = Scope.parse("openid profile email");
     
     @Autowired
     @Qualifier("shibboleth.StorageService")
@@ -112,7 +114,7 @@ public class TokenFlowTest extends AbstractOidcClientAuthenticationFlowTest {
     @Test
     public void testUnauthorized() throws IOException, ParseException {
         setHttpFormRequest("POST", createRequestParameters(redirectUri, "authorization_code", "mockCode", clientId));
-        storeMetadata(storageService, clientId, clientSecret);
+        storeMetadata(storageService, clientId, clientSecret, scope);
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
         assertErrorCode(result, OAuth2Error.INVALID_CLIENT_CODE);
     }
@@ -120,19 +122,36 @@ public class TokenFlowTest extends AbstractOidcClientAuthenticationFlowTest {
     @Test
     public void testInvalidGrant() throws ParseException, IOException {
         setHttpFormRequest("POST", createRequestParameters(redirectUri, "authorization_code", "mockCode", clientId));
-        storeMetadata(storageService, clientId, clientSecret);
+        storeMetadata(storageService, clientId, clientSecret, scope);
         setBasicAuth(clientId, clientSecret);
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
         assertErrorCode(result, OAuth2Error.INVALID_GRANT_CODE);
     }
+
+    @Test
+    public void testNoScopes() throws Exception {
+        // TODO: This test should probably be failing. The absence of scopes should prevent an id_token from
+        // being issued, but isn't. The sub claim is there via the authorization code even if it isn't
+        // "released" by the filter policy.
+        setHttpFormRequest("POST",
+                createRequestParameters(redirectUri, "authorization_code",
+                        buildAuthorizationCode(clientId), clientId));
+        storeMetadata(storageService, clientId, clientSecret, null);
+        setBasicAuth(clientId, clientSecret);
+        storeConsent(storageService, "jdoe", clientId, "mail");
+        final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+        final OIDCTokenResponse response = parseSuccessResponse(result, OIDCTokenResponse.class);
+        Assert.assertNotNull(response.getTokens().getAccessToken());
+        Assert.assertNotNull(response.getOIDCTokens().getIDToken());
+    }
     
     protected void initializeGrantAndRequest(final String clientId, final Map<String, String> requestParameters)
             throws IOException {
         setHttpFormRequest("POST", requestParameters);
-        storeMetadata(storageService, clientId, clientSecret);
+        storeMetadata(storageService, clientId, clientSecret, scope);
         setBasicAuth(clientId, clientSecret);
     }
-
+    
     @Test
     public void testValidGrant() throws Exception {
         initializeGrantAndRequest(clientId, createRequestParameters(redirectUri, "authorization_code",
@@ -143,6 +162,26 @@ public class TokenFlowTest extends AbstractOidcClientAuthenticationFlowTest {
         Assert.assertNotNull(response.getTokens().getAccessToken());
         Assert.assertNotNull(response.getOIDCTokens().getIDToken());
     }
+
+    @Test
+    public void testValidGrantWithRequestedScope() throws Exception {
+        final Map<String,String> params = createRequestParameters(redirectUri, "authorization_code",
+                buildAuthorizationCode(clientId), clientId);
+        params.put("scope", "openid profile");
+        initializeGrantAndRequest(clientId, params);
+        storeConsent(storageService, "jdoe", clientId, "mail");
+        final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+        final OIDCTokenResponse response = parseSuccessResponse(result, OIDCTokenResponse.class);
+        Assert.assertNotNull(response.getTokens().getAccessToken());
+        Assert.assertNotNull(response.getOIDCTokens().getIDToken());
+        
+        final ValidateGrantTest test = new ValidateGrantTest();
+        final AccessTokenClaimsSet token =
+                AccessTokenClaimsSet.parse(response.getTokens().getAccessToken().getValue(), test.getDataSealer());
+        Assert.assertTrue(token.getScope().contains("openid"));
+        Assert.assertTrue(token.getScope().contains("profile"));
+        Assert.assertFalse(token.getScope().contains("email"));
+    }
     
     @Test
     public void testValidLegacyGrant() throws Exception {
@@ -431,7 +470,7 @@ public class TokenFlowTest extends AbstractOidcClientAuthenticationFlowTest {
             throws Exception {
         final String code = ValidateGrantTest.buildAuthorizationCode(clientId, "https://op.example.org", "jdoe", "mock",
                 redirectUri).toString();
-        storeMetadata(storageService, clientId, clientSecret, JWSAlgorithm.HS256,
+        storeMetadata(storageService, clientId, clientSecret, scope, JWSAlgorithm.HS256,
                 ClientAuthenticationMethod.CLIENT_SECRET_JWT);
         final Map<String, String> requestParameters =
                 createRequestParameters(redirectUri, "authorization_code", code, clientId);
@@ -445,9 +484,9 @@ public class TokenFlowTest extends AbstractOidcClientAuthenticationFlowTest {
         final String code = ValidateGrantTest.buildAuthorizationCode(clientId, "https://op.example.org", "jdoe", "mock",
                 redirectUri).toString();
         if (ClientAuthenticationMethod.CLIENT_SECRET_JWT.equals(method)) {
-            storeMetadata(storageService, clientId, clientSecret, algorithm, method);
+            storeMetadata(storageService, clientId, clientSecret, scope, algorithm, method);
         } else {
-            storeMetadata(storageService, clientId, null, algorithm, method, null, rsaPublicKey);
+            storeMetadata(storageService, clientId, null, scope, algorithm, method, null, rsaPublicKey);
         }
         final Map<String, String> requestParameters =
                 createRequestParameters(redirectUri, "authorization_code", code, clientId);
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/UserInfoTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/UserInfoTest.java
index 2e61fad6..41dc6206 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/UserInfoTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/UserInfoTest.java
@@ -54,6 +54,8 @@ public class UserInfoTest extends AbstractOidcApiFlowTest {
     String clientId = "mockClientId";
     String clientIdSaml = "mockSamlClientId";
     String subject = "mockSubject";
+
+    Scope scope = Scope.parse("openid profile email");
     
     @Autowired
     @Qualifier("shibboleth.StorageService")
@@ -96,7 +98,7 @@ public class UserInfoTest extends AbstractOidcApiFlowTest {
     public void testSuccessOnlySubject() throws URISyntaxException, NoSuchAlgorithmException, DataSealerException,
         ComponentInitializationException, IOException {
         final BearerAccessToken token = buildToken(clientId, subject, new Scope("openid"));
-        storeMetadata(storageService, clientId, "mockSecret");
+        storeMetadata(storageService, clientId, "mockSecret", scope);
         request.addHeader("Authorization", token.toAuthorizationHeader());
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
         final UserInfoSuccessResponse response = parseSuccessResponse(result, UserInfoSuccessResponse.class);
@@ -112,7 +114,7 @@ public class UserInfoTest extends AbstractOidcApiFlowTest {
     public void testSuccessOnlySubjectWithLegacyToken() throws URISyntaxException, NoSuchAlgorithmException, DataSealerException,
         ComponentInitializationException, IOException, com.nimbusds.oauth2.sdk.ParseException {
         final BearerAccessToken token = buildLegacyToken(clientId, subject, new Scope("openid"));
-        storeMetadata(storageService, clientId, "mockSecret");
+        storeMetadata(storageService, clientId, "mockSecret", scope);
         request.addHeader("Authorization", token.toAuthorizationHeader());
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
         final UserInfoSuccessResponse response = parseSuccessResponse(result, UserInfoSuccessResponse.class);
@@ -144,7 +146,7 @@ public class UserInfoTest extends AbstractOidcApiFlowTest {
     public void testSuccessEmailResolution() throws URISyntaxException, NoSuchAlgorithmException, DataSealerException,
         ComponentInitializationException, IOException {
         final BearerAccessToken token = buildToken(clientId, subject, new Scope("openid", "email", "profile"));
-        storeMetadata(storageService, clientId, "mockSecret");
+        storeMetadata(storageService, clientId, "mockSecret", scope);
         storeConsent(storageService, "jdoe", clientId, "mail");
         request.addHeader("Authorization", token.toAuthorizationHeader());
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
@@ -161,7 +163,7 @@ public class UserInfoTest extends AbstractOidcApiFlowTest {
     public void testSuccessEmailResolutionWithLegacyToken() throws URISyntaxException, NoSuchAlgorithmException,
         DataSealerException, ComponentInitializationException, IOException, com.nimbusds.oauth2.sdk.ParseException {
         final BearerAccessToken token = buildLegacyToken(clientId, subject, new Scope("openid", "email", "profile"));
-        storeMetadata(storageService, clientId, "mockSecret");
+        storeMetadata(storageService, clientId, "mockSecret", scope);
         storeConsent(storageService, "jdoe", clientId, "mail");
         request.addHeader("Authorization", token.toAuthorizationHeader());
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
@@ -179,7 +181,7 @@ public class UserInfoTest extends AbstractOidcApiFlowTest {
         DataSealerException, ComponentInitializationException, IOException, com.nimbusds.oauth2.sdk.ParseException {
         final BearerAccessToken token = buildLegacyToken(clientId, subject, new Scope("openid", "email", "profile"),
                 "mail");
-        storeMetadata(storageService, clientId, "mockSecret");
+        storeMetadata(storageService, clientId, "mockSecret", scope);
         storeConsent(storageService, "jdoe", clientId, "mail");
         request.addHeader("Authorization", token.toAuthorizationHeader());
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
@@ -198,7 +200,7 @@ public class UserInfoTest extends AbstractOidcApiFlowTest {
         com.nimbusds.oauth2.sdk.ParseException {
         final BearerAccessToken token = buildLegacyToken(clientId, subject, new Scope("openid", "email", "profile"),
                 "not_mail");
-        storeMetadata(storageService, clientId, "mockSecret");
+        storeMetadata(storageService, clientId, "mockSecret", scope);
         storeConsent(storageService, "jdoe", clientId, "mail");
         request.addHeader("Authorization", token.toAuthorizationHeader());
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
@@ -217,7 +219,7 @@ public class UserInfoTest extends AbstractOidcApiFlowTest {
         final TokenDeliveryClaimsClaimsSet set = new TokenDeliveryClaimsClaimsSet();
         set.setClaim("nickname", "mockNickname");
         final BearerAccessToken token = buildToken(clientId, subject, new Scope("openid", "profile"), set);
-        storeMetadata(storageService, clientId, "mockSecret");
+        storeMetadata(storageService, clientId, "mockSecret", scope);
         request.addHeader("Authorization", token.toAuthorizationHeader());
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
         final UserInfoSuccessResponse response = parseSuccessResponse(result, UserInfoSuccessResponse.class);
@@ -233,8 +235,8 @@ public class UserInfoTest extends AbstractOidcApiFlowTest {
     public void testSuccessEmailResolutionAndIssuerWithSignedResponse() throws URISyntaxException,
         NoSuchAlgorithmException, DataSealerException, ComponentInitializationException, IOException, ParseException {
         final BearerAccessToken token = buildToken(clientId, subject, new Scope("openid", "email", "profile"));
-        storeMetadata(storageService, clientId, "mockSecret", null, ClientAuthenticationMethod.CLIENT_SECRET_BASIC,
-                JWSAlgorithm.RS256);
+        storeMetadata(storageService, clientId, "mockSecret", scope, null,
+                ClientAuthenticationMethod.CLIENT_SECRET_BASIC, JWSAlgorithm.RS256);
         storeConsent(storageService, "jdoe", clientId, "mail");
         request.addHeader("Authorization", token.toAuthorizationHeader());
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/BaseOIDCResponseActionTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/BaseOIDCResponseActionTest.java
index a84600b6..15cb4612 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/BaseOIDCResponseActionTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/BaseOIDCResponseActionTest.java
@@ -198,7 +198,7 @@ public abstract class BaseOIDCResponseActionTest {
         respCtx.setProcessedToken(jwt);
     }
 
-    protected DataSealer getDataSealer() throws ComponentInitializationException, NoSuchAlgorithmException {
+    public DataSealer getDataSealer() throws ComponentInitializationException, NoSuchAlgorithmException {
         if (dataSealer == null) {
             dataSealer = initializeDataSealer();
         }
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetSubjectToResponseContextTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetSubjectToResponseContextTest.java
index 4cdef58e..1ca8c150 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetSubjectToResponseContextTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetSubjectToResponseContextTest.java
@@ -17,6 +17,7 @@
 
 package net.shibboleth.idp.plugin.oidc.op.profile.impl;
 
+import net.shibboleth.idp.plugin.oidc.op.profile.OidcEventIds;
 import net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.TokenRequestSubjectLookupFunction;
 import net.shibboleth.idp.plugin.oidc.op.token.support.AccessTokenClaimsSet;
 import net.shibboleth.idp.plugin.oidc.op.token.support.TokenClaimsSet;
@@ -29,7 +30,6 @@ import java.net.URISyntaxException;
 import java.security.NoSuchAlgorithmException;
 import java.time.Instant;
 
-import org.opensaml.profile.action.EventIds;
 import org.springframework.webflow.execution.Event;
 import org.testng.Assert;
 import org.testng.annotations.Test;
@@ -53,7 +53,7 @@ public class SetSubjectToResponseContextTest extends BaseOIDCResponseActionTest
     public void testNoSubject() throws ComponentInitializationException {
         init();
         final Event event = action.execute(requestCtx);
-        ActionTestingSupport.assertEvent(event, EventIds.INVALID_PROFILE_CTX);
+        ActionTestingSupport.assertEvent(event, OidcEventIds.INVALID_SUBJECT);
     }
 
     /**
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/profile/impl/ValidateScopeTest.java
index cd041d40..29d23edc 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/profile/impl/ValidateScopeTest.java
@@ -85,7 +85,9 @@ public class ValidateScopeTest extends BaseOIDCResponseActionTest {
     }
 
     /**
-     * Test that action filters out non valid scopes on front-channel.
+     * Test that action filters out non valid scopes during an Authentication request.
+     * 
+     * <p>The message here is created by the base class's setup method.</p>
      * 
      * @throws ComponentInitializationException
      */
@@ -95,14 +97,16 @@ public class ValidateScopeTest extends BaseOIDCResponseActionTest {
         ActionTestingSupport.assertProceedEvent(event);
         // input is openid, profile, offline_access and email. profile and offline_access should be filtered out
         // (offline because the request is implicit).
+        Assert.assertNotNull(respCtx.getScope());
         Assert.assertTrue(respCtx.getScope().contains(OIDCScopeValue.OPENID));
         Assert.assertTrue(respCtx.getScope().contains(OIDCScopeValue.EMAIL));
         Assert.assertFalse(respCtx.getScope().contains(OIDCScopeValue.OFFLINE_ACCESS));
         Assert.assertFalse(respCtx.getScope().contains(OIDCScopeValue.PROFILE));
     }
-
+    
    /**
-    * Test that action copes if there are no registered scopes.
+    * Test that action copes if there are no registered scopes in the metadata during an
+    * Authentication request.
     * 
     * @throws ComponentInitializationException
     */
@@ -120,6 +124,8 @@ public class ValidateScopeTest extends BaseOIDCResponseActionTest {
     /**
      * Test that action filters out unregistered scopes on back-channel with no prior grant.
      * 
+     * <p>This is most common for the client_credentials grant, which has no prior step.</p>
+     * 
      * @throws ComponentInitializationException
      * @throws URISyntaxException
      */
@@ -152,6 +158,8 @@ public class ValidateScopeTest extends BaseOIDCResponseActionTest {
    /**
     * Test that action filters out unregistered scopes on back-channel with prior grant of nothing.
     * 
+    * <p>This is likely impossible in practice, but theoretical.</p>
+    * 
     * @throws ComponentInitializationException
     * @throws URISyntaxException
     */
@@ -224,6 +232,8 @@ public class ValidateScopeTest extends BaseOIDCResponseActionTest {
   /**
    * Test that action filters out unregistered scopes on UserInfo with prior grants.
    * 
+   * <p>Exercises case with prior grants but nothing requested.</p>
+   * 
    * @throws ComponentInitializationException
    * @throws URISyntaxException
    */

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


More information about the commits mailing list