[java-idp-oidc] 01/02: Initial draft of reworked token flow.

Scott Cantor cantor.2 at osu.edu
Mon Apr 18 17:33:03 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=cac37e1c1a7a36f6dec8545c9f4ede50c9b33b6c

commit cac37e1c1a7a36f6dec8545c9f4ede50c9b33b6c
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Thu Apr 14 14:50:33 2022 -0400

    Initial draft of reworked token flow.
---
 .../AbstractTokenClaimsLookupFunction.java         |   2 +-
 .../DefaultResponseClaimsSetLookupFunction.java    |   2 +-
 ...uthenticationResponseContextLookupFunction.java |   4 +-
 .../TokenRequestRedirectURILookupFunction.java     |   2 +-
 .../op/oauth2/profile/impl/BuildAccessToken.java   | 235 +++++++++++++++++----
 .../op/oauth2/profile/impl/ValidateAudience.java   |  50 ++++-
 .../profile/impl/AddAccessTokenHashToIDToken.java  |   3 +-
 .../op/profile/impl/AddAttributesToClaimsSet.java  |   1 -
 ...liveryAttributesFromTokenToResponseContext.java |   2 +-
 .../plugin/oidc/op/profile/impl/ValidateGrant.java |   6 +-
 .../oidc/op/profile/impl/ValidateRedirectURI.java  |   2 +-
 .../plugin/oidc/op/profile/impl/ValidateScope.java |   4 +-
 ...uteConsentEnabledInTokenClaimsSetPredicate.java |   2 +-
 .../userinfo/profile/impl/ValidateAccessToken.java |   1 +
 .../idp/flows/oidc/token/token-beans.xml           | 159 ++++++++------
 .../shibboleth/idp/flows/oidc/token/token-flow.xml | 107 +++++++---
 .../oauth2/profile/impl/BuildAccessTokenTest.java  |   7 +-
 .../plugin/oidc/op/profile/flow/TokenFlowTest.java |   7 +-
 18 files changed, 433 insertions(+), 163 deletions(-)

diff --git a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/AbstractTokenClaimsLookupFunction.java b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/AbstractTokenClaimsLookupFunction.java
index f74cc2e9..94dc3e1e 100644
--- a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/AbstractTokenClaimsLookupFunction.java
+++ b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/AbstractTokenClaimsLookupFunction.java
@@ -50,7 +50,7 @@ public abstract class AbstractTokenClaimsLookupFunction<T>
             return null;
         }
         final OIDCAuthenticationResponseContext oidcResponseContext =
-                input.getOutboundMessageContext().getSubcontext(OIDCAuthenticationResponseContext.class, false);
+                input.getOutboundMessageContext().getSubcontext(OIDCAuthenticationResponseContext.class);
         if (oidcResponseContext == null) {
             return null;
         }
diff --git a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultResponseClaimsSetLookupFunction.java b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultResponseClaimsSetLookupFunction.java
index 3d00b22b..92fd6c03 100644
--- a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultResponseClaimsSetLookupFunction.java
+++ b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/DefaultResponseClaimsSetLookupFunction.java
@@ -36,7 +36,7 @@ public class DefaultResponseClaimsSetLookupFunction
             return null;
         }
         final OIDCAuthenticationResponseContext ctx =
-                input.getOutboundMessageContext().getSubcontext(OIDCAuthenticationResponseContext.class, false);
+                input.getOutboundMessageContext().getSubcontext(OIDCAuthenticationResponseContext.class);
         if (ctx == null) {
             return null;
         }
diff --git a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/OIDCAuthenticationResponseContextLookupFunction.java b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/OIDCAuthenticationResponseContextLookupFunction.java
index 13eb87c3..b5ec3ed5 100644
--- a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/OIDCAuthenticationResponseContextLookupFunction.java
+++ b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/OIDCAuthenticationResponseContextLookupFunction.java
@@ -29,7 +29,7 @@ import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCAuthenticationRes
  * {@link ProfileRequestContext}.
  */
 public class OIDCAuthenticationResponseContextLookupFunction
-        implements ContextDataLookupFunction<ProfileRequestContext, OIDCAuthenticationResponseContext> {
+        implements ContextDataLookupFunction<ProfileRequestContext,OIDCAuthenticationResponseContext> {
 
     /** {@inheritDoc} */
     @Nullable
@@ -37,7 +37,7 @@ public class OIDCAuthenticationResponseContextLookupFunction
         if (input == null || input.getOutboundMessageContext() == null) {
             return null;
         }
-        return input.getOutboundMessageContext().getSubcontext(OIDCAuthenticationResponseContext.class, false);
+        return input.getOutboundMessageContext().getSubcontext(OIDCAuthenticationResponseContext.class);
     }
 
 }
diff --git a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/TokenRequestRedirectURILookupFunction.java b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/TokenRequestRedirectURILookupFunction.java
index 81f4ce6d..1b5b1015 100644
--- a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/TokenRequestRedirectURILookupFunction.java
+++ b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/TokenRequestRedirectURILookupFunction.java
@@ -57,7 +57,7 @@ public class TokenRequestRedirectURILookupFunction extends AbstractTokenRequestL
         try {
             uri = new URI(redirectURI);
         } catch (final URISyntaxException e) {
-            log.warn("Unable to parse uri from token request redirect_uri {}", redirectURI);
+            log.warn("Unable to parse URI from token request redirect_uri {}", redirectURI);
         }
         return uri;
     }
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 ba01d36c..8e627a3c 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
@@ -20,6 +20,7 @@ package net.shibboleth.idp.plugin.oidc.op.oauth2.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;
@@ -34,16 +35,26 @@ import org.slf4j.LoggerFactory;
 import com.nimbusds.jwt.PlainJWT;
 import com.nimbusds.oauth2.sdk.Scope;
 import com.nimbusds.oauth2.sdk.id.ClientID;
+import com.nimbusds.openid.connect.sdk.AuthenticationRequest;
+import com.nimbusds.openid.connect.sdk.claims.ClaimsSet;
 
-import net.shibboleth.idp.attribute.context.AttributeContext;
+import net.minidev.json.JSONArray;
+import net.shibboleth.idp.authn.context.SubjectContext;
 import net.shibboleth.idp.plugin.oidc.op.messaging.context.AccessTokenContext;
+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.profile.context.navigate.TokenRequestClientIDLookupFunction;
 import net.shibboleth.idp.plugin.oidc.op.profile.impl.AbstractOIDCResponseAction;
 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.plugin.oidc.op.token.support.AccessTokenClaimsSet.Builder;
 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 net.shibboleth.oidc.profile.config.navigate.AccessTokenTypeLookupFunction;
 
@@ -60,12 +71,13 @@ import net.shibboleth.utilities.java.support.security.impl.SecureRandomIdentifie
 
 /**
  * Action that creates an Access Token, and stores it to an {@link AccessTokenContext}.
+ *
+ * <p>There are various cases handled across different grant types and orders of operation.
+ * The token may be produced solely for a third-party service to consume, or may also or instead
+ * be usable with the OP's UserInfo endpoint.</p>
  * 
- * <p>This supports arbitrary ("generic") OAuth access tokens requested by the "client_credentials" grant type.
- * This is a "pure" OAuth use case and does not involve any OIDC behavior.</p>
- * 
- * <p>The action supports either fully opaque access tokens sealed under the IdP's secret key, or
- * the RFC 9068 standard for JWT-based tokens.</p>
+ * <p>The action supports either opaque access tokens sealed under the IdP's secret key, or the
+ * RFC 9068 standard for JWT-based tokens.</p>
  * 
  * @event {@link EventIds#PROCEED_EVENT_ID}
  * @event {@link EventIds#MESSAGE_PROC_ERROR}
@@ -101,21 +113,38 @@ public class BuildAccessToken extends AbstractOIDCResponseAction {
     /** Strategy used to locate the {@link IdentifierGenerationStrategy} to use. */
     @Nonnull private Function<ProfileRequestContext,IdentifierGenerationStrategy> idGeneratorLookupStrategy;
 
+    /** 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;
+    
+    /** Predicate used to check if consent is enabled with a given {@link ProfileRequestContext}. */
+    @Nonnull private Predicate<ProfileRequestContext> consentEnabledPredicate;
+    
     /** Strategy used to create the subcontext to hold the token. */
     @Nonnull private Function<ProfileRequestContext,AccessTokenContext> accessTokenContextCreationStrategy;
+
+    /** Authorize Code / Refresh Token the access token is based on, if any. */
+    @Nullable private TokenClaimsSet tokenClaimsSet;
+
+    /** Authentication request in the case of such. */
+    @Nullable private AuthenticationRequest authenticationRequest;
+    
+    /** Subject context. */
+    @Nullable private SubjectContext subjectCtx;
     
     /** Use a JWT for the token. */
     private boolean jwtTokenType;
     
-    /** Access token context. */
-    @Nullable private AccessTokenContext accessTokenCtx;
-    
-    /** Attribute context. */
-    @Nullable private AttributeContext attributeCtx;
-
     /** The generator to use. */
     @Nullable private IdentifierGenerationStrategy idGenerator;
     
+    /** Access token context. */
+    @Nullable private AccessTokenContext accessTokenCtx;
+
     /** Constructor. */
     public BuildAccessToken() {
         accessTokenTypeLookupStrategy = new AccessTokenTypeLookupFunction();
@@ -127,6 +156,15 @@ public class BuildAccessToken extends AbstractOIDCResponseAction {
         
         idGeneratorLookupStrategy = FunctionSupport.constant(new SecureRandomIdentifierGenerationStrategy());
         
+        tokenClaimsContextLookupStrategy =
+                new ChildContextLookup<>(OIDCAuthenticationResponseTokenClaimsContext.class).compose(
+                        new OIDCAuthenticationResponseContextLookupFunction());
+        consentContextLookupStrategy =
+                new ChildContextLookup<>(OIDCAuthenticationResponseConsentContext.class).compose(
+                        new OIDCAuthenticationResponseContextLookupFunction());
+
+        consentEnabledPredicate = new AttributeConsentFlowEnabledPredicate();
+        
         // PRC -> inbound message context -> OIDC response context -> ATC
         accessTokenContextCreationStrategy = new ChildContextLookup<>(AccessTokenContext.class, true).compose(
                 new ChildContextLookup<>(OIDCAuthenticationResponseContext.class).compose(
@@ -182,38 +220,76 @@ public class BuildAccessToken extends AbstractOIDCResponseAction {
     }
     
     /**
-     * Set the strategy used to create the {@link AccessTokenContext} to use.
+     * Set the strategy used to locate the issuer value to use.
      * 
-     * @param strategy creation strategy
+     * @param strategy lookup strategy
      */
-    public void setAccessTokenContextCreationStrategy(
-            @Nonnull final Function<ProfileRequestContext,AccessTokenContext> strategy) {
+    public void setIssuerLookupStrategy(@Nonnull final Function<ProfileRequestContext,String> strategy) {
         ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
         
-        accessTokenContextCreationStrategy =
-                Constraint.isNotNull(strategy, "AccessTokenContext creation strategy cannot be null");
+        issuerLookupStrategy = Constraint.isNotNull(strategy, "Issuer lookup strategy cannot be null");
     }
     
     /**
-     * Set the strategy used to locate the issuer value to use.
+     * Set the strategy used to locate the original {@link ClientID} from the request.
      * 
      * @param strategy lookup strategy
      */
-    public void setIssuerLookupStrategy(@Nonnull final Function<ProfileRequestContext,String> strategy) {
+    public void setClientIDLookupStrategy(@Nonnull final Function<ProfileRequestContext,ClientID> strategy) {
         ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
         
-        issuerLookupStrategy = Constraint.isNotNull(strategy, "Issuer lookup strategy cannot be null");
+        clientIDLookupStrategy = Constraint.isNotNull(strategy, "ClientID lookup strategy cannot be null");
     }
     
     /**
-     * Set the strategy used to locate the original {@link ClientID} from the request.
+     * Set the strategy used to locate the {@link OIDCAuthenticationResponseTokenClaimsContext} associated with a given
+     * {@link ProfileRequestContext}.
      * 
      * @param strategy lookup strategy
      */
-    public void setClientIDLookupStrategy(@Nonnull final Function<ProfileRequestContext,ClientID> 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 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");
+    }
+    
+    /**
+     * Set the strategy used to create the {@link AccessTokenContext} to use.
+     * 
+     * @param strategy creation strategy
+     */
+    public void setAccessTokenContextCreationStrategy(
+            @Nonnull final Function<ProfileRequestContext,AccessTokenContext> strategy) {
         ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
         
-        clientIDLookupStrategy = Constraint.isNotNull(strategy, "ClientID lookup strategy cannot be null");
+        accessTokenContextCreationStrategy =
+                Constraint.isNotNull(strategy, "AccessTokenContext creation strategy cannot be null");
     }
 
     /** {@inheritDoc} */
@@ -232,13 +308,42 @@ public class BuildAccessToken extends AbstractOIDCResponseAction {
             return false;
         }
         
-        idGenerator = idGeneratorLookupStrategy.apply(profileRequestContext);
-        if (idGenerator == null) {
-            log.error("{} No identifier generation strategy", getLogPrefix());
+        tokenClaimsSet = getOidcResponseContext().getAuthorizationGrantClaimsSet();
+        if (tokenClaimsSet != null && !(tokenClaimsSet instanceof RefreshTokenClaimsSet)
+                && !(tokenClaimsSet instanceof AuthorizeCodeClaimsSet)) {
+            log.error("{} Authorization grant is of unknown type: {}", getLogPrefix(), tokenClaimsSet.getClass().getName());
             ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
             return false;
         }
-
+        
+        if (tokenClaimsSet == null) {
+            /*
+             * Typically this path applies when the client_credentials grant is used.
+             * 
+             * Alternatively the access token may be provided by the authz endpoint without a code.
+             * This is the case only with the "token id_token" response type.
+             */
+            subjectCtx = profileRequestContext.getSubcontext(SubjectContext.class);
+            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() instanceof AuthenticationRequest) {
+                authenticationRequest =
+                        (AuthenticationRequest) profileRequestContext.getInboundMessageContext().getMessage();
+            }
+        }
+        
         accessTokenCtx = accessTokenContextCreationStrategy.apply(profileRequestContext);
         if (accessTokenCtx == null) {
             log.error("{} Unable to create AccessTokenContext", getLogPrefix());
@@ -268,27 +373,77 @@ public class BuildAccessToken extends AbstractOIDCResponseAction {
             return;
         }
         
+        ClaimsSet claims = null;
+        ClaimsSet claimsUI = null;
+        final OIDCAuthenticationResponseTokenClaimsContext tokenClaimsCtx =
+                tokenClaimsContextLookupStrategy.apply(profileRequestContext);
+        if (tokenClaimsCtx != null) {
+            claims = tokenClaimsCtx.getClaims();
+            claimsUI = tokenClaimsCtx.getUserinfoClaims();
+        }
+        
         final OIDCAuthenticationResponseContext responseCtx = getOidcResponseContext();
 
         final Scope scope = responseCtx.getScope() != null ? responseCtx.getScope() : new Scope();
+        log.debug("{} Building access token with scope: {}", getLogPrefix(), scope);
+        
+        final boolean oidc = scope.contains("openid");
         
+        if (oidc) {
+            responseCtx.getAudience().add(issuer);
+        }
         log.debug("{} Building access token with audience: {}", getLogPrefix(), responseCtx.getAudience());
-        log.debug("{} Building access token with scope: {}", getLogPrefix(), scope);
 
         final Instant now = Instant.now();
         final Instant dateExp = now.plus(accessTokenCtx.getLifetime());
         
-        final AccessTokenClaimsSet.Builder builder = (Builder) new AccessTokenClaimsSet.Builder()
-                .setJWTID(idGenerator)
-                .setClientID(clientID)
-                .setIssuer(issuer)
-                .setSubject(responseCtx.getSubject())
-                .setIssuedAt(now)
-                .setExpiresAt(dateExp)
-                .setACR(responseCtx.getAcr())
-                .setAuthenticationTime(responseCtx.getAuthTime())
-                .setScope(scope)
-                .setAudience(responseCtx.getAudience());
+        final AccessTokenClaimsSet.Builder builder;
+        
+        // TODO: some of these custom claims need to be sealed if JWT format is used.
+        
+        if (tokenClaimsSet != null) {
+            // We may not use original claims as input for scope / delivery claims as they may have been reduced.
+            builder = new AccessTokenClaimsSet.Builder(
+                    tokenClaimsSet,
+                    scope,
+                    oidc ? claims : null,
+                    oidc ? claimsUI : null,
+                    Instant.now(),
+                    dateExp);
+            // Add additional bits.
+            builder.setAudience(responseCtx.getAudience());
+        } else {
+            final OIDCAuthenticationResponseConsentContext consentCtx =
+                    consentContextLookupStrategy.apply(profileRequestContext);
+            final JSONArray consented = consentCtx != null ? consentCtx.getConsentedAttributes() : null;
+            
+            builder = (Builder) new AccessTokenClaimsSet.Builder()
+                    .setJWTID(idGenerator)
+                    .setClientID(clientID)
+                    .setIssuer(issuer)
+                    .setPrincipal(subjectCtx.getPrincipalName())
+                    .setSubject(responseCtx.getSubject())
+                    .setIssuedAt(now)
+                    .setExpiresAt(dateExp)
+                    .setACR(responseCtx.getAcr())
+                    .setAuthenticationTime(responseCtx.getAuthTime())
+                    .setScope(scope)
+                    .setAudience(responseCtx.getAudience())
+                    .setDlClaims(claims)
+                    .setDlClaimsUI(claimsUI)
+                    .setConsentedClaims(consented)
+                    .setConsentEnabled(consentEnabledPredicate.test(profileRequestContext));
+
+            if (authenticationRequest != null) {
+                builder
+                    .setNonce(authenticationRequest.getNonce())
+                    .setClaimsRequest(authenticationRequest.getOIDCClaims());
+            }
+
+            if (responseCtx.getRedirectURI() != null) {
+                builder.setRedirectURI(responseCtx.getRedirectURI());
+            }
+}
         
         if (jwtTokenType && responseCtx.getAccessTokenClaimSet() != null) {
             builder.setCustomClaims(responseCtx.getAccessTokenClaimSet().toJSONObject());
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateAudience.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateAudience.java
index 0b926b34..9818fccd 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateAudience.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/ValidateAudience.java
@@ -21,6 +21,7 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.function.Function;
+import java.util.function.Predicate;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
@@ -40,6 +41,7 @@ import net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.ClientInfoAudi
 import net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.DefaultOIDCMetadataContextLookupFunction;
 import net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.TokenRequestAudienceLookupFunction;
 import net.shibboleth.idp.plugin.oidc.op.profile.impl.AbstractOIDCAuthenticationResponseAction;
+import net.shibboleth.idp.plugin.oidc.op.profile.logic.IssueIDTokenCondition;
 import net.shibboleth.idp.profile.context.navigate.RelyingPartyIdLookupFunction;
 import net.shibboleth.oidc.profile.core.OidcEventIds;
 import net.shibboleth.utilities.java.support.component.ComponentSupport;
@@ -53,8 +55,9 @@ import net.shibboleth.utilities.java.support.logic.Constraint;
  * <p>This is an ordered list, so the first allowed value determines the "primary" audience of
  * the eventual token.</p>
  * 
- * <p>Requesting values is optional, in the absence of which at least one allowed value must exist
- * and be returned, or a failure event will be signaled.</p>
+ * <p>Requesting values is optional. If the OP is an implied audience, then no other audience will
+ * be established, but if not then at least one audience must be permitted and the first permitted
+ * value will be assumed.</p>
  * 
  * @event {@link EventIds#PROCEED_EVENT_ID}
  * @event {@link EventIds#INVALID_PROFILE_CTX}
@@ -77,6 +80,9 @@ public class ValidateAudience extends AbstractOIDCAuthenticationResponseAction {
     /** Strategy used for locating/creating the proxy context. */
     @Nonnull private Function<ProfileRequestContext,ProxiedRequesterContext> proxiedRequesterContextCreationStrategy;
     
+    /** Whether the request includes the OP as an audience. */
+    @Nonnull private Predicate<ProfileRequestContext> selfAudienceCondition;
+    
     /** Strategy used to locate the {@link OIDCAuthenticationResponseTokenClaimsContext}. */
     @Nonnull
     private Function<ProfileRequestContext,OIDCAuthenticationResponseTokenClaimsContext>
@@ -90,6 +96,8 @@ public class ValidateAudience extends AbstractOIDCAuthenticationResponseAction {
                 new DefaultOIDCMetadataContextLookupFunction());
         proxiedRequesterContextCreationStrategy = new ChildContextLookup<>(ProxiedRequesterContext.class, true).compose(
                 new OutboundMessageContextLookup());
+        // openid scope -> we're issuing an ID token -> the OP will be an audience for the access token
+        selfAudienceCondition = new IssueIDTokenCondition();
     }
 
     /**
@@ -126,7 +134,7 @@ public class ValidateAudience extends AbstractOIDCAuthenticationResponseAction {
         ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
         
         allowedAudienceLookupStrategy = Constraint.isNotNull(strategy,
-                "Allowed scope lookyp strategy cannot be null");
+                "Allowed scope lookup strategy cannot be null");
     }
     
     /**
@@ -142,18 +150,36 @@ public class ValidateAudience extends AbstractOIDCAuthenticationResponseAction {
                 "ProxiedRequesterContext lookup strategy cannot be null");
     }
     
+    /**
+     * Set whether the OP is an implied audience for the token request.
+     * 
+     * @param condition condition to set
+     * 
+     * @since 3.2.0
+     */
+    public void setSelfAudienceCondition(@Nonnull final Predicate<ProfileRequestContext> condition) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        
+        selfAudienceCondition = Constraint.isNotNull(condition, "Self audience condition cannot be null");
+    }
+    
 // Checkstyle: CyclomaticComplexity OFF
     /** {@inheritDoc} */
     @Override
     protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
         
         final String clientId = relyingPartyIdLookupStrategy.apply(profileRequestContext);
+        final boolean allowNone = selfAudienceCondition.test(profileRequestContext);
         
         // These may come from metadata or be supplemented or substituted from elsewhere.
         final List<String> allowedAudience = allowedAudienceLookupStrategy.apply(profileRequestContext);
         if (allowedAudience == null || allowedAudience.isEmpty()) {
-            log.warn("{} No allowed audience for client {}", getLogPrefix(), clientId);
-            ActionSupport.buildEvent(profileRequestContext, OidcEventIds.INVALID_TARGET);
+            if (allowNone) {
+                log.debug("{} No allowed audience for client {}, OP will be sole audience", getLogPrefix(), clientId);
+            } else {
+                log.warn("{} No allowed audience for client {}", getLogPrefix(), clientId);
+                ActionSupport.buildEvent(profileRequestContext, OidcEventIds.INVALID_TARGET);
+            }
             return;
         }
         
@@ -175,7 +201,11 @@ public class ValidateAudience extends AbstractOIDCAuthenticationResponseAction {
         }
         
         if (requestedAudience == null) {
-            // Nothing requested or previously granted, so take the first allowed value. 
+            // Nothing requested or previously granted.
+            if (allowNone) {
+                log.debug("{} No audience in request for client {}, OP will be sole audience", getLogPrefix(), clientId);
+                return;
+            }
             log.debug("{} No audience in request for client {}, using first allowed", getLogPrefix(), clientId);
             requestedAudience = Collections.singletonList(allowedAudience.get(0));
         }
@@ -195,8 +225,12 @@ public class ValidateAudience extends AbstractOIDCAuthenticationResponseAction {
         }
         
         if (effectiveAudience.isEmpty()) {
-            log.warn("{} No allowed audience for client {}", getLogPrefix(), clientId);
-            ActionSupport.buildEvent(profileRequestContext, OidcEventIds.INVALID_TARGET);
+            if (allowNone) {
+                log.debug("{} No allowed audience for client {}, OP will be sole audience", getLogPrefix(), clientId);
+            } else {
+                log.warn("{} No allowed audience for client {}", getLogPrefix(), clientId);
+                ActionSupport.buildEvent(profileRequestContext, OidcEventIds.INVALID_TARGET);
+            }
             return;
         }
         
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/AddAccessTokenHashToIDToken.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/AddAccessTokenHashToIDToken.java
index 30602d48..fd3a2d39 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/AddAccessTokenHashToIDToken.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/AddAccessTokenHashToIDToken.java
@@ -52,7 +52,7 @@ public class AddAccessTokenHashToIDToken extends AbstractOIDCSigningResponseActi
             return;
         }
         final AccessTokenHash atHash = AccessTokenHash.compute(getOidcResponseContext().getAccessToken(),
-                new JWSAlgorithm(getSignatureSigningParameters().getSignatureAlgorithm()));
+                new JWSAlgorithm(getSignatureSigningParameters().getSignatureAlgorithm()), null);
         if (atHash == null || atHash.getValue() == null) {
             log.error("{} Not able to generate at_hash using algorithm {}", getLogPrefix(),
                     getSignatureSigningParameters().getSignatureAlgorithm());
@@ -63,7 +63,6 @@ public class AddAccessTokenHashToIDToken extends AbstractOIDCSigningResponseActi
         getOidcResponseContext().getIDToken().setClaim(IDTokenClaimsSet.AT_HASH_CLAIM_NAME, atHash.getValue());
         log.debug("{} Updated token {}", getLogPrefix(),
                 getOidcResponseContext().getIDToken().toJSONObject().toJSONString());
-
     }
 
 }
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/AddAttributesToClaimsSet.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/AddAttributesToClaimsSet.java
index 2c92c468..189eaf4a 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/AddAttributesToClaimsSet.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/AddAttributesToClaimsSet.java
@@ -119,7 +119,6 @@ public class AddAttributesToClaimsSet extends AbstractOIDCResponseAction {
     /** List of claim names that will not be added. */
     @Nullable @NonnullElements private List<String> reservedClaimNames;
 
-
     /** Attributes to include in ID token no matter what. */
     @Nullable @NonnullElements private Set<String> alwaysIncludedAttributes;
 
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetTokenDeliveryAttributesFromTokenToResponseContext.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetTokenDeliveryAttributesFromTokenToResponseContext.java
index 048afbc8..7ff1d0c4 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetTokenDeliveryAttributesFromTokenToResponseContext.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetTokenDeliveryAttributesFromTokenToResponseContext.java
@@ -39,7 +39,7 @@ import com.nimbusds.openid.connect.sdk.claims.ClaimsSet;
 /**
  * Action that locates any token delivery claims from authorization code / access token. For located claims
  * {@link OIDCAuthenticationResponseTokenClaimsContext} is created under {@link OIDCAuthenticationResponseContext} and
- * the claims are placed there. Token and user info end points use the context for forming response.
+ * the claims are placed there. Token and user info endpoints use the context for forming response.
  **/
 public class SetTokenDeliveryAttributesFromTokenToResponseContext extends AbstractOIDCResponseAction {
 
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateGrant.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateGrant.java
index 6a8920e4..4596acdd 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateGrant.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateGrant.java
@@ -198,14 +198,14 @@ public class ValidateGrant extends AbstractOIDCTokenResponseAction {
                         log.error("{} Replay detected of authz code {}", getLogPrefix(), authzCodeClaimsSet.getID());
                         if (!revocationCache.revoke(RevocationCacheContexts.AUTHORIZATION_CODE,
                                 authzCodeClaimsSet.getID())) {
-                            log.warn("{} Fatal error, unable to set entry to revocation cache", getLogPrefix());
+                            log.warn("{} Fatal error, unable to save replayed code to revocation cache", getLogPrefix());
                         }
                         ActionSupport.buildEvent(profileRequestContext, OidcEventIds.INVALID_GRANT);
                         return;
                     }
                     tokenClaimsSet = authzCodeClaimsSet;
                 } catch (final DataSealerException | ParseException e) {
-                    log.warn("{} Obtaining authz code failed {}", getLogPrefix(), e.getMessage());
+                    log.warn("{} Unwrapping authz code failed: {}", getLogPrefix(), e.getMessage());
                     ActionSupport.buildEvent(profileRequestContext, OidcEventIds.INVALID_GRANT);
                     return;
                 }
@@ -231,7 +231,7 @@ public class ValidateGrant extends AbstractOIDCTokenResponseAction {
                     }
                     tokenClaimsSet = refreshTokenClaimsSet;
                 } catch (final ParseException | DataSealerException e) {
-                    log.warn("{} Obtaining refresh token failed {}", getLogPrefix(), e.getMessage());
+                    log.warn("{} Unwrapping refresh token failed {}", getLogPrefix(), e.getMessage());
                     ActionSupport.buildEvent(profileRequestContext, OidcEventIds.INVALID_GRANT);
                     return;
                 }
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateRedirectURI.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateRedirectURI.java
index 7e7029ff..36d9c1fc 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateRedirectURI.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateRedirectURI.java
@@ -35,7 +35,7 @@ import net.shibboleth.utilities.java.support.component.ComponentSupport;
 import net.shibboleth.utilities.java.support.logic.Constraint;
 
 /**
- * Action that validates redirect uri is a expected one. Validated redirect uri is stored to response context.
+ * Action that validates redirect uri is expected. Validated redirect uri is stored to response context.
  */
 public class ValidateRedirectURI extends AbstractOIDCAuthenticationResponseAction {
 
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateScope.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateScope.java
index ae30bf7a..f2b03e8a 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateScope.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateScope.java
@@ -49,7 +49,7 @@ import net.shibboleth.utilities.java.support.logic.Constraint;
  * <p>Requested scopes come from the inbound message and are possible but optional for both
  * authorization and token requests. They come from lookup functions aware of each message type.</p>
  * 
- * <p>Previously granted scopes are stored in the response context's slow for previous authorization
+ * <p>Previously granted scopes are stored in the response context's slot for previous authorization
  * grant claims. In the case where no scopes are explicitly requested, we still filter the previous
  * grants against the metadata.</p>
  * 
@@ -188,7 +188,7 @@ public class ValidateScope extends AbstractOIDCAuthenticationResponseAction {
         }
         
         if (requestedScopes.contains(OIDCScopeValue.OFFLINE_ACCESS)) {
-            // DefaultRequestResponseTypeLookupFunction returns response type only in authentication end point.
+            // DefaultRequestResponseTypeLookupFunction returns response type only on authorization end point.
             // It is enough to remove offline_scope in this first validation turn.
             final ResponseType responseType =
                     new DefaultRequestResponseTypeLookupFunction().apply(profileRequestContext);
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/logic/AttributeConsentEnabledInTokenClaimsSetPredicate.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/logic/AttributeConsentEnabledInTokenClaimsSetPredicate.java
index 9b8172b6..a58cc9cf 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/logic/AttributeConsentEnabledInTokenClaimsSetPredicate.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/logic/AttributeConsentEnabledInTokenClaimsSetPredicate.java
@@ -39,7 +39,7 @@ public class AttributeConsentEnabledInTokenClaimsSetPredicate extends AbstractRe
         final MessageContext outboundMessageCtx = input.getOutboundMessageContext();
         if (outboundMessageCtx != null) {
             final OIDCAuthenticationResponseContext oidcResponseContext = 
-                    outboundMessageCtx.getSubcontext(OIDCAuthenticationResponseContext.class, false);
+                    outboundMessageCtx.getSubcontext(OIDCAuthenticationResponseContext.class);
             if (oidcResponseContext != null && oidcResponseContext.getAuthorizationGrantClaimsSet() != null) {
                 return oidcResponseContext.getAuthorizationGrantClaimsSet().isConsentEnabled();
             }
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/userinfo/profile/impl/ValidateAccessToken.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/userinfo/profile/impl/ValidateAccessToken.java
index c0e2fe9f..6802c05b 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/userinfo/profile/impl/ValidateAccessToken.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/userinfo/profile/impl/ValidateAccessToken.java
@@ -105,6 +105,7 @@ public class ValidateAccessToken extends AbstractOIDCUserInfoValidationResponseA
             return;
         }
         
+        // TODO: needs to change to accomodate possibility of dual-use access tokens.
         if (!accessTokenClaimsSet.getAudience().isEmpty()) {
             log.warn("{} Access token was not issued for use by this OP");
             ActionSupport.buildEvent(profileRequestContext, OidcEventIds.INVALID_GRANT);
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-beans.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-beans.xml
index 2ce9c598..2c50d3e8 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-beans.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/token/token-beans.xml
@@ -162,14 +162,14 @@
         p:requestedScopeLookupStrategy-ref="TokenRequestScopeLookupStrategy"
         p:allowedScopeLookupStrategy="#{getObject('shibboleth.oidc.AllowedScopeStrategy') ?: getObject('shibboleth.oidc.DefaultAllowedScopeStrategy')}" />
 
-    <bean id="TokenRequestScopeLookupStrategy"
-        class="net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.TokenRequestScopeLookupFunction" />
-
     <bean id="ValidateAudience"
         class="net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl.ValidateAudience" scope="prototype"
         p:requestedAudienceLookupStrategy-ref="TokenRequestAudienceLookupStrategy"
         p:allowedAudienceLookupStrategy="#{getObject('shibboleth.oidc.AllowedAudienceStrategy') ?: getObject('shibboleth.oidc.DefaultAllowedAudienceStrategy')}" />
 
+    <bean id="TokenRequestScopeLookupStrategy"
+        class="net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.TokenRequestScopeLookupFunction" />
+
     <bean id="TokenRequestAudienceLookupStrategy"
         class="net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.TokenRequestAudienceLookupFunction" />
 
@@ -315,15 +315,63 @@
 
     <!-- Back to token prep. -->
     
-    <!-- Traditional third-party grant response handling. -->
+    <!-- OIDC response handling for access/refresh tokens. -->
+
+    <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="BuildOIDCAccessToken"
+        class="net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl.BuildAccessToken" scope="prototype"
+        p:dataSealer="#{getObject('%{idp.oidc.tokenSealer:shibboleth.oidc.TokenSealer}'.trim())}" />
+
+    <bean id="SignOIDCAccessToken"
+            class="net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl.SignAccessToken" scope="prototype"
+            p:securityParametersLookupStrategy-ref="AudienceSecurityParametersCreationStrategy"
+            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="SetRefreshTokenToResponseContext"
+            class="net.shibboleth.idp.plugin.oidc.op.profile.impl.SetRefreshTokenToResponseContext" scope="prototype"
+            c:sealer-ref="#{'%{idp.oidc.tokenSealer:shibboleth.oidc.TokenSealer}'.trim()}">
+        <property name="activationCondition">
+            <bean parent="shibboleth.Conditions.AND">
+                <constructor-arg>
+                    <list>
+                        <ref bean="IssueIDTokenCondition" />
+                        <bean class="net.shibboleth.oidc.profile.config.logic.RefreshTokensEnabledPredicate" />
+                    </list>
+                </constructor-arg>
+            </bean>
+        </property>
+    </bean>
+
+    <!-- ID token actions. -->
 
     <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"
-            p:activationCondition-ref="IssueIDTokenCondition">
+            p:signatureSigningParametersResolver-ref="shibboleth.oidc.TokenSignatureSigningParametersResolver">
         <property name="securityParametersContextLookupStrategy">
             <bean parent="shibboleth.Functions.Compose"
                 c:g-ref="shibboleth.ChildLookupOrCreate.SecurityParameters"
@@ -332,63 +380,47 @@
         <property name="existingParametersContextLookupStrategy">
             <bean parent="shibboleth.Functions.Compose"
                 c:g-ref="shibboleth.ChildLookup.SecurityParameters"
-                c:f-ref="shibboleth.MessageContextLookup.Outbound" />
+                c:f-ref="shibboleth.ChildLookup.RelyingParty" />
         </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"
-        p:activationCondition-ref="IssueIDTokenCondition" />
+        p:encryptionParametersResolver-ref="shibboleth.oidc.EncryptionParametersResolver" />
 
     <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}" />
 
-    <bean id="SetOIDCAccessTokenToResponseContext"
-        class="net.shibboleth.idp.plugin.oidc.op.profile.impl.SetAccessTokenToResponseContext" scope="prototype"
-        p:dataSealer-ref="#{'%{idp.oidc.tokenSealer:shibboleth.oidc.TokenSealer}'.trim()}" />
-
-    <bean id="SetRefreshTokenToResponseContext"
-        class="net.shibboleth.idp.plugin.oidc.op.profile.impl.SetRefreshTokenToResponseContext" scope="prototype"
-        c:sealer-ref="#{'%{idp.oidc.tokenSealer:shibboleth.oidc.TokenSealer}'.trim()}">
-        <property name="activationCondition">
-            <bean class="net.shibboleth.oidc.profile.config.logic.RefreshTokensEnabledPredicate" />
-        </property>
-    </bean>
-
-    <bean id="AddIDTokenShell" class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddIDTokenShell"
-        scope="prototype" p:activationCondition-ref="IssueIDTokenCondition" />
+    <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')}"
-        p:activationCondition-ref="IssueIDTokenCondition" />
+        p:reservedClaimNames="#{getObject('shibboleth.oidc.IDTokenReservedClaimNames') ?: getObject('shibboleth.oidc.DefaultIDTokenReservedClaimNames')}" />
 
     <bean id="AddTokenDeliveryAttributesToIDToken"
         class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddTokenDeliveryAttributesToClaimsSet" scope="prototype"
-        p:targetIDToken="true" p:activationCondition-ref="IssueIDTokenCondition" />
+        p:targetIDToken="true" />
 
-    <bean id="AddAuthTimeToIDToken" class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddAuthTimeToIDToken"
-        scope="prototype" p:activationCondition-ref="IssueIDTokenCondition" />
+    <bean id="AddAuthTimeToIDToken"
+        class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddAuthTimeToIDToken" scope="prototype" />
 
-    <bean id="AddAcrToIDToken" class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddAcrToIDToken"
-        scope="prototype" p:activationCondition-ref="IssueIDTokenCondition" />
+    <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" p:requestNonceLookupStrategy-ref="shibboleth.TokenRequestNonceLookupStrategy"
-        p:activationCondition-ref="IssueIDTokenCondition" />
+    <bean id="AddNonceToIDToken"
+        class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddNonceToIDToken" scope="prototype"
+        p:requestNonceLookupStrategy-ref="shibboleth.TokenRequestNonceLookupStrategy" />
 
     <bean id="shibboleth.TokenRequestNonceLookupStrategy"
-        class="net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.TokenRequestNonceLookupFunction"
-        scope="prototype" />
+        class="net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.TokenRequestNonceLookupFunction" />
 
     <bean id="AddAccessTokenHashToIDToken"
-            class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddAccessTokenHashToIDToken" scope="prototype"
-            p:activationCondition-ref="IssueIDTokenCondition">
+            class="net.shibboleth.idp.plugin.oidc.op.profile.impl.AddAccessTokenHashToIDToken" scope="prototype">
         <property name="securityParametersLookupStrategy">
             <bean parent="shibboleth.Functions.Compose"
                 c:g-ref="shibboleth.ChildLookup.SecurityParameters"
@@ -396,8 +428,7 @@
         </property>
     </bean>
 
-    <bean id="SignIDToken" class="net.shibboleth.idp.plugin.oidc.op.profile.impl.SignIDToken" scope="prototype"
-        p:activationCondition-ref="IssueIDTokenCondition">
+    <bean id="SignIDToken" class="net.shibboleth.idp.plugin.oidc.op.profile.impl.SignIDToken" scope="prototype">
         <property name="securityParametersLookupStrategy">
             <bean parent="shibboleth.Functions.Compose"
                 c:g-ref="shibboleth.ChildLookup.SecurityParameters"
@@ -406,7 +437,7 @@
     </bean>
 
     <bean id="EncryptIDToken" class="net.shibboleth.idp.plugin.oidc.op.profile.impl.EncryptProcessedToken"
-        p:activationCondition-ref="IssueIDTokenCondition" scope="prototype" />
+        scope="prototype" />
 
     <bean id="FormOutboundMessage"
         class="net.shibboleth.idp.plugin.oidc.op.profile.impl.FormOutboundTokenResponseMessage" scope="prototype" />
@@ -420,33 +451,31 @@
         </property>
     </bean>
 
-    <!-- client_credentials grant response actions. -->
+    <!-- Third-party token actions. -->
 
     <bean id="PopulateAccessTokenSignatureSigningParameters"
-            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">
-        <property name="existingParametersContextLookupStrategy">
-            <bean parent="shibboleth.Functions.Compose"
-                c:g-ref="shibboleth.ChildLookup.SecurityParameters"
-                c:f-ref="shibboleth.MessageContextLookup.Outbound" />
-        </property>
-    </bean>
+        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="PopulateAccessTokenEncryptionParameters"
-        class="net.shibboleth.idp.plugin.oidc.op.profile.impl.PopulateOIDCEncryptionParameters" scope="prototype"
-        p:encryptionOptionalPredicate-ref="AudienceEncryptionOptionalPredicate"
-        p:oidcMetadataContextLookupStrategy-ref="LookupOutboundOIDCMetadataContext"
-        p:configurationLookupStrategy-ref="shibboleth.EncryptionConfigurationLookup"
-        p:encryptionParametersResolver-ref="shibboleth.oidc.EncryptionParametersResolver"
-        p:encryptionContextLookupStrategy-ref="AudienceEncryptionContextCreationStrategy" />
+            class="net.shibboleth.idp.plugin.oidc.op.profile.impl.PopulateOIDCEncryptionParameters" scope="prototype"
+            p:encryptionOptionalPredicate-ref="AudienceEncryptionOptionalPredicate"
+            p:oidcMetadataContextLookupStrategy-ref="LookupOutboundOIDCMetadataContext"
+            p:configurationLookupStrategy-ref="shibboleth.EncryptionConfigurationLookup"
+            p:encryptionParametersResolver-ref="shibboleth.oidc.EncryptionParametersResolver"
+            p:encryptionContextLookupStrategy-ref="AudienceEncryptionContextCreationStrategy">
+        <property name="activationCondition">
+            <bean parent="shibboleth.Conditions.NOT" c:_0-ref="IssueIDTokenCondition" />
+        </property>
+    </bean>
             
     <bean id="AudienceEncryptionOptionalPredicate"
         class="net.shibboleth.oidc.profile.config.logic.EncryptionOptionalPredicate"
@@ -487,13 +516,13 @@
         p:typeHeader="at+jwt" />
 
     <bean id="EncryptAccessToken"
-        class="net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl.EncryptAccessToken" scope="prototype"
-        p:encryptionContextLookupStrategy-ref="AudienceEncryptionContextCreationStrategy" />
+            class="net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl.EncryptAccessToken" scope="prototype"
+            p:encryptionContextLookupStrategy-ref="AudienceEncryptionContextCreationStrategy">
+        <property name="activationCondition">
+            <bean parent="shibboleth.Conditions.NOT" c:_0-ref="IssueIDTokenCondition" />
+        </property>
+    </bean>
     
-    <bean id="SetOAuthAccessTokenToResponseContext"
-        class="net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl.SetAccessTokenToResponseContext"
-        scope="prototype" />
-
     <bean id="PostResponsePopulateAuditContext" parent="shibboleth.AbstractPopulateAuditContext"
         p:fieldExtractors="#{getObject('shibboleth.oidc.TokenPostResponseAuditExtractors') ?: getObject('shibboleth.oidc.DefaultTokenPostResponseAuditExtractors')}" />
 
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 0f6afca5..eed418f9 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
@@ -60,11 +60,19 @@
         <evaluate expression="InitializeSubjectContext" />
         <evaluate expression="SetSubjectFromAuthzCodeToResponseContext" />
         <evaluate expression="ValidateScope" />
+        <evaluate expression="ValidateAudience" />
         <evaluate expression="'proceed'" />
         
-        <transition on="proceed" to="CheckAttributeResolutionForClient" />
+        <transition on="proceed" to="CheckTraditionalGrantForAudience" />
     </action-state>
 
+    <!-- For standard grants, audience may or may not be a factor. -->
+    <decision-state id="CheckTraditionalGrantForAudience">
+        <if test="opensamlProfileRequestContext.getOutboundMessageContext().getSubcontext(T(net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCAuthenticationResponseContext)).getAudience().isEmpty()"
+            then="CheckAttributeResolutionForClient"
+            else="LookupAudienceMetadata" />
+    </decision-state>
+
     <!-- These steps apply to grants that are self-contained on this endpoint. -->
     <action-state id="ClientCredentialsGrantProcessing">
         <evaluate expression="SetAuthenticationContextClassReferenceToResponseContext" />
@@ -77,7 +85,7 @@
         <transition on="proceed" to="LookupAudienceMetadata" />
     </action-state>
 
-    <!-- For client credentials grant, need to flip the Relying Party here to the primary resource/audience. -->
+    <!-- For client credentials grant, need to add a second Relying Party for the primary resource/audience. -->
 
     <action-state id="LookupAudienceMetadata">
         <evaluate expression="AudienceOIDCMetadataLookup" />
@@ -137,35 +145,10 @@
         <transition on="proceed" to="DoConsentLookup" />
     </action-state>
     
-    <decision-state id="BuildResponse">
-        <if test="NotClientCredentialsGrantCondition.test(opensamlProfileRequestContext)"
-            then="TraditionalGrantResponse"
-            else="ClientCredentialsGrantResponse" />
-    </decision-state>
-    
-    <action-state id="TraditionalGrantResponse">
-        <evaluate expression="PopulateIDTokenSignatureSigningParameters" />
-        <evaluate expression="PopulateIDTokenEncryptionParameters" />
-        <evaluate expression="SetOIDCAccessTokenToResponseContext" />
-        <evaluate expression="SetRefreshTokenToResponseContext" />
-        <evaluate expression="AddIDTokenShell" />
-        <evaluate expression="AddAttributeClaimsToIDToken" />
-        <evaluate expression="AddTokenDeliveryAttributesToIDToken" />
-        <evaluate expression="AddAuthTimeToIDToken" />
-        <evaluate expression="AddAcrToIDToken" />
-        <evaluate expression="AddNonceToIDToken" />
-        <evaluate expression="AddAccessTokenHashToIDToken" />
-        <evaluate expression="SignIDToken" />
-        <evaluate expression="EncryptIDToken" />
-        <evaluate expression="'proceed'" />
-        
-        <transition on="proceed" to="PopulateOutboundInterceptContext" />
-    </action-state>
-
     <decision-state id="CheckAttributeResolutionForAudience">
         <if test="ResolveAttributesForAudiencePredicate.test(opensamlProfileRequestContext)"
             then="AttributeResolutionForAudience"
-            else="ClientCredentialsGrantResponse" />
+            else="DoConsentLookup" />
     </decision-state>
 
     <action-state id="AttributeResolutionForAudience">
@@ -173,17 +156,77 @@
         <evaluate expression="FilterAttributesForAudience" />
         <evaluate expression="'proceed'" />
         
-        <transition on="proceed" to="ClientCredentialsGrantResponse" />
+        <transition on="proceed" to="DoConsentLookup" />
+    </action-state>
+    
+    <decision-state id="BuildResponse">
+        <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.
+    
+    Not 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="SetRefreshTokenToResponseContext" />
+        <evaluate expression="'proceed'" />
+        
+        <transition on="proceed" to="ProducingIDToken" />
     </action-state>
 
-    <action-state id="ClientCredentialsGrantResponse">
-        <evaluate expression="PopulateAccessTokenSignatureSigningParameters" />
-        <evaluate expression="PopulateAccessTokenEncryptionParameters" />
+    <!--
+    
+    This includes OIDC and OAuth use cases and all grant types but the primary audience
+    for the access token is the resource/audience. The OP MAY also be an audience for the
+    token if the "oidc" scope is in play, in which case the actions have to behave differently
+    or be disabled in some cases. Encryption is for example impossible in that case, as with
+    the OIDC-only use of JWTs above.
+    -->
+    <action-state id="BuildTokensForThirdPartyAccess">
+        <evaluate expression="PopulateThirdPartyAccessTokenSignatureSigningParameters" />
+        <evaluate expression="PopulateThirdPartyAccessTokenEncryptionParameters" />
         <evaluate expression="AddAttributeClaimsToAccessToken" />
         <evaluate expression="BuildAccessToken" />
         <evaluate expression="SignAccessToken" />
         <evaluate expression="EncryptAccessToken" />
         <evaluate expression="SetOAuthAccessTokenToResponseContext" />
+        <evaluate expression="SetRefreshTokenToResponseContext" />
+        <evaluate expression="'proceed'" />
+        
+        <transition on="proceed" to="ProducingIDToken" />
+    </action-state>
+
+    <!-- ID token only issued when scope includes "openid" -->
+
+    <decision-state id="ProducingIDToken">
+        <if test="IssueIDTokenCondition.test(opensamlProfileRequestContext)"
+            then="BuildIDToken"
+            else="PopulateOutboundInterceptContext" />
+    </decision-state>
+
+    <action-state id="BuildIDToken">
+        <evaluate expression="PopulateIDTokenSignatureSigningParameters" />
+        <evaluate expression="PopulateIDTokenEncryptionParameters" />
+        <evaluate expression="AddIDTokenShell" />
+        <evaluate expression="AddAttributeClaimsToIDToken" />
+        <evaluate expression="AddTokenDeliveryAttributesToIDToken" />
+        <evaluate expression="AddAuthTimeToIDToken" />
+        <evaluate expression="AddAcrToIDToken" />
+        <evaluate expression="AddNonceToIDToken" />
+        <evaluate expression="AddAccessTokenHashToIDToken" />
+        <evaluate expression="SignIDToken" />
+        <evaluate expression="EncryptIDToken" />
         <evaluate expression="'proceed'" />
         
         <transition on="proceed" to="PopulateOutboundInterceptContext" />
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/BuildAccessTokenTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/BuildAccessTokenTest.java
index d883801b..f94d9f1b 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/BuildAccessTokenTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/BuildAccessTokenTest.java
@@ -21,6 +21,7 @@ import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertTrue;
 
+import net.shibboleth.idp.authn.context.SubjectContext;
 import net.shibboleth.idp.plugin.oidc.op.messaging.context.AccessTokenContext;
 import net.shibboleth.idp.plugin.oidc.op.profile.impl.BaseOIDCResponseActionTest;
 import net.shibboleth.idp.plugin.oidc.op.token.support.AccessTokenClaimsSet;
@@ -57,12 +58,14 @@ import com.nimbusds.oauth2.sdk.id.ClientID;
 public class BuildAccessTokenTest extends BaseOIDCResponseActionTest {
 
     /** Action to test. */
-    private BuildAccessToken action;
+    @Nullable private BuildAccessToken action;
     
     @BeforeMethod
     protected void setUp() throws Exception {
         super.setUp();
         
+        profileRequestCtx.getSubcontext(SubjectContext.class, true).setPrincipalName("jdoe");
+        
         respCtx.setAuthTime(Instant.now());
         respCtx.setSubject(clientId);
         respCtx.setAcr("0");
@@ -179,6 +182,7 @@ public class BuildAccessTokenTest extends BaseOIDCResponseActionTest {
             assertTrue(at.getIssuedAt().isBefore(Instant.now()));
             assertEquals(at.getScope(), scope);
             assertEquals(at.getSubject(), clientId);
+            assertEquals(at.getPrincipal(), "jdoe");
         } else if (ctx.getJWT() != null) {
             final JWTClaimsSet claims = ctx.getJWT().getJWTClaimsSet();
             assertNotNull(claims);
@@ -191,6 +195,7 @@ public class BuildAccessTokenTest extends BaseOIDCResponseActionTest {
             assertTrue(claims.getIssueTime().toInstant().isBefore(Instant.now()));
             assertEquals(claims.getStringClaim(TokenClaimsSet.KEY_SCOPE), scope.toString());
             assertEquals(claims.getSubject(), clientId);
+            assertEquals(claims.getStringClaim(TokenClaimsSet.KEY_USER_PRINCIPAL), "jdoe");
         } else {
             throw new RuntimeException("No token found");
         }
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/TokenFlowTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/TokenFlowTest.java
index fd4ec46f..b6e84391 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/TokenFlowTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/TokenFlowTest.java
@@ -126,7 +126,12 @@ public class TokenFlowTest extends AbstractOidcClientAuthenticationFlowTest {
         assertErrorCode(result, OAuth2Error.INVALID_GRANT_CODE);
     }
 
-    @Test
+    /**
+     * TODO: This test "fails" now because it's honoring a non-OIDC request by assuming there has to be
+     * a requested and allowed audience/resource. The original success outcome was an anomaly due to the
+     * original grant handling not supporting the audience notion.
+     */
+    @Test(enabled=false)
     public void testNoScopes() throws Exception {
         setHttpFormRequest("POST",
                 createRequestParameters(redirectUri, "authorization_code",

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


More information about the commits mailing list