[java-idp-oidc] branch dev/JOIDC-7 updated: Adapt authorize flow to handle JWT and resource/audience enhancements.

Scott Cantor cantor.2 at osu.edu
Tue Apr 26 18:15:28 UTC 2022


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

scantor pushed a commit to branch dev/JOIDC-7
in repository java-idp-oidc.

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

The following commit(s) were added to refs/heads/dev/JOIDC-7 by this push:
     new d097d284 Adapt authorize flow to handle JWT and resource/audience enhancements.
d097d284 is described below

commit d097d284ca8bcae277f43ca4773b5cab58df5fc8
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Tue Apr 26 14:15:25 2022 -0400

    Adapt authorize flow to handle JWT and resource/audience enhancements.
    
    Also restores nonce to access/refresh tokens.
---
 .../op/token/support/AccessTokenClaimsSet.java     |   1 +
 .../op/token/support/RefreshTokenClaimsSet.java    |   1 +
 .../op/oauth2/profile/impl/BuildAccessToken.java   |   6 +-
 .../impl/SetAccessTokenToResponseContext.java      | 332 ------------------
 .../idp/flows/oidc/authorize/authorize-beans.xml   | 381 +++++++++++++++------
 .../idp/flows/oidc/authorize/authorize-flow.xml    | 140 +++++++-
 .../shibboleth/idp/flows/oidc/token/token-flow.xml |   2 +-
 .../oidc/op/profile/flow/AbstractOidcFlowTest.java |   2 +-
 .../oidc/op/profile/flow/AuthorizeFlowTest.java    | 219 +++++++++---
 .../impl/SetAccessTokenToResponseContextTest.java  | 283 ---------------
 10 files changed, 588 insertions(+), 779 deletions(-)

diff --git a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/token/support/AccessTokenClaimsSet.java b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/token/support/AccessTokenClaimsSet.java
index 9c24d4c4..81347dbe 100644
--- a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/token/support/AccessTokenClaimsSet.java
+++ b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/token/support/AccessTokenClaimsSet.java
@@ -184,6 +184,7 @@ public final class AccessTokenClaimsSet extends TokenClaimsSet {
             setPrincipal(existing.getPrincipal());
             setSubject(existing.getClaimsSet().getSubject());
             setACR(existing.getACR() == null ? null : new ACR(existing.getACR()));
+            setNonce(existing.getNonce());
             setNotBefore(existing.getNotBefore());
             setAuthenticationTime(existing.getAuthenticationTime());
             setAudience(existing.getAudience());
diff --git a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/token/support/RefreshTokenClaimsSet.java b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/token/support/RefreshTokenClaimsSet.java
index c3e77403..682363e9 100644
--- a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/token/support/RefreshTokenClaimsSet.java
+++ b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/token/support/RefreshTokenClaimsSet.java
@@ -137,6 +137,7 @@ public final class RefreshTokenClaimsSet extends TokenClaimsSet {
             setSubject(existing.getClaimsSet().getSubject());
             setACR(existing.getACR() == null ? null : new ACR(existing.getACR()));
             setAuthenticationTime(existing.getAuthenticationTime());
+            setNonce(existing.getNonce());
             setRedirectURI(existing.getRedirectURI());
             setScope(existing.getScope());
             setClaimsRequest(existing.getClaimsRequest());
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/BuildAccessToken.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/BuildAccessToken.java
index 8e627a3c..7460ed66 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/BuildAccessToken.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/BuildAccessToken.java
@@ -454,12 +454,10 @@ public class BuildAccessToken extends AbstractOIDCResponseAction {
         try {
             if (jwtTokenType) {
                 accessTokenCtx.setJWT(new PlainJWT(claimsSet.getClaimsSet()));
-                log.debug("{} Claims stored to JWT access token: {}", getLogPrefix(), claimsSet.serialize(),
-                        accessTokenCtx.getJWT());
+                log.debug("{} Claims stored to JWT access token: {}", getLogPrefix(), claimsSet.serialize());
             } else { 
                 accessTokenCtx.setOpaque(claimsSet.serialize(dataSealer));
-                log.debug("{} Claims '{}' converted to opaque access token: {}", getLogPrefix(), claimsSet.serialize(),
-                        accessTokenCtx.getOpaque());
+                log.debug("{} Claims converted to opaque access token: {}", getLogPrefix(), claimsSet.serialize());
             }
         } catch (final DataSealerException e) {
             log.error("{} Access Token wrapping failed: {}", getLogPrefix(), e);
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetAccessTokenToResponseContext.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetAccessTokenToResponseContext.java
deleted file mode 100644
index befe031a..00000000
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetAccessTokenToResponseContext.java
+++ /dev/null
@@ -1,332 +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.time.Duration;
-import java.time.Instant;
-import java.util.function.Function;
-import java.util.function.Predicate;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import org.opensaml.profile.action.EventIds;
-import org.opensaml.profile.context.ProfileRequestContext;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.nimbusds.oauth2.sdk.Scope;
-import com.nimbusds.openid.connect.sdk.AuthenticationRequest;
-import com.nimbusds.openid.connect.sdk.claims.ClaimsSet;
-
-import net.minidev.json.JSONArray;
-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.OIDCAuthenticationResponseContext;
-import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCAuthenticationResponseTokenClaimsContext;
-import net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.OIDCAuthenticationResponseContextLookupFunction;
-import net.shibboleth.idp.plugin.oidc.op.token.support.AccessTokenClaimsSet;
-import net.shibboleth.idp.plugin.oidc.op.token.support.AuthorizeCodeClaimsSet;
-import net.shibboleth.idp.plugin.oidc.op.token.support.RefreshTokenClaimsSet;
-import net.shibboleth.idp.plugin.oidc.op.token.support.TokenClaimsSet;
-import net.shibboleth.idp.profile.IdPEventIds;
-import net.shibboleth.idp.profile.context.navigate.ResponderIdLookupFunction;
-import net.shibboleth.oidc.profile.config.logic.AttributeConsentFlowEnabledPredicate;
-import net.shibboleth.oidc.profile.config.navigate.AccessTokenLifetimeLookupFunction;
-
-import org.opensaml.messaging.context.navigate.ChildContextLookup;
-import org.opensaml.profile.action.ActionSupport;
-
-import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
-import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
-import net.shibboleth.utilities.java.support.component.ComponentSupport;
-import net.shibboleth.utilities.java.support.logic.Constraint;
-import net.shibboleth.utilities.java.support.logic.FunctionSupport;
-import net.shibboleth.utilities.java.support.security.DataSealer;
-import net.shibboleth.utilities.java.support.security.DataSealerException;
-import net.shibboleth.utilities.java.support.security.IdentifierGenerationStrategy;
-import net.shibboleth.utilities.java.support.security.impl.SecureRandomIdentifierGenerationStrategy;
-
-/**
- * Action that creates a Access Token, and sets it to work context
- * {@link OIDCAuthenticationResponseContext#getAccessToken()} located under
- * {@link ProfileRequestContext#getOutboundMessageContext()}.
- */
-public class SetAccessTokenToResponseContext extends AbstractOIDCResponseAction {
-
-    /** Class logger. */
-    @Nonnull private Logger log = LoggerFactory.getLogger(SetAccessTokenToResponseContext.class);
-
-    /** Data sealer for handling access token. */
-    @NonnullAfterInit private DataSealer dataSealer;
-
-    /** Authorize Code / Refresh Token the access token is based on. */
-    @Nullable private TokenClaimsSet tokenClaimsSet;
-
-    /** Strategy used to obtain the response issuer value. */
-    @Nonnull private Function<ProfileRequestContext, String> issuerLookupStrategy;
-
-    /** Strategy used to obtain the access token lifetime. */
-    @Nonnull private Function<ProfileRequestContext,Duration> accessTokenLifetimeLookupStrategy;
-    
-    /** Predicate used to check if consent is enabled with a given {@link ProfileRequestContext}. */
-    @Nonnull
-    private Predicate<ProfileRequestContext> consentEnabledPredicate;
-
-    /** Access Token lifetime. */
-    @Nullable private Duration accessTokenLifetime;
-    
-    /** Subject context. */
-    @Nullable private SubjectContext subjectCtx;
-
-    /** The generator to use. */
-    @Nullable private IdentifierGenerationStrategy idGenerator;
-
-    /** Strategy used to locate the {@link IdentifierGenerationStrategy} to use. */
-    @Nonnull private Function<ProfileRequestContext, IdentifierGenerationStrategy> idGeneratorLookupStrategy;
-
-    /** Authentication request the token is based on. */
-    @Nullable private AuthenticationRequest authenticationRequest;
-
-    /** Strategy used to locate the {@link OIDCAuthenticationResponseTokenClaimsContext}. */
-    @Nonnull
-    private Function<ProfileRequestContext,OIDCAuthenticationResponseTokenClaimsContext>
-    tokenClaimsContextLookupStrategy;
-    
-    /** Strategy used to locate the {@link OIDCAuthenticationResponseConsentContext}. */
-    @Nonnull
-    private Function<ProfileRequestContext, OIDCAuthenticationResponseConsentContext> consentContextLookupStrategy;
-
-    /**
-     * Constructor.
-     */
-    public SetAccessTokenToResponseContext() {
-        tokenClaimsContextLookupStrategy =
-                new ChildContextLookup<>(OIDCAuthenticationResponseTokenClaimsContext.class).compose(
-                        new OIDCAuthenticationResponseContextLookupFunction());
-        consentContextLookupStrategy =
-                new ChildContextLookup<>(OIDCAuthenticationResponseConsentContext.class).compose(
-                        new OIDCAuthenticationResponseContextLookupFunction());
-        accessTokenLifetimeLookupStrategy = new AccessTokenLifetimeLookupFunction();
-        consentEnabledPredicate = new AttributeConsentFlowEnabledPredicate();
-        issuerLookupStrategy = new ResponderIdLookupFunction();
-        idGeneratorLookupStrategy = FunctionSupport.constant(new SecureRandomIdentifierGenerationStrategy());
-    }
-
-    /**
-     * Set the data sealer instance to use.
-     * 
-     * @param sealer data sealer to use
-     */
-    public void setDataSealer(@Nonnull final DataSealer sealer) {
-        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-        dataSealer = Constraint.isNotNull(sealer, "DataSealer cannot be null");
-    }
-    
-    /**
-     * Set the strategy used to locate the {@link OIDCAuthenticationResponseTokenClaimsContext} associated with a given
-     * {@link ProfileRequestContext}.
-     * 
-     * @param strategy lookup strategy
-     */
-    public void setOIDCAuthenticationResponseTokenClaimsContextLookupStrategy(
-            @Nonnull final Function<ProfileRequestContext, OIDCAuthenticationResponseTokenClaimsContext> strategy) {
-        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-        tokenClaimsContextLookupStrategy = Constraint.isNotNull(strategy,
-                "OIDCAuthenticationResponseTokenClaimsContextt lookup strategy cannot be null");
-    }
-
-    /**
-     * Set the strategy used to locate the {@link OIDCAuthenticationResponseConsentContext} associated with a given
-     * {@link ProfileRequestContext}.
-     * 
-     * @param strategy lookup strategy
-     */
-    public void setOIDCAuthenticationResponseConsentContextLookupStrategy(
-            @Nonnull final Function<ProfileRequestContext, OIDCAuthenticationResponseConsentContext> strategy) {
-        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-        consentContextLookupStrategy = Constraint.isNotNull(strategy,
-                "OIDCAuthenticationResponseConsentContext lookup strategy cannot be null");
-    }
-
-    /**
-     * Set the strategy used to obtain the access token lifetime.
-     * 
-     * @param strategy lookup strategy
-     */
-    public void setAccessTokenLifetimeLookupStrategy(@Nonnull final Function<ProfileRequestContext,Duration> strategy) {
-        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-        
-        accessTokenLifetimeLookupStrategy =
-                Constraint.isNotNull(strategy, "Access token lifetime lookup strategy cannot be null");
-    }
-
-    /**
-     * Set the strategy used to locate the {@link IdentifierGenerationStrategy} to use.
-     * 
-     * @param strategy lookup strategy
-     */
-    public void setIdentifierGeneratorLookupStrategy(
-            @Nonnull final Function<ProfileRequestContext, IdentifierGenerationStrategy> strategy) {
-        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-
-        idGeneratorLookupStrategy =
-                Constraint.isNotNull(strategy, "Identifier generation strategy cannot be null");
-    }
-
-    /**
-     * Set the strategy used to locate the issuer value to use.
-     * 
-     * @param strategy lookup strategy
-     */
-    public void setIssuerLookupStrategy(@Nonnull final Function<ProfileRequestContext, String> strategy) {
-        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-        
-        issuerLookupStrategy = Constraint.isNotNull(strategy, "Issuer lookup strategy cannot be null");
-    }
-
-    /**
-     * Set the predicate used to check if consent is enabled with a given {@link ProfileRequestContext}.
-     * 
-     * @param predicate predicate used to check if consent is enabled with a given {@link ProfileRequestContext}.
-     */
-    public void setConsentEnabledPredicate(@Nonnull final Predicate<ProfileRequestContext> predicate) {
-        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-
-        consentEnabledPredicate =
-                Constraint.isNotNull(predicate, "predicate used to check if consent is enabled cannot be null");
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    protected void doInitialize() throws ComponentInitializationException {
-        super.doInitialize();
-        
-        if (dataSealer == null) {
-            throw new ComponentInitializationException("DataSealer cannot be null");
-        }
-    }
-    
-    // Checkstyle: CyclomaticComplexity OFF
-    /** {@inheritDoc} */
-    @Override
-    protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
-        if (!super.doPreExecute(profileRequestContext)) {
-            return false;
-        }
-        
-        accessTokenLifetime = accessTokenLifetimeLookupStrategy.apply(profileRequestContext);
-        if (accessTokenLifetime == null) {
-            log.warn("{} No lifetime supplied for access token", getLogPrefix());
-            ActionSupport.buildEvent(profileRequestContext, IdPEventIds.INVALID_PROFILE_CONFIG);
-            return false;
-        }
-        
-        tokenClaimsSet = getOidcResponseContext().getAuthorizationGrantClaimsSet();
-        if (tokenClaimsSet != null && !(tokenClaimsSet instanceof RefreshTokenClaimsSet)
-                && !(tokenClaimsSet instanceof AuthorizeCodeClaimsSet)) {
-            log.error("{} No token grant if of illegal type", getLogPrefix());
-            ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
-            return false;
-        } else if (tokenClaimsSet == null) {
-            /*
-             * Alternate path possible only when access token is to be provided by authz endpoint without authorization
-             * code This is the case only with "token id_token" response type. Unusually complex initialization.
-             */
-            subjectCtx = profileRequestContext.getSubcontext(SubjectContext.class, false);
-            if (subjectCtx == null) {
-                log.error("{} No subject context", getLogPrefix());
-                ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
-                return false;
-            }
-            idGenerator = idGeneratorLookupStrategy.apply(profileRequestContext);
-            if (idGenerator == null) {
-                log.error("{} No identifier generation strategy", getLogPrefix());
-                ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
-                return false;
-            }
-            if (profileRequestContext.getInboundMessageContext() == null
-                    || profileRequestContext.getInboundMessageContext().getMessage() == null || !(profileRequestContext
-                            .getInboundMessageContext().getMessage() instanceof AuthenticationRequest)) {
-                log.error("{} No authentication request avalailable", getLogPrefix());
-                ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MSG_CTX);
-                return false;
-            }
-            authenticationRequest =
-                    (AuthenticationRequest) profileRequestContext.getInboundMessageContext().getMessage();
-        }
-        return true;
-    }
-    // Checkstyle: CyclomaticComplexity ON
-
-    /** {@inheritDoc} */
-    @Override
-    protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
-        final Instant dateExp = Instant.now().plus(accessTokenLifetime);
-        ClaimsSet claims = null;
-        ClaimsSet claimsUI = null;
-        final OIDCAuthenticationResponseTokenClaimsContext tokenClaimsCtx =
-                tokenClaimsContextLookupStrategy.apply(profileRequestContext);
-        if (tokenClaimsCtx != null) {
-            claims = tokenClaimsCtx.getClaims();
-            claimsUI = tokenClaimsCtx.getUserinfoClaims();
-        }
-        final AccessTokenClaimsSet claimsSet;
-        if (tokenClaimsSet != null) {
-            // We may not use original claims as input for scope / delivery claims as they may have been reduced.
-            claimsSet = new AccessTokenClaimsSet.Builder(tokenClaimsSet,
-                    getOidcResponseContext().getScope() != null ? getOidcResponseContext().getScope() : new Scope(),
-                    claims, claimsUI, Instant.now(), dateExp).build();
-        } else {
-            final OIDCAuthenticationResponseConsentContext consentCtx =
-                    consentContextLookupStrategy.apply(profileRequestContext);
-            final JSONArray consented = consentCtx != null ? consentCtx.getConsentedAttributes() : null;
-            // "token id_token" response type. Access token is not derived from Authorization code / Refresh token..
-            claimsSet = new AccessTokenClaimsSet.Builder()
-                    .setJWTID(idGenerator)
-                    .setClientID(authenticationRequest.getClientID())
-                    .setIssuer(issuerLookupStrategy.apply(profileRequestContext))
-                    .setPrincipal(subjectCtx.getPrincipalName())
-                    .setSubject(getOidcResponseContext().getSubject())
-                    .setIssuedAt(Instant.now())
-                    .setExpiresAt(dateExp)
-                    .setAuthenticationTime(getOidcResponseContext().getAuthTime())
-                    .setRedirectURI(getOidcResponseContext().getRedirectURI())
-                    .setScope(getOidcResponseContext().getScope())
-                    .setACR(getOidcResponseContext().getAcr())
-                    .setNonce(authenticationRequest.getNonce())
-                    .setClaimsRequest(authenticationRequest.getOIDCClaims())
-                    .setDlClaims(claims)
-                    .setDlClaimsUI(claimsUI)
-                    .setConsentedClaims(consented)
-                    .setConsentEnabled(consentEnabledPredicate.test(profileRequestContext))
-                    .build();
-        }
-        
-        try {
-            getOidcResponseContext().setAccessToken(claimsSet.serialize(dataSealer), accessTokenLifetime);
-            log.debug("{} Setting access token {} as {} to response context ", getLogPrefix(), claimsSet.serialize(),
-                    getOidcResponseContext().getAccessToken());
-        } catch (final DataSealerException e) {
-            log.error("{} Access Token generation failed {}", getLogPrefix(), e.getMessage());
-            ActionSupport.buildEvent(profileRequestContext, EventIds.UNABLE_TO_ENCRYPT);
-        }
-
-    }
-
-}
\ 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 bb60f197..61033f4a 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
@@ -45,9 +45,6 @@
         class="net.shibboleth.idp.plugin.oidc.op.profile.impl.SetRequestedClaimsToResponseContext" scope="prototype"
         p:transcoderRegistry-ref="shibboleth.AttributeRegistryService" />
 
-    <bean id="VerifyRequestedSubjectIdentifier"
-        class="net.shibboleth.idp.plugin.oidc.op.profile.impl.VerifyRequestedSubjectIdentifier" scope="prototype" />
-
     <bean id="PopulatePostAuthnInterceptContext"
             class="net.shibboleth.idp.profile.interceptor.impl.PopulateProfileInterceptorContext" scope="prototype"
             p:availableFlows="#{@'shibboleth.ProfileInterceptorFlowDescriptorManager'.getComponents()}"
@@ -190,35 +187,6 @@
     <bean id="AuthenticationRequestAudienceLookupStrategy"
         class="net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.DefaultRequestAudienceLookupFunction" />
 
-    <bean id="PopulateIDTokenSignatureSigningParameters"
-        class="net.shibboleth.idp.plugin.oidc.op.profile.impl.PopulateOIDCSignatureSigningParameters " scope="prototype"
-        c:strategy-ref="shibboleth.MessageContextLookup.Inbound"
-        p:configurationLookupStrategy-ref="shibboleth.SignatureSigningConfigurationLookup"
-        p:signatureSigningParametersResolver-ref="shibboleth.oidc.TokenSignatureSigningParametersResolver">
-        <property name="activationCondition">
-            <ref bean="IDTokenRequested" />
-        </property>
-        <property name="securityParametersContextLookupStrategy">
-            <bean parent="shibboleth.Functions.Compose"
-                c:g-ref="shibboleth.ChildLookupOrCreate.SecurityParameters"
-                c:f-ref="shibboleth.ChildLookup.RelyingParty" />
-        </property>
-        <property name="existingParametersContextLookupStrategy">
-            <bean parent="shibboleth.Functions.Compose"
-                c:g-ref="shibboleth.ChildLookup.SecurityParameters"
-                c:f-ref="shibboleth.MessageContextLookup.Outbound" />
-        </property>
-    </bean>
-
-    <bean id="PopulateIDTokenEncryptionParameters"
-        class="net.shibboleth.idp.plugin.oidc.op.profile.impl.PopulateOIDCEncryptionParameters" scope="prototype"
-        p:configurationLookupStrategy-ref="shibboleth.EncryptionConfigurationLookup"
-        p:encryptionParametersResolver-ref="shibboleth.oidc.EncryptionParametersResolver">
-        <property name="activationCondition">
-            <ref bean="IDTokenRequested" />
-        </property>
-    </bean>
-
     <bean id="shibboleth.oidc.EncryptionParametersResolver"
         class="net.shibboleth.idp.plugin.oidc.op.security.impl.OIDCClientInformationEncryptionParametersResolver"
         p:remoteJwkSetCache-ref="shibboleth.oidc.RemoteJwkSetCache" p:keyFetchInterval="%{idp.oidc.jwksuri.fetchInterval:PT30M}" />
@@ -227,6 +195,171 @@
         class="org.opensaml.storage.impl.client.PopulateClientStorageLoadContext" scope="prototype"
         p:storageServices="#{ getObject('shibboleth.ClientStorageServices') ?: getObject('shibboleth.DefaultClientStorageServices') }" />
 
+    <bean id="RevokeConsent" class="net.shibboleth.idp.plugin.oidc.op.profile.impl.RevokeConsent" scope="prototype" />
+
+    <bean id="SetAuthenticationTimeToResponseContext"
+        class="net.shibboleth.idp.plugin.oidc.op.profile.impl.SetAuthenticationTimeToResponseContext" scope="prototype" />
+
+    <bean id="SetSectorIdentifierForAttributeResolution"
+        class="net.shibboleth.idp.plugin.oidc.op.profile.impl.SetSectorIdentifierForAttributeResolution" scope="prototype" />
+
+    <bean id="SetAuthenticationContextClassReferenceToResponseContext"
+        class="net.shibboleth.idp.plugin.oidc.op.profile.impl.SetAuthenticationContextClassReferenceToResponseContext"
+        scope="prototype" />
+
+    <!--
+    Do a metadata lookup for the primary audience of the token for encryption purposes.
+    Contexts are stored under the outbound MessageContext, including the new RelyingPartyContext.
+    -->
+    
+    <bean id="AudienceOIDCMetadataLookup" class="net.shibboleth.idp.profile.impl.WebFlowMessageHandlerAdaptor"
+            scope="prototype" c:executionDirection="OUTBOUND">
+        <constructor-arg name="messageHandler">
+            <bean class="net.shibboleth.idp.plugin.oidc.op.profile.impl.OIDCMetadataLookupHandler" scope="prototype">
+                <property name="clientInformationResolver">
+                    <ref bean="shibboleth.ClientInformationResolver" />
+                </property>
+                <property name="clientIDLookupStrategy">
+                    <ref bean="AudienceClientIDLookupStrategy" />
+                </property>
+            </bean>
+        </constructor-arg>
+    </bean>
+    
+    <bean id="AudienceClientIDLookupStrategy"
+        class="net.shibboleth.idp.plugin.oidc.op.messaging.context.navigate.AudienceClientIDLookupFunction" />
+
+    <bean id="InitializeAudienceRelyingPartyContext"
+        class="net.shibboleth.idp.plugin.oidc.op.profile.impl.InitializeRelyingPartyContext" scope="prototype"
+        p:relyingPartyContextCreationStrategy-ref="AudienceRelyingPartyCreationStrategy"
+        p:oidcMetadataContextLookupStrategy-ref="LookupOutboundOIDCMetadataContext"
+        p:clientIDLookupStrategy-ref="AudienceClientIDLookupStrategy"
+        p:inbound="false" />
+
+    <bean id="LookupOutboundOIDCMetadataContext"
+        class="net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.DefaultOIDCMetadataContextLookupFunction"
+        p:inbound="false" />
+
+    <bean id="AudienceRelyingPartyCreationStrategy" parent="shibboleth.Functions.Compose"
+        c:g-ref="shibboleth.ChildLookupOrCreate.RelyingPartyContext"
+        c:f-ref="shibboleth.MessageContextLookup.Outbound" />
+
+    <bean id="AudienceSAMLProtocolAndRole"
+            class="net.shibboleth.idp.profile.impl.WebFlowMessageHandlerAdaptor" scope="prototype"
+            c:executionDirection="OUTBOUND"
+            p:activationCondition-ref="%{idp.oidc.metadata.saml:shibboleth.Conditions.TRUE}">
+        <constructor-arg name="messageHandler">
+            <bean class="org.opensaml.saml.common.binding.impl.SAMLProtocolAndRoleHandler" scope="prototype"
+                p:protocol="http://openid.net/specs/openid-connect-core-1_0.html"
+                p:role-ref="shibboleth.MetadataLookup.Role"
+                p:entityContextClass="org.opensaml.saml.common.messaging.context.SAMLPeerEntityContext"/>
+        </constructor-arg>
+    </bean>
+
+    <bean id="SetAudienceEntityIdToSAMLPeerEntityContext"
+            class="net.shibboleth.idp.profile.impl.WebFlowMessageHandlerAdaptor" scope="prototype"
+            c:executionDirection="OUTBOUND"
+            p:errorEvent="#{T(org.opensaml.profile.action.EventIds).INVALID_MSG_CTX}"
+            p:activationCondition-ref="%{idp.oidc.metadata.saml:shibboleth.Conditions.TRUE}">
+        <constructor-arg name="messageHandler">
+            <bean class="net.shibboleth.idp.plugin.oidc.op.oauth2.messaging.impl.SetEntityIdToSAMLPeerEntityContext"
+                scope="prototype"
+                p:clientIDLookupStrategy-ref="AudienceClientIDLookupStrategy"
+                p:entityContextClass="org.opensaml.saml.common.messaging.context.SAMLPeerEntityContext" />
+        </constructor-arg>
+    </bean>
+ 
+     <bean id="AudienceSAMLMetadataLookup"
+        class="net.shibboleth.idp.profile.impl.WebFlowMessageHandlerAdaptor" scope="prototype"
+        c:executionDirection="OUTBOUND"
+        p:activationCondition-ref="%{idp.oidc.metadata.saml:shibboleth.Conditions.TRUE}">
+        <constructor-arg name="messageHandler">
+            <bean class="org.opensaml.saml.common.binding.impl.SAMLMetadataLookupHandler" scope="prototype"
+                p:entityContextClass="org.opensaml.saml.common.messaging.context.SAMLPeerEntityContext">
+                <property name="roleDescriptorResolver">
+                    <bean class="org.opensaml.saml.metadata.resolver.impl.PredicateRoleDescriptorResolver"
+                        c:mdResolver-ref="shibboleth.MetadataResolver" />
+                </property>
+            </bean>
+        </constructor-arg>
+    </bean>
+ 
+     <bean id="PopulateAudienceOIDCMetadataContext"
+            class="net.shibboleth.idp.profile.impl.WebFlowMessageHandlerAdaptor" scope="prototype"
+            c:executionDirection="OUTBOUND"
+            p:activationCondition-ref="%{idp.oidc.metadata.saml:shibboleth.Conditions.TRUE}">
+        <constructor-arg name="messageHandler">
+            <bean class="net.shibboleth.idp.plugin.oidc.op.oauth2.messaging.impl.PopulateOIDCMetadataContext"
+                scope="prototype" />
+        </constructor-arg>
+    </bean>
+        
+    <bean id="InitializeAudienceRelyingPartyContextFromSAMLPeer"
+        class="net.shibboleth.idp.saml.profile.impl.InitializeRelyingPartyContextFromSAMLPeer" scope="prototype"
+        p:relyingPartyContextCreationStrategy-ref="AudienceRelyingPartyCreationStrategy"
+        p:peerEntityContextLookupStrategy-ref="LookupOutboundPeerEntityContext" />
+
+    <bean id="LookupOutboundPeerEntityContext" parent="shibboleth.Functions.Compose"
+        c:g-ref="shibboleth.ChildLookup.SAMLPeerEntityContext"
+        c:f-ref="shibboleth.MessageContextLookup.Outbound"/>
+
+    <bean id="SelectAudienceRelyingPartyConfiguration"
+        class="net.shibboleth.idp.profile.impl.SelectRelyingPartyConfiguration" scope="prototype"
+        p:relyingPartyContextLookupStrategy-ref="AudienceRelyingPartyCreationStrategy"
+        p:relyingPartyConfigurationResolver-ref="shibboleth.CriteriaRelyingPartyConfigurationResolver" />
+
+    <bean id="SelectAudienceProfileConfiguration"
+        class="net.shibboleth.idp.profile.impl.SelectProfileConfiguration" scope="prototype"
+        p:relyingPartyContextLookupStrategy-ref="AudienceRelyingPartyCreationStrategy"
+        p:profileId="#{T(net.shibboleth.oidc.profile.oauth2.config.OAuth2TokenAudienceConfiguration).PROFILE_ID}" />
+
+    <bean id="ResolveAttributesForAudience" class="net.shibboleth.idp.profile.impl.ResolveAttributes" scope="prototype"
+        c:resolverService-ref="shibboleth.AttributeResolverService"
+        p:transcoderRegistry-ref="shibboleth.AttributeRegistryService"
+        p:maskFailures="%{idp.service.attribute.resolver.maskFailures:true}"
+        p:issuerLookupStrategy-ref="AudienceIssuerLookupFunction" />
+
+    <bean id="FilterAttributesForAudience" class="net.shibboleth.idp.profile.impl.FilterAttributes" scope="prototype"
+        c:filterService-ref="shibboleth.AttributeFilterService"
+        p:maskFailures="%{idp.service.attribute.filter.maskFailures:true}"
+        p:issuerLookupStrategy-ref="AudienceIssuerLookupFunction"
+        p:proxiedRequesterContextLookupStrategy-ref="AudienceProxiedRequesterLookupFunction"
+        p:proxiedRequesterMetadataContextLookupStrategy-ref="LookupOutboundSAMLEntityContext" />
+
+    <bean id="AudienceIssuerLookupFunction"
+        class="net.shibboleth.idp.profile.context.navigate.ResponderIdLookupFunction"
+        p:relyingPartyContextLookupStrategy-ref="AudienceRelyingPartyCreationStrategy" />
+
+    <bean id="AudienceProxiedRequesterLookupFunction" parent="shibboleth.Functions.Compose">
+        <constructor-arg name="g">
+            <bean class="org.opensaml.messaging.context.navigate.ChildContextLookup"
+                c:type="#{ T(org.opensaml.profile.context.ProxiedRequesterContext) }" />
+        </constructor-arg>
+        <constructor-arg name="f">
+            <ref bean="shibboleth.MessageContextLookup.Outbound" />
+        </constructor-arg>
+    </bean>
+
+    <bean id="LookupOutboundSAMLEntityContext" parent="shibboleth.Functions.Compose"
+        c:g-ref="shibboleth.ChildLookup.SAMLMetadataContext"
+        c:f-ref="LookupOutboundPeerEntityContext"/>
+
+    <!-- Back to token prep. -->
+
+    <bean id="SetSubjectToResponseContext"
+        class="net.shibboleth.idp.plugin.oidc.op.profile.impl.SetSubjectToResponseContext" scope="prototype">
+        <property name="subjectLookupStrategy">
+            <bean class="net.shibboleth.idp.plugin.oidc.op.profile.logic.AttributeResolutionSubjectLookupFunction"
+                p:transcoderRegistry-ref="shibboleth.AttributeRegistryService" />
+        </property>
+        <property name="subjectTypeLookupStrategy">
+            <bean class="net.shibboleth.idp.plugin.oidc.op.profile.logic.DefaultSubjectTypeStrategy" />
+        </property>
+    </bean>
+
+    <bean id="VerifyRequestedSubjectIdentifier"
+        class="net.shibboleth.idp.plugin.oidc.op.profile.impl.VerifyRequestedSubjectIdentifier" scope="prototype" />
+
     <bean id="SetTokenDeliveryAttributesToResponseContext"
             class="net.shibboleth.idp.plugin.oidc.op.profile.impl.SetTokenDeliveryAttributesToResponseContext" scope="prototype"
             p:transcoderRegistry-ref="shibboleth.AttributeRegistryService">
@@ -245,8 +378,6 @@
     <bean id="SetConsentToResponseContext"
         class="net.shibboleth.idp.plugin.oidc.op.profile.impl.SetConsentToResponseContext" scope="prototype" />
 
-    <bean id="RevokeConsent" class="net.shibboleth.idp.plugin.oidc.op.profile.impl.RevokeConsent" scope="prototype" />
-
     <bean id="SetAuthorizationCodeToResponseContext"
         class="net.shibboleth.idp.plugin.oidc.op.profile.impl.SetAuthorizationCodeToResponseContext" scope="prototype"
         p:dataSealer-ref="#{'%{idp.oidc.tokenSealer:shibboleth.oidc.TokenSealer}'.trim()}">
@@ -259,106 +390,147 @@
         </property>
     </bean>
 
-    <bean id="SetAccessTokenToResponseContext"
-        class="net.shibboleth.idp.plugin.oidc.op.profile.impl.SetAccessTokenToResponseContext" scope="prototype"
-        p:dataSealer-ref="#{'%{idp.oidc.tokenSealer:shibboleth.oidc.TokenSealer}'.trim()}">
-        <property name="activationCondition">
-            <ref bean="AccessTokenRequested" />
+    <!-- If access token is strictly for UserInfo endpoint... -->
+
+    <bean id="PopulateUserInfoAccessTokenSignatureSigningParameters"
+            class="net.shibboleth.idp.plugin.oidc.op.profile.impl.PopulateOIDCSignatureSigningParameters"
+            scope="prototype"
+            c:strategy-ref="shibboleth.MessageContextLookup.Inbound"
+            p:configurationLookupStrategy-ref="shibboleth.SignatureSigningConfigurationLookup"
+            p:signatureSigningParametersResolver-ref="shibboleth.oidc.TokenSignatureSigningParametersResolver">
+        <property name="securityParametersContextLookupStrategy">
+            <bean parent="shibboleth.Functions.Compose"
+                c:g-ref="shibboleth.ChildLookupOrCreate.SecurityParameters"
+                c:f-ref="shibboleth.ChildLookup.RelyingParty" />
         </property>
     </bean>
 
-    <bean id="SetSubjectToResponseContext"
-        class="net.shibboleth.idp.plugin.oidc.op.profile.impl.SetSubjectToResponseContext" scope="prototype">
-        <property name="subjectLookupStrategy">
-            <bean class="net.shibboleth.idp.plugin.oidc.op.profile.logic.AttributeResolutionSubjectLookupFunction"
-                p:transcoderRegistry-ref="shibboleth.AttributeRegistryService" />
-        </property>
-        <property name="subjectTypeLookupStrategy">
-            <bean class="net.shibboleth.idp.plugin.oidc.op.profile.logic.DefaultSubjectTypeStrategy" />
+    <bean id="BuildOIDCAccessToken"
+        class="net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl.BuildAccessToken" scope="prototype"
+        p:dataSealer="#{getObject('%{idp.oidc.tokenSealer:shibboleth.oidc.TokenSealer}'.trim())}"
+        p:clientIDLookupStrategy-ref="RequestClientIDLookup" />
+        
+    <bean id="RequestClientIDLookup" parent="shibboleth.Functions.Compose"
+        c:g-ref="shibboleth.ClientIDLookupStrategy"
+        c:f-ref="shibboleth.MessageContextLookup.Inbound" />
+
+    <bean id="SignOIDCAccessToken"
+            class="net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl.SignAccessToken" scope="prototype"
+            p:typeHeader="at+jwt">
+        <property name="securityParametersLookupStrategy">
+            <bean parent="shibboleth.Functions.Compose"
+                c:g-ref="shibboleth.ChildLookup.SecurityParameters"
+                c:f-ref="shibboleth.ChildLookup.RelyingParty" />
         </property>
     </bean>
+ 
+    <bean id="SetOAuthAccessTokenToResponseContext"
+        class="net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl.SetAccessTokenToResponseContext"
+        scope="prototype" />
 
-    <bean id="SetAuthenticationTimeToResponseContext"
-        class="net.shibboleth.idp.plugin.oidc.op.profile.impl.SetAuthenticationTimeToResponseContext" scope="prototype" />
-
-    <bean id="SetSectorIdentifierForAttributeResolution"
-        class="net.shibboleth.idp.plugin.oidc.op.profile.impl.SetSectorIdentifierForAttributeResolution" scope="prototype" />
+    <!-- If access token is also for third-party resource... -->
 
-    <bean id="SetAuthenticationContextClassReferenceToResponseContext"
-        class="net.shibboleth.idp.plugin.oidc.op.profile.impl.SetAuthenticationContextClassReferenceToResponseContext"
-        scope="prototype" />
+    <bean id="PopulateThirdPartyAccessTokenSignatureSigningParameters"
+        class="net.shibboleth.idp.plugin.oidc.op.profile.impl.PopulateOIDCSignatureSigningParameters"
+        scope="prototype"
+        c:strategy-ref="shibboleth.MessageContextLookup.Outbound"
+        p:configurationLookupStrategy-ref="shibboleth.SignatureSigningConfigurationLookup"
+        p:signatureSigningParametersResolver-ref="shibboleth.oidc.TokenSignatureSigningParametersResolver"
+        p:securityParametersContextLookupStrategy-ref="AudienceSecurityParametersCreationStrategy" />
+        
+    <bean id="AudienceSecurityParametersCreationStrategy" parent="shibboleth.Functions.Compose"
+        c:g-ref="shibboleth.ChildLookupOrCreate.SecurityParameters"
+        c:f-ref="AudienceRelyingPartyCreationStrategy" />
+
+    <bean id="AddAttributeClaimsToAccessToken"
+        class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddAttributesToClaimsSet" scope="prototype"
+        p:transcoderRegistry-ref="shibboleth.AttributeRegistryService"
+        p:responseClaimsSetLookupStrategy-ref="AccessTokenClaimsSetLookupFunction"
+        p:reservedClaimNames="#{getObject('shibboleth.oidc.AccessTokenReservedClaimNames') ?: getObject('shibboleth.oidc.DefaultAccessTokenReservedClaimNames')}" />
+
+    <bean id="AccessTokenClaimsSetLookupFunction"
+        class="net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.AccessTokenClaimsSetLookupFunction"
+        p:autoCreate="true" />
+
+    <bean id="BuildAccessToken"
+        class="net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl.BuildAccessToken" scope="prototype"
+        p:dataSealer="#{getObject('%{idp.oidc.tokenSealer:shibboleth.oidc.TokenSealer}'.trim())}"
+        p:issuerLookupStrategy-ref="AudienceIssuerLookupFunction"
+        p:clientIDLookupStrategy-ref="RequestClientIDLookup"
+        p:accessTokenTypeLookupStrategy-ref="AccessTokenTypeLookupFunction"
+        p:accessTokenLifetimeLookupStrategy-ref="AccessTokenLifetimeLookupFunction" />
+
+    <bean id="AccessTokenTypeLookupFunction"
+        class="net.shibboleth.oidc.profile.config.navigate.AccessTokenTypeLookupFunction"
+        p:relyingPartyContextLookupStrategy-ref="AudienceRelyingPartyCreationStrategy" />
+        
+    <bean id="AccessTokenLifetimeLookupFunction"
+        class="net.shibboleth.oidc.profile.config.navigate.AccessTokenLifetimeLookupFunction"
+        p:relyingPartyContextLookupStrategy-ref="AudienceRelyingPartyCreationStrategy" />
+
+    <bean id="SignAccessToken"
+        class="net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl.SignAccessToken" scope="prototype"
+        p:securityParametersLookupStrategy-ref="AudienceSecurityParametersCreationStrategy"
+        p:typeHeader="at+jwt" />
+
+    <!--  ID token actions. -->
 
-    <bean id="AddIDTokenShell" class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddIDTokenShell" scope="prototype">
-        <property name="activationCondition">
-            <ref bean="IDTokenRequested" />
+    <bean id="PopulateIDTokenSignatureSigningParameters"
+        class="net.shibboleth.idp.plugin.oidc.op.profile.impl.PopulateOIDCSignatureSigningParameters " scope="prototype"
+        c:strategy-ref="shibboleth.MessageContextLookup.Inbound"
+        p:configurationLookupStrategy-ref="shibboleth.SignatureSigningConfigurationLookup"
+        p:signatureSigningParametersResolver-ref="shibboleth.oidc.TokenSignatureSigningParametersResolver">
+        <property name="securityParametersContextLookupStrategy">
+            <bean parent="shibboleth.Functions.Compose"
+                c:g-ref="shibboleth.ChildLookupOrCreate.SecurityParameters"
+                c:f-ref="shibboleth.ChildLookup.RelyingParty" />
+        </property>
+        <property name="existingParametersContextLookupStrategy">
+            <bean parent="shibboleth.Functions.Compose"
+                c:g-ref="shibboleth.ChildLookup.SecurityParameters"
+                c:f-ref="shibboleth.MessageContextLookup.Outbound" />
         </property>
     </bean>
 
+    <bean id="PopulateIDTokenEncryptionParameters"
+        class="net.shibboleth.idp.plugin.oidc.op.profile.impl.PopulateOIDCEncryptionParameters" scope="prototype"
+        p:configurationLookupStrategy-ref="shibboleth.EncryptionConfigurationLookup"
+        p:encryptionParametersResolver-ref="shibboleth.oidc.EncryptionParametersResolver" />
+
+    <bean id="AddIDTokenShell" class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddIDTokenShell" scope="prototype" />
+
     <bean id="AddAttributeClaimsToIDToken"
             class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddAttributesToClaimsSet" scope="prototype"
             p:transcoderRegistry-ref="shibboleth.AttributeRegistryService"
-            p:reservedClaimNames="#{getObject('shibboleth.oidc.IDTokenReservedClaimNames') ?: getObject('shibboleth.oidc.DefaultIDTokenReservedClaimNames')}">
-        <property name="activationCondition">
-            <ref bean="IDTokenRequested" />
-        </property>
-    </bean>
+            p:reservedClaimNames="#{getObject('shibboleth.oidc.IDTokenReservedClaimNames') ?: getObject('shibboleth.oidc.DefaultIDTokenReservedClaimNames')}" />
 
     <bean id="AddAuthTimeToIDToken" class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddAuthTimeToIDToken"
-        scope="prototype">
-        <property name="activationCondition">
-            <ref bean="IDTokenRequested" />
-        </property>
-    </bean>
+        scope="prototype" />
 
-    <bean id="AddAcrToIDToken" class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddAcrToIDToken" scope="prototype">
-        <property name="activationCondition">
-            <ref bean="IDTokenRequested" />
-        </property>
-    </bean>
+    <bean id="AddAcrToIDToken" class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddAcrToIDToken"
+        scope="prototype" />
 
     <bean id="AddNonceToIDToken" class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddNonceToIDToken"
-        scope="prototype">
-        <property name="activationCondition">
-            <ref bean="IDTokenRequested" />
-        </property>
-    </bean>
+        scope="prototype" />
 
     <bean id="AddAccessTokenHashToIDToken"
-        class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddAccessTokenHashToIDToken" scope="prototype">
+            class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddAccessTokenHashToIDToken" scope="prototype"
+            p:activationCondition-ref="AccessTokenRequested">
         <property name="securityParametersLookupStrategy">
             <bean parent="shibboleth.Functions.Compose"
                 c:g-ref="shibboleth.ChildLookup.SecurityParameters"
                 c:f-ref="shibboleth.ChildLookup.RelyingParty" />
         </property>
-        <property name="activationCondition">
-            <bean parent="shibboleth.Conditions.AND">
-                <constructor-arg>
-                    <ref bean="AccessTokenRequested" />
-                </constructor-arg>
-                <constructor-arg>
-                    <ref bean="IDTokenRequested" />
-                </constructor-arg>
-            </bean>
-        </property>
     </bean>
 
     <bean id="AddAuthorizationCodeHashToIDToken"
-        class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddAuthorizationCodeHashToIDToken" scope="prototype">
+            class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddAuthorizationCodeHashToIDToken" scope="prototype"
+            p:activationCondition-ref="AuthorizeCodeRequested">
         <property name="securityParametersLookupStrategy">
             <bean parent="shibboleth.Functions.Compose"
                 c:g-ref="shibboleth.ChildLookup.SecurityParameters"
                 c:f-ref="shibboleth.ChildLookup.RelyingParty" />
         </property>
-        <property name="activationCondition">
-            <bean parent="shibboleth.Conditions.AND">
-                <constructor-arg>
-                    <ref bean="AuthorizeCodeRequested" />
-                </constructor-arg>
-                <constructor-arg>
-                    <ref bean="IDTokenRequested" />
-                </constructor-arg>
-            </bean>
-        </property>
     </bean>
 
     <bean id="SignIDToken" class="net.shibboleth.idp.plugin.oidc.op.profile.impl.SignIDToken" scope="prototype">
@@ -367,17 +539,10 @@
                 c:g-ref="shibboleth.ChildLookup.SecurityParameters"
                 c:f-ref="shibboleth.ChildLookup.RelyingParty" />
         </property>
-        <property name="activationCondition">
-            <ref bean="IDTokenRequested" />
-        </property>
     </bean>
 
     <bean id="EncryptIDToken" class="net.shibboleth.idp.plugin.oidc.op.profile.impl.EncryptProcessedToken"
-        scope="prototype">
-        <property name="activationCondition">
-            <ref bean="IDTokenRequested" />
-        </property>
-    </bean>
+        scope="prototype" />
 
     <bean id="UpdateSessionWithSPSession"
             class="net.shibboleth.idp.session.impl.UpdateSessionWithSPSession" scope="prototype"
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 7578912b..48096df1 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
@@ -53,8 +53,6 @@
         <evaluate expression="ValidateAudience" />
         <evaluate expression="SetRequestedClaimsToResponseContext" />
         <evaluate expression="SetRequestedSubjectToResponseContext" />
-        <evaluate expression="PopulateIDTokenSignatureSigningParameters" />
-        <evaluate expression="PopulateIDTokenEncryptionParameters" />
         <evaluate expression="'proceed'" />
         <transition on="proceed" to="PopulateClientStorageLoadContext" />
     </action-state>
@@ -87,17 +85,81 @@
     <action-state id="SetAuthenticationInformationToResponseContext">
         <evaluate expression="SetAuthenticationContextClassReferenceToResponseContext" />
         <evaluate expression="SetAuthenticationTimeToResponseContext" />
+        <evaluate expression="SetSectorIdentifierForAttributeResolution" />
         <evaluate expression="'proceed'" />
-        <transition on="proceed" to="AttributeResolution" />
+        <transition on="proceed" to="CheckForAudience" />
     </action-state>
 
-    <action-state id="AttributeResolution">
-        <evaluate expression="SetSectorIdentifierForAttributeResolution" />
+    <!-- Audience may or may not be a factor. -->
+    <decision-state id="CheckForAudience">
+        <if test="opensamlProfileRequestContext.getOutboundMessageContext().getSubcontext(T(net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCAuthenticationResponseContext)).getAudience().isEmpty()"
+            then="AttributeResolutionForClient"
+            else="LookupAudienceMetadata" />
+    </decision-state>
+
+    <!-- May need to add a second Relying Party for the primary resource/audience. -->
+
+    <action-state id="LookupAudienceMetadata">
+        <evaluate expression="AudienceOIDCMetadataLookup" />
+        <evaluate expression="InitializeAudienceRelyingPartyContext" />
+        <evaluate expression="'proceed'" />
+    
+        <transition on="proceed" to="CheckIfAudienceFoundFromClientInformationService" />
+    </action-state>
+    
+    <decision-state id="CheckIfAudienceFoundFromClientInformationService">
+        <if test="opensamlProfileRequestContext.getOutboundMessageContext().containsSubcontext(T(net.shibboleth.oidc.metadata.context.OIDCMetadataContext))"
+            then="SelectAudienceProfileConfiguration" else="LookupAudienceSAMLMetadata" />
+    </decision-state>
+    
+    <action-state id="LookupAudienceSAMLMetadata">
+        <evaluate expression="AudienceSAMLProtocolAndRole" />
+        <evaluate expression="SetAudienceEntityIdToSAMLPeerEntityContext" />
+        <evaluate expression="AudienceSAMLMetadataLookup" />
+        <evaluate expression="'proceed'" />
+
+        <transition on="proceed" to="CheckIfAudienceFoundFromSAMLMetadata" />
+    </action-state>
+
+    <decision-state id="CheckIfAudienceFoundFromSAMLMetadata">
+        <if test="opensamlProfileRequestContext.getOutboundMessageContext().containsSubcontext(T(org.opensaml.saml.common.messaging.context.SAMLPeerEntityContext)) and opensamlProfileRequestContext.getOutboundMessageContext().getSubcontext(T(org.opensaml.saml.common.messaging.context.SAMLPeerEntityContext)).containsSubcontext(T(org.opensaml.saml.common.messaging.context.SAMLMetadataContext))"
+            then="PopulateAudienceOIDCMetadataContextFromSAML"
+            else="SelectAudienceProfileConfiguration" />
+    </decision-state>
+    
+    <action-state id="PopulateAudienceOIDCMetadataContextFromSAML">
+        <evaluate expression="PopulateAudienceOIDCMetadataContext" />
+        <evaluate expression="InitializeAudienceRelyingPartyContextFromSAMLPeer" />
+        <evaluate expression="'proceed'" />
+        
+        <transition on="proceed" to="SelectAudienceProfileConfiguration" />
+    </action-state>
+
+    <action-state id="SelectAudienceProfileConfiguration">
+        <evaluate expression="SelectAudienceRelyingPartyConfiguration" />
+        <evaluate expression="SelectAudienceProfileConfiguration" />
+        <evaluate expression="'proceed'" />
+
+        <transition on="proceed" to="AttributeResolutionForAudience" />
+    </action-state>
+
+    <action-state id="AttributeResolutionForClient">
         <evaluate expression="ResolveAttributes" />
         <evaluate expression="FilterAttributes" />
         <evaluate expression="RevokeConsent" />
         <evaluate expression="PopulatePostAuthnInterceptContext" />
         <evaluate expression="'proceed'" />
+        
+        <transition on="proceed" to="CheckPostAuthnInterceptContext" />
+    </action-state>
+
+    <action-state id="AttributeResolutionForAudience">
+        <evaluate expression="ResolveAttributesForAudience" />
+        <evaluate expression="FilterAttributesForAudience" />
+        <evaluate expression="RevokeConsent" />
+        <evaluate expression="PopulatePostAuthnInterceptContext" />
+        <evaluate expression="'proceed'" />
+        
         <transition on="proceed" to="CheckPostAuthnInterceptContext" />
     </action-state>
 
@@ -117,19 +179,75 @@
         <evaluate expression="SetSubjectToResponseContext" />
         <evaluate expression="VerifyRequestedSubjectIdentifier" />
         <evaluate expression="'proceed'" />
-        <transition on="proceed" to="BuildTokens" />
+        
+        <transition on="proceed" to="BuildCode" />
     </action-state>
 
-    <action-state id="BuildTokens">
+    <action-state id="BuildCode">
         <evaluate expression="SetTokenDeliveryAttributesToResponseContext" />
         <evaluate expression="SetConsentToResponseContext" />
         <evaluate expression="SetAuthorizationCodeToResponseContext" />
-        <evaluate expression="SetAccessTokenToResponseContext" />
         <evaluate expression="'proceed'" />
-        <transition on="proceed" to="BuildResponse" />
+        
+        <transition on="proceed" to="CheckIfAccessTokenNeeded" />
+    </action-state>
+
+    <decision-state id="CheckIfAccessTokenNeeded">
+        <if test="AccessTokenRequested.test(opensamlProfileRequestContext)"
+            then="CheckTokenRequirements"
+            else="CheckIfIDTokenNeeded" />
+    </decision-state>
+
+    <decision-state id="CheckTokenRequirements">
+        <if test="opensamlProfileRequestContext.getOutboundMessageContext().getSubcontext(T(net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCAuthenticationResponseContext)).getAudience().isEmpty()"
+            then="BuildTokensForUserInfoAccess"
+            else="BuildTokensForThirdPartyAccess" />
+    </decision-state>
+
+    <!--
+    Note no JWT encryption here. The token shouldn't even be a JWT, but even if it is,
+    the only audience is us, and the client can't be expected to decrypt it, so it
+    wouldn't be usable. If it were encrypted to our key then there would be no point to
+    allowing it to be a JWT.
+    
+    Note also no attribute claims are added to the access token since that isn't a proper
+    delivery mechanism for claims to the OIDC client.
+    -->
+    <action-state id="BuildTokensForUserInfoAccess">
+        <evaluate expression="PopulateUserInfoAccessTokenSignatureSigningParameters" />
+        <evaluate expression="BuildOIDCAccessToken" />
+        <evaluate expression="SignOIDCAccessToken" />
+        <evaluate expression="SetOAuthAccessTokenToResponseContext" />
+        <evaluate expression="'proceed'" />
+        
+        <transition on="proceed" to="CheckIfIDTokenNeeded" />
+    </action-state>
+
+    <!--
+    This includes OIDC use cases and all grant types but the primary audience
+    for the access token is the resource/audience. Encryption remains impossible until
+    this supports pure OAuth scenarios.
+    -->
+    <action-state id="BuildTokensForThirdPartyAccess">
+        <evaluate expression="PopulateThirdPartyAccessTokenSignatureSigningParameters" />
+        <evaluate expression="AddAttributeClaimsToAccessToken" />
+        <evaluate expression="BuildAccessToken" />
+        <evaluate expression="SignAccessToken" />
+        <evaluate expression="SetOAuthAccessTokenToResponseContext" />
+        <evaluate expression="'proceed'" />
+        
+        <transition on="proceed" to="CheckIfIDTokenNeeded" />
     </action-state>
 
-    <action-state id="BuildResponse">
+    <decision-state id="CheckIfIDTokenNeeded">
+        <if test="IDTokenRequested.test(opensamlProfileRequestContext)"
+            then="BuildIDToken"
+            else="PopulateClientStorageSaveContext" />
+    </decision-state>
+
+    <action-state id="BuildIDToken">
+        <evaluate expression="PopulateIDTokenSignatureSigningParameters" />
+        <evaluate expression="PopulateIDTokenEncryptionParameters" />
         <evaluate expression="AddIDTokenShell" />
         <evaluate expression="AddAttributeClaimsToIDToken" />
         <evaluate expression="AddAuthTimeToIDToken" />
@@ -139,12 +257,12 @@
         <evaluate expression="AddAuthorizationCodeHashToIDToken" />
         <evaluate expression="SignIDToken" />
         <evaluate expression="EncryptIDToken" />
-        <evaluate expression="UpdateSessionWithSPSession" />
         <evaluate expression="'proceed'" />
         <transition on="proceed" to="PopulateClientStorageSaveContext" />
      </action-state>
 
     <action-state id="PopulateClientStorageSaveContext">
+        <evaluate expression="UpdateSessionWithSPSession" />
         <evaluate expression="PopulateClientStorageSaveContext" />
         <evaluate expression="'proceed'" />
         <transition on="proceed" to="ClientStorageSave" />
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-flow.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-flow.xml
index f1bba542..a2be67eb 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-flow.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-flow.xml
@@ -85,7 +85,7 @@
         <transition on="proceed" to="LookupAudienceMetadata" />
     </action-state>
 
-    <!-- For client credentials grant, need to add a second Relying Party for the primary resource/audience. -->
+    <!-- May need to add a second Relying Party for the primary resource/audience. -->
 
     <action-state id="LookupAudienceMetadata">
         <evaluate expression="AudienceOIDCMetadataLookup" />
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 b5a6b294..be7781d1 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
@@ -243,7 +243,7 @@ public abstract class AbstractOidcFlowTest extends AbstractFlowTest {
         metadata.setIDTokenJWEEnc(encMethod);
         metadata.setTokenEndpointAuthMethod(tokenEndpointMethod);
         metadata.setUserInfoJWSAlg(userInfoSigAlg);
-        metadata.setCustomField("audience", List.of("https://rp.example.org", "https://rp2.example.org"));
+        metadata.setCustomField("audience", List.of("https://rp.example.org", "https://rp2.example.org", "https://resource.example.org"));
         final OIDCClientInformation information;
         if (publicKey == null) {
             information = new OIDCClientInformation(new ClientID(clientId), new Date(),
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 62bb2070..13fb2f9d 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,9 +18,12 @@
 package net.shibboleth.idp.plugin.oidc.op.profile.flow;
 
 import java.io.IOException;
+import java.text.ParseException;
 import java.time.Duration;
 import java.time.Instant;
+import java.util.Collections;
 import java.util.Date;
+import java.util.List;
 
 import org.opensaml.storage.StorageService;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -36,13 +39,13 @@ 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.ParseException;
 import com.nimbusds.oauth2.sdk.Scope;
 import com.nimbusds.openid.connect.sdk.AuthenticationResponse;
 import com.nimbusds.openid.connect.sdk.AuthenticationSuccessResponse;
 import com.nimbusds.openid.connect.sdk.claims.ClaimRequirement;
 import com.nimbusds.openid.connect.sdk.claims.ClaimsSetRequest;
 
+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;
@@ -55,6 +58,8 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     
     public static final String FLOW_ID = "oidc/authorize";
     
+    String resource = "https://resource.example.org";
+    String issuer = "https://op.example.org";
     String redirectUri = "https://example.org/cb";
     String clientId = "mockClientId";
     String clientSecret = "mockClientSecretmockClientSecretmockClientSecretmockClientSecretmockClientSecret";
@@ -74,7 +79,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     }
 
     @Test
-    public void testWithAuthorizationCodeFlow() throws IOException, ParseException, SessionException {
+    public void testWithAuthorizationCodeFlow() throws IOException, SessionException {
         request.setMethod("GET");
         request.setQueryString("client_id=mockClientId&response_type=code&scope=openid%20profile&redirect_uri="
                 + redirectUri);
@@ -92,7 +97,25 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     }
 
     @Test
-    public void testWithAuthorizationCodeFlowNoOpenid() throws IOException, ParseException, SessionException {
+    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);
+        storeMetadata(storageService, clientId, clientSecret, scope, redirectUri);
+
+        initializeThreadLocals();
+        
+        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 testWithAuthorizationCodeFlowNoOpenid() throws IOException, SessionException {
         request.setMethod("GET");
         request.setQueryString("client_id=mockClientId&response_type=code&scope=profile&redirect_uri="
                 + redirectUri);
@@ -105,7 +128,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     }
 
     @Test
-    public void testWithImplicitFlowNoNonce() throws IOException, ParseException, SessionException {
+    public void testWithImplicitFlowNoNonce() throws IOException, SessionException {
         request.setMethod("GET");
         request.setQueryString("client_id=mockClientId&response_type=id_token&scope=openid%20profile&redirect_uri="
                 + redirectUri);
@@ -118,7 +141,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     }
 
     @Test
-    public void testWithImplicitFlow() throws IOException, ParseException, SessionException {
+    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");
@@ -136,7 +159,25 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     }
 
     @Test
-    public void testWithImplicitTokenFlow() throws IOException, ParseException, SessionException {
+    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");
+        storeMetadata(storageService, clientId, clientSecret, scope, redirectUri);
+
+        initializeThreadLocals();
+        
+        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.assertNotNull(successResponse.getIDToken());
+        Assert.assertNull(successResponse.getAccessToken());
+        Assert.assertNull(successResponse.getAuthorizationCode());
+    }
+
+    @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");
@@ -151,10 +192,37 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
         Assert.assertNotNull(successResponse.getIDToken());
         Assert.assertNotNull(successResponse.getAccessToken());
         Assert.assertNull(successResponse.getAuthorizationCode());
+        
+        final AccessTokenClaimsSet token =
+                AccessTokenClaimsSet.parse(successResponse.getAccessToken().getValue(), getDataSealer());
+        Assert.assertEquals(token.getAudience(), Collections.singletonList(issuer));
+        Assert.assertNull(token.getClaimsSet().getStringClaim("eduPersonScopedAffiliation"));
     }
 
     @Test
-    public void testWithImplicitTokenFlowNoNonce() throws IOException, ParseException, SessionException {
+    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");
+        storeMetadata(storageService, clientId, clientSecret, scope, redirectUri);
+
+        initializeThreadLocals();
+        
+        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.assertNotNull(successResponse.getIDToken());
+        Assert.assertNotNull(successResponse.getAccessToken());
+        Assert.assertNull(successResponse.getAuthorizationCode());
+
+        final JWTClaimsSet token =  SignedJWT.parse(successResponse.getAccessToken().getValue()).getJWTClaimsSet();
+        Assert.assertEquals(token.getAudience(), List.of(resource, issuer));
+        Assert.assertNotNull(token.getStringClaim("eduPersonScopedAffiliation"));
+    }
+
+    @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);
@@ -167,7 +235,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     }
 
     @Test
-    public void testWithImplicitFlowNoOpenIdScope() throws IOException, ParseException, SessionException {
+    public void testWithImplicitFlowNoOpenIdScope() throws IOException, SessionException {
         request.setMethod("GET");
         request.setQueryString("client_id=mockClientId&response_type=id_token&scope=profile&redirect_uri="
                 + redirectUri + "&nonce=idhas3h23hi13h1o2i32");
@@ -180,7 +248,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     }
 
     @Test
-    public void testWithHybridIdTokenFlow() throws IOException, ParseException, SessionException {
+    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");
@@ -198,7 +266,25 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     }
 
     @Test
-    public void testWithHybridIdTokenFlowNoNonce() throws IOException, ParseException, SessionException {
+    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");
+        storeMetadata(storageService, clientId, clientSecret, scope, redirectUri);
+
+        initializeThreadLocals();
+        
+        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.assertNotNull(successResponse.getIDToken());
+        Assert.assertNull(successResponse.getAccessToken());
+        Assert.assertNotNull(successResponse.getAuthorizationCode());
+    }
+
+    @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);
@@ -211,7 +297,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     }
 
     @Test
-    public void testWithHybridTokenFlow() throws IOException, ParseException, SessionException {
+    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);
@@ -227,10 +313,38 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
         Assert.assertNull(successResponse.getIDToken());
         Assert.assertNotNull(successResponse.getAccessToken());
         Assert.assertNotNull(successResponse.getAuthorizationCode());
+        
+        final AccessTokenClaimsSet token =
+                AccessTokenClaimsSet.parse(successResponse.getAccessToken().getValue(), getDataSealer());
+        Assert.assertEquals(token.getAudience(), Collections.singletonList(issuer));
+        Assert.assertNull(token.getClaimsSet().getStringClaim("eduPersonScopedAffiliation"));
     }
 
     @Test
-    public void testWithHybridIdTokenTokenFlow() throws IOException, ParseException, SessionException {
+    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);
+        storeMetadata(storageService, clientId, clientSecret, scope, redirectUri);
+
+        initializeThreadLocals();
+        
+        // success response as id_token is not involved and thus nonce is not required
+        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.assertNotNull(successResponse.getAccessToken());
+        Assert.assertNotNull(successResponse.getAuthorizationCode());
+
+        final JWTClaimsSet token =  SignedJWT.parse(successResponse.getAccessToken().getValue()).getJWTClaimsSet();
+        Assert.assertEquals(token.getAudience(), List.of(resource, issuer));
+        Assert.assertNotNull(token.getStringClaim("eduPersonScopedAffiliation"));
+    }
+
+    @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");
@@ -245,10 +359,37 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
         Assert.assertNotNull(successResponse.getIDToken());
         Assert.assertNotNull(successResponse.getAccessToken());
         Assert.assertNotNull(successResponse.getAuthorizationCode());
+
+        final AccessTokenClaimsSet token =
+                AccessTokenClaimsSet.parse(successResponse.getAccessToken().getValue(), getDataSealer());
+        Assert.assertEquals(token.getAudience(), Collections.singletonList(issuer));
+        Assert.assertNull(token.getClaimsSet().getStringClaim("eduPersonScopedAffiliation"));
+    }
+
+    @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");
+        storeMetadata(storageService, clientId, clientSecret, scope, redirectUri);
+
+        initializeThreadLocals();
+        
+        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.assertNotNull(successResponse.getIDToken());
+        Assert.assertNotNull(successResponse.getAccessToken());
+        Assert.assertNotNull(successResponse.getAuthorizationCode());
+
+        final JWTClaimsSet token =  SignedJWT.parse(successResponse.getAccessToken().getValue()).getJWTClaimsSet();
+        Assert.assertEquals(token.getAudience(), List.of(resource, issuer));
+        Assert.assertNotNull(token.getStringClaim("eduPersonScopedAffiliation"));
     }
 
     @Test
-    public void testWithHybridIdTokenTokenFlowNoNonce() throws IOException, ParseException, SessionException {
+    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);
@@ -261,7 +402,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     }
 
     @Test
-    public void testWithAuthorizationCodeFlowUnforcedPKCE() throws IOException, ParseException, SessionException {
+    public void testWithAuthorizationCodeFlowUnforcedPKCE() throws IOException, SessionException {
         request.setMethod("GET");
         request.setQueryString("client_id=mockClientIdPKCEPlainUnforced&response_type=code&scope=openid%20profile&redirect_uri="
                 + redirectUri);
@@ -279,7 +420,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     }
 
     @Test
-    public void testWithAuthorizationCodeFlowForcedPlainPKCEMissingChallenge() throws IOException, ParseException, SessionException {
+    public void testWithAuthorizationCodeFlowForcedPlainPKCEMissingChallenge() throws IOException, SessionException {
         request.setMethod("GET");
         request.setQueryString("client_id=mockClientIdPKCEPlain&response_type=code&scope=openid%20profile&redirect_uri="
                 + redirectUri);
@@ -293,7 +434,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     }
 
     @Test
-    public void testWithAuthorizationCodeFlowForcedPlainPKCEUnknownChallenge() throws IOException, ParseException, SessionException {
+    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");
@@ -307,7 +448,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     }
 
     @Test
-    public void testWithAuthorizationCodeFlowForcedPlainPKCEValidChallenge() throws IOException, ParseException, SessionException {
+    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");
@@ -325,7 +466,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     }
 
     @Test
-    public void testWithAuthorizationCodeFlowForcedS256PKCEPlainChallenge() throws IOException, ParseException, SessionException {
+    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");
@@ -339,7 +480,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     }
 
     @Test
-    public void testWithAuthorizationCodeFlowForcedS256PKCEUnknownChallenge() throws IOException, ParseException, SessionException {
+    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");
@@ -353,7 +494,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     }
 
     @Test
-    public void testWithAuthorizationCodeFlowForcedS256PKCEValidChallenge() throws IOException, ParseException, SessionException {
+    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");
@@ -371,7 +512,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     }
 
     @Test
-    public void testWithAuthorizationCodeFlowNoScopes() throws IOException, ParseException, SessionException {
+    public void testWithAuthorizationCodeFlowNoScopes() throws IOException, SessionException {
         request.setMethod("GET");
         request.setQueryString("client_id=mockClientId&response_type=code&scope=openid%20profile&redirect_uri="
                 + redirectUri);
@@ -387,7 +528,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     }
     
     @Test
-    public void testWithAuthorizationCodeFlowWithIDTokenClaims() throws IOException, ParseException, SessionException, java.text.ParseException, DataSealerException {
+    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"
@@ -416,7 +557,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     }
     
     @Test
-    public void testWithAuthorizationCodeFlowWithUIClaims() throws IOException, ParseException, SessionException, java.text.ParseException, DataSealerException {
+    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"
@@ -445,7 +586,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     }
 
     @Test
-    public void testWithAuthorizationCodeFlowUsingSAMLMetadata() throws IOException, ParseException, SessionException {
+    public void testWithAuthorizationCodeFlowUsingSAMLMetadata() throws IOException, SessionException {
         request.setMethod("GET");
         request.setQueryString("client_id=mockSamlClientId&response_type=code&scope=openid%20profile&redirect_uri="
                 + redirectUri);
@@ -463,7 +604,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     }
     
     @Test
-    public void testWithAuthorizationCodeFlowUsingUntrustedRP() throws IOException, ParseException, SessionException {
+    public void testWithAuthorizationCodeFlowUsingUntrustedRP() throws IOException, SessionException {
         request.setMethod("GET");
         request.setQueryString("client_id=notTrusted&response_type=code&scope=openid%20profile&redirect_uri="
                 + redirectUri);
@@ -475,7 +616,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     }
 
     @Test
-    public void testWithPlainReqObjectExpired() throws IOException, ParseException, SessionException {
+    public void testWithPlainReqObjectExpired() throws IOException, SessionException {
         final JWTClaimsSet ro = new JWTClaimsSet.Builder()
                 .expirationTime(Date.from(Instant.now().minus(Duration.ofMinutes(5))))
                 .build();
@@ -483,7 +624,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     }
 
     @Test
-    public void testWithPlainReqObjectNbfInFuture() throws IOException, ParseException, SessionException {
+    public void testWithPlainReqObjectNbfInFuture() throws IOException, SessionException {
         final JWTClaimsSet ro = new JWTClaimsSet.Builder()
                 .notBeforeTime(Date.from(Instant.now().plus(Duration.ofMinutes(5))))
                 .build();
@@ -491,7 +632,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     }
 
     @Test
-    public void testWithPlainReqObjectOverwriteRedirectUri() throws IOException, ParseException, SessionException {
+    public void testWithPlainReqObjectOverwriteRedirectUri() throws IOException, SessionException {
         final JWTClaimsSet ro = new JWTClaimsSet.Builder()
                 .claim("redirect_uri", redirectUri)
                 .build();
@@ -513,8 +654,8 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     }
 
     @Test
-    public void testWithPlainReqObjectClaimsRequest() throws IOException, ParseException, SessionException,
-            java.text.ParseException, DataSealerException {
+    public void testWithPlainReqObjectClaimsRequest() throws IOException, SessionException,
+            DataSealerException, ParseException {
         final String payload = "{\n"
                 + "  \"iss\": \"" + clientId + "\",\n"
                 + "  \"response_type\": \"code\",\n"
@@ -578,16 +719,16 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     }
 
     @Test
-    public void testWithSignedReqObjectNoIssuer() throws IOException, ParseException, SessionException,
+    public void testWithSignedReqObjectNoIssuer() throws IOException, SessionException,
             JOSEException {
         final JWTClaimsSet ro = new JWTClaimsSet.Builder()
-                .audience("https://op.example.org")
+                .audience(issuer)
                 .build();
         assertRequestObjectError(createSecretJWT(ro, clientSecret));
     }
 
     @Test
-    public void testWithSignedReqObjectNoAudience() throws IOException, ParseException, SessionException,
+    public void testWithSignedReqObjectNoAudience() throws IOException, SessionException,
             JOSEException {
         final JWTClaimsSet ro = new JWTClaimsSet.Builder()
                 .issuer(clientId)
@@ -596,17 +737,17 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     }
 
     @Test
-    public void testWithSignedReqObjectWrongIssuer() throws IOException, ParseException, SessionException,
+    public void testWithSignedReqObjectWrongIssuer() throws IOException, SessionException,
             JOSEException {
         final JWTClaimsSet ro = new JWTClaimsSet.Builder()
-                .audience("https://op.example.org")
+                .audience(issuer)
                 .issuer("invalid")
                 .build();
         assertRequestObjectError(createSecretJWT(ro, clientSecret));
     }
 
     @Test
-    public void testWithSignedReqObjectWrongAudience() throws IOException, ParseException, SessionException,
+    public void testWithSignedReqObjectWrongAudience() throws IOException, SessionException,
             JOSEException {
         final JWTClaimsSet ro = new JWTClaimsSet.Builder()
                 .audience("https://invalid.org")
@@ -619,7 +760,7 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     public void testWithSignedReqObjectOverwriteRedirectUri() throws IOException, ParseException,
             SessionException, JOSEException {
         final JWTClaimsSet ro = new JWTClaimsSet.Builder()
-                .audience("https://op.example.org")
+                .audience(issuer)
                 .issuer(clientId)
                 .claim("redirect_uri", redirectUri)
                 .build();
@@ -641,8 +782,8 @@ public class AuthorizeFlowTest extends AbstractOidcFlowTest {
     }
 
     @Test
-    public void testWithSignedReqObjectClaimsRequest() throws IOException, ParseException,
-            SessionException, JOSEException, java.text.ParseException, DataSealerException {
+    public void testWithSignedReqObjectClaimsRequest() throws IOException,
+            SessionException, JOSEException, DataSealerException, ParseException {
         final String payload = "{\n"
                 + "  \"iss\": \"" + clientId + "\",\n"
                 + "  \"response_type\": \"code\",\n"
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetAccessTokenToResponseContextTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetAccessTokenToResponseContextTest.java
deleted file mode 100644
index e6fa6333..00000000
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetAccessTokenToResponseContextTest.java
+++ /dev/null
@@ -1,283 +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 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.token.support.AccessTokenClaimsSet;
-import net.shibboleth.idp.plugin.oidc.op.token.support.AuthorizeCodeClaimsSet;
-import net.shibboleth.idp.plugin.oidc.op.token.support.TokenClaimsSet;
-import net.shibboleth.idp.profile.IdPEventIds;
-import net.shibboleth.idp.profile.context.RelyingPartyContext;
-import net.shibboleth.idp.profile.testing.ActionTestingSupport;
-import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
-import net.shibboleth.utilities.java.support.security.DataSealerException;
-
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.security.NoSuchAlgorithmException;
-import java.text.ParseException;
-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;
-
-import com.nimbusds.oauth2.sdk.RefreshTokenGrant;
-import com.nimbusds.oauth2.sdk.Scope;
-import com.nimbusds.oauth2.sdk.TokenRequest;
-import com.nimbusds.oauth2.sdk.id.ClientID;
-import com.nimbusds.oauth2.sdk.token.RefreshToken;
-import com.nimbusds.openid.connect.sdk.claims.ACR;
-
-// Checkstyle: ThrowsCount OFF
-
-/** {@link SetAccessTokenToResponseContext} unit test. */
-public class SetAccessTokenToResponseContextTest extends BaseOIDCResponseActionTest {
-
-    /** Action to test. */
-    private SetAccessTokenToResponseContext action;
-
-    private void init() throws ComponentInitializationException, NoSuchAlgorithmException, URISyntaxException {
-        respCtx.setScope(new Scope());
-        final TokenClaimsSet claims = new AuthorizeCodeClaimsSet.Builder()
-                .setJWTID(idGenerator)
-                .setClientID(new ClientID())
-                .setIssuer("issuer")
-                .setPrincipal("userPrin")
-                .setSubject("subject")
-                .setIssuedAt(Instant.now())
-                .setExpiresAt(Instant.now())
-                .setAuthenticationTime(Instant.now())
-                .setRedirectURI(new URI("http://example.com"))
-                .setScope(new Scope())
-                .setACR(new ACR("0"))
-                .build();
-        respCtx.setSubject("subject");
-        respCtx.setAuthTime(Instant.now());
-        respCtx.setAuthorizationGrantClaimsSet(claims);
-        respCtx.setAcr("0");
-        respCtx.setRedirectURI(new URI("http://example.com"));
-        action = new SetAccessTokenToResponseContext();
-        action.setDataSealer(getDataSealer());
-        action.initialize();
-        final SubjectContext subjectCtx = profileRequestCtx.getSubcontext(SubjectContext.class, true);
-        subjectCtx.setPrincipalName("userPrin");
-    }
-
-    /**
-     * Basic success case.
-     * 
-     * @throws ComponentInitializationException 
-     * @throws NoSuchAlgorithmException 
-     * @throws URISyntaxException 
-     * @throws ParseException 
-     * @throws DataSealerException 
-     */
-    @Test
-    public void testSuccess() throws ComponentInitializationException, NoSuchAlgorithmException, URISyntaxException,
-            ParseException, DataSealerException {
-        init();
-        final Event event = action.execute(requestCtx);
-        ActionTestingSupport.assertProceedEvent(event);
-        Assert.assertNotNull(respCtx.getAccessToken());
-        final AccessTokenClaimsSet at =
-                AccessTokenClaimsSet.parse(respCtx.getAccessToken().getValue(), getDataSealer());
-        Assert.assertNotNull(at);
-    }
-
-    /**
-     * Basic success case for non derived token.
-     * 
-     * @throws ComponentInitializationException 
-     * @throws NoSuchAlgorithmException 
-     * @throws URISyntaxException 
-     * @throws ParseException 
-     * @throws DataSealerException 
-     */
-    @Test
-    public void testSuccess2() throws ComponentInitializationException, NoSuchAlgorithmException, URISyntaxException,
-            ParseException, DataSealerException {
-        init();
-        respCtx.setAuthorizationGrantClaimsSet(null);
-        final Event event = action.execute(requestCtx);
-        ActionTestingSupport.assertProceedEvent(event);
-        Assert.assertNotNull(respCtx.getAccessToken());
-        final AccessTokenClaimsSet at =
-                AccessTokenClaimsSet.parse(respCtx.getAccessToken().getValue(), getDataSealer());
-        Assert.assertNotNull(at);
-    }
-
-    /**
-     * Basic success case for non derived token. Test for consent.
-     * 
-     * @throws ComponentInitializationException 
-     * @throws NoSuchAlgorithmException 
-     * @throws URISyntaxException 
-     * @throws ParseException 
-     * @throws DataSealerException 
-     */
-    @Test
-    public void testSuccess2Consent() throws ComponentInitializationException, NoSuchAlgorithmException,
-            URISyntaxException, ParseException, DataSealerException {
-        init();
-        respCtx.setAuthorizationGrantClaimsSet(null);
-        final OIDCAuthenticationResponseConsentContext consCtx =
-                (OIDCAuthenticationResponseConsentContext) respCtx.addSubcontext(
-                        new OIDCAuthenticationResponseConsentContext());
-        consCtx.getConsentedAttributes().add("3");
-        final Event event = action.execute(requestCtx);
-        ActionTestingSupport.assertProceedEvent(event);
-        Assert.assertNotNull(respCtx.getAccessToken());
-        final AccessTokenClaimsSet at =
-                AccessTokenClaimsSet.parse(respCtx.getAccessToken().getValue(), getDataSealer());
-        Assert.assertNotNull(at);
-        Assert.assertEquals(at.getConsentedClaims(), consCtx.getConsentedAttributes());
-    }
-
-    /**
-     * Basic success case with delivery claims.
-     * 
-     * @throws ComponentInitializationException 
-     * @throws NoSuchAlgorithmException 
-     * @throws URISyntaxException 
-     * @throws ParseException 
-     * @throws DataSealerException 
-     */
-    @Test
-    public void testSuccessWithTokenDelivery() throws ComponentInitializationException, NoSuchAlgorithmException,
-            URISyntaxException, ParseException, DataSealerException {
-        init();
-        final OIDCAuthenticationResponseTokenClaimsContext tokenCtx =
-                (OIDCAuthenticationResponseTokenClaimsContext) respCtx.addSubcontext(
-                        new OIDCAuthenticationResponseTokenClaimsContext());
-        tokenCtx.getClaims().setClaim("1", "1");
-        tokenCtx.getIdtokenClaims().setClaim("2", "2");
-        tokenCtx.getUserinfoClaims().setClaim("3", "3");
-        final Event event = action.execute(requestCtx);
-        ActionTestingSupport.assertProceedEvent(event);
-        Assert.assertNotNull(respCtx.getAccessToken());
-        final AccessTokenClaimsSet at =
-                AccessTokenClaimsSet.parse(respCtx.getAccessToken().getValue(), getDataSealer());
-        Assert.assertNotNull(at);
-        Assert.assertNotNull(at.getDeliveryClaims().getClaim("1"));
-        Assert.assertNotNull(at.getUserinfoDeliveryClaims().getClaim("3"));
-        Assert.assertNull(at.getIDTokenDeliveryClaims());
-    }
-
-    /**
-     * fails as request is of wrong type.
-     * 
-     * @throws NoSuchAlgorithmException 
-     * @throws ComponentInitializationException 
-     * @throws URISyntaxException 
-     */
-    @Test
-    public void testFailNoAuthnReqCase2()
-            throws NoSuchAlgorithmException, ComponentInitializationException, URISyntaxException {
-        init();
-        respCtx.setAuthorizationGrantClaimsSet(null);
-        final TokenRequest req =
-                new TokenRequest(new URI("http://example.com"), new RefreshTokenGrant(new RefreshToken()), null);
-        setTokenRequest(req);
-        final Event event = action.execute(requestCtx);
-        ActionTestingSupport.assertEvent(event, EventIds.INVALID_MSG_CTX);
-    }
-
-    /**
-     * fails as there is no subject ctx.
-     * 
-     * @throws NoSuchAlgorithmException 
-     * @throws ComponentInitializationException 
-     * @throws URISyntaxException 
-     */
-    @Test
-    public void testFailNoSubjectCtxCase2()
-            throws NoSuchAlgorithmException, ComponentInitializationException, URISyntaxException {
-        init();
-        respCtx.setAuthorizationGrantClaimsSet(null);
-        profileRequestCtx.removeSubcontext(SubjectContext.class);
-        final Event event = action.execute(requestCtx);
-        ActionTestingSupport.assertEvent(event, EventIds.INVALID_PROFILE_CTX);
-    }
-
-    /**
-     * fails as there is no rp ctx.
-     * 
-     * @throws NoSuchAlgorithmException 
-     * @throws ComponentInitializationException 
-     * @throws URISyntaxException 
-     */
-    @Test
-    public void testFailNoRPCtx()
-            throws NoSuchAlgorithmException, ComponentInitializationException, URISyntaxException {
-        init();
-        profileRequestCtx.removeSubcontext(RelyingPartyContext.class);
-        final Event event = action.execute(requestCtx);
-        ActionTestingSupport.assertEvent(event, IdPEventIds.INVALID_PROFILE_CONFIG);
-    }
-
-    /**
-     * fails as there is no profile conf.
-     * 
-     * @throws NoSuchAlgorithmException 
-     * @throws ComponentInitializationException 
-     * @throws URISyntaxException 
-     */
-    @Test
-    public void testFailNoProfileConf()
-            throws NoSuchAlgorithmException, ComponentInitializationException, URISyntaxException {
-        init();
-        final RelyingPartyContext rpCtx = profileRequestCtx.getSubcontext(RelyingPartyContext.class, false);
-        rpCtx.setProfileConfig(null);
-        final Event event = action.execute(requestCtx);
-        ActionTestingSupport.assertEvent(event, IdPEventIds.INVALID_PROFILE_CONFIG);
-    }
-
-    /**
-     * fails as the token is of wrong type.
-     * 
-     * @throws NoSuchAlgorithmException 
-     * @throws ComponentInitializationException 
-     * @throws URISyntaxException 
-     */
-    @Test
-    public void testFailTokenNotCodeOrRefresh()
-            throws NoSuchAlgorithmException, ComponentInitializationException, URISyntaxException {
-        init();
-        final TokenClaimsSet claims = new AccessTokenClaimsSet.Builder()
-                .setJWTID(idGenerator)
-                .setClientID(new ClientID())
-                .setIssuer("issuer")
-                .setPrincipal("userPrin")
-                .setSubject("subject")
-                .setIssuedAt(Instant.now())
-                .setExpiresAt(Instant.now())
-                .setAuthenticationTime(Instant.now())
-                .setRedirectURI(new URI("http://example.com"))
-                .setScope(new Scope())
-                .build();
-        
-        respCtx.setAuthorizationGrantClaimsSet(claims);
-        final Event event = action.execute(requestCtx);
-        ActionTestingSupport.assertEvent(event, EventIds.INVALID_PROFILE_CTX);
-    }
-
-}
\ No newline at end of file

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


More information about the commits mailing list