[java-idp-oidc] branch main updated: JOIDC-37 Refactor user consent logic

Henri Mikkonen henri.mikkonen at iki.fi
Sun Mar 7 13:33:52 UTC 2021


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

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

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

The following commit(s) were added to refs/heads/main by this push:
       new  cdb63d84  JOIDC-37 Refactor user consent logic
cdb63d84 is described below

commit cdb63d84b623acb7e16a1a07c48eefefe2d96962
Author: Henri Mikkonen <henri.mikkonen at iki.fi>
AuthorDate: Sun Mar 7 15:29:15 2021 +0200

    JOIDC-37 Refactor user consent logic
    
    https://issues.shibboleth.net/jira/browse/JOIDC-37
    
    - By default, exploit attribute-release-query instead of direct ctx access
    - Added property 'idp.oidc.encodeConsentInTokens' to enable storing consent inside tokens
      - Got rid of consentable claim -concept inside the tokens & authz code
    
    Initial testing done successfully, more still needed.
---
 .../OIDCAuthenticationResponseConsentContext.java  |  15 ---
 .../op/token/support/AccessTokenClaimsSet.java     |  14 +--
 .../op/token/support/AuthorizeCodeClaimsSet.java   |  14 +--
 .../op/token/support/RefreshTokenClaimsSet.java    |   4 +-
 .../oidc/op/token/support/TokenClaimsSet.java      |  68 +++++-----
 ...DCAuthenticationResponseConsentContextTest.java |   3 -
 .../BaseTokenRequestLookupFunctionTest.java        |   6 +-
 ...estConsentableAttributesLookupFunctionTest.java |  37 ------
 .../oidc/op/token/support/TokenClaimsSetTest.java  |  11 +-
 .../support/testing/BaseTokenClaimsSetTest.java    |   3 +-
 .../op/config/OIDCCoreProtocolConfiguration.java   |  34 +++++
 ...uteConsentEnabledInTokenClaimsSetPredicate.java |  49 ++++++++
 .../AttributeConsentFlowEnabledPredicate.java      |  29 ++---
 .../op/profile/impl/AddAttributesToClaimsSet.java  |   3 +-
 .../impl/SetAccessTokenToResponseContext.java      |  31 +++--
 .../SetAuthorizationCodeToResponseContext.java     |  32 +++--
 .../impl/SetConsentFromTokenToResponseContext.java |  22 +---
 .../profile/impl/SetConsentToResponseContext.java  | 139 ++++++++++++++-------
 .../oidc/consent-lookup/consent-lookup-beans.xml   |  21 ++++
 .../oidc/consent-lookup/consent-lookup-flow.xml    |  29 +++++
 .../shibboleth/idp/flows/oidc/token/token-flow.xml |   6 +-
 .../idp/flows/oidc/userinfo/token-flow.xml         |   6 +-
 .../idp/service/relying-party/postconfig.xml       |   9 ++
 .../idp/plugin/oidc/op/conf/oidc.properties        |   5 +-
 .../oidc/op/profile/flow/AbstractOidcFlowTest.java |  10 ++
 .../plugin/oidc/op/profile/flow/TokenFlowTest.java |  12 +-
 .../plugin/oidc/op/profile/flow/UserInfoTest.java  |   1 +
 .../profile/impl/AddAttributesToClaimsSetTest.java |   4 +-
 .../impl/SetAccessTokenToResponseContextTest.java  |   4 -
 .../SetAuthorizationCodeToResponseContextTest.java |   4 -
 .../SetConsentFromTokenToResponseContextTest.java  |   7 +-
 .../impl/SetConsentToResponseContextTest.java      |  86 ++++++-------
 .../impl/SetSubjectToResponseContextTest.java      |   3 -
 .../oidc/op/profile/impl/ValidateGrantTest.java    |  14 ++-
 .../src/test/resources/conf/idp.properties         |   1 +
 .../EntityDescriptor-with-oidcmd-clientsecret.xml  |  10 +-
 36 files changed, 445 insertions(+), 301 deletions(-)

diff --git a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/messaging/context/OIDCAuthenticationResponseConsentContext.java b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/messaging/context/OIDCAuthenticationResponseConsentContext.java
index e46bf667..dd74113f 100644
--- a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/messaging/context/OIDCAuthenticationResponseConsentContext.java
+++ b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/messaging/context/OIDCAuthenticationResponseConsentContext.java
@@ -33,16 +33,11 @@ public class OIDCAuthenticationResponseConsentContext extends BaseContext {
     @Nullable
     private JSONArray consentedAttributes;
 
-    /** Attributes requiring consent. */
-    @Nullable
-    private JSONArray consentableAttributes;
-
     /**
      * Constructor.
      */
     public OIDCAuthenticationResponseConsentContext() {
         consentedAttributes = new JSONArray();
-        consentableAttributes = new JSONArray();
     }
 
     /**
@@ -55,14 +50,4 @@ public class OIDCAuthenticationResponseConsentContext extends BaseContext {
         return consentedAttributes;
     }
 
-    /**
-     * Get consentable attributes.
-     * 
-     * @return consentable attributes.
-     */
-    @Nonnull
-    public JSONArray getConsentableAttributes() {
-        return consentableAttributes;
-    }
-
 }
\ No newline at end of file
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 2b9743ed..de4adee0 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
@@ -68,8 +68,8 @@ public final class AccessTokenClaimsSet extends TokenClaimsSet {
                 tokenClaimSet.getClaimsSet().getSubject(),
                 tokenClaimSet.getACR() == null ? null : new ACR(tokenClaimSet.getACR()), iat, exp,
                 tokenClaimSet.getNonce(), tokenClaimSet.getAuthenticationTime(), tokenClaimSet.getRedirectURI(), scope,
-                tokenClaimSet.getClaimsRequest(), dlClaims, null, dlClaimsUI, tokenClaimSet.getConsentableClaims(),
-                tokenClaimSet.getConsentedClaims(), null);
+                tokenClaimSet.getClaimsRequest(), dlClaims, null, dlClaimsUI, tokenClaimSet.getConsentedClaims(),
+                null, tokenClaimSet.isConsentEnabled());
     }
 
     /**
@@ -90,8 +90,8 @@ public final class AccessTokenClaimsSet extends TokenClaimsSet {
      * @param claims Claims request of the authentication request. May be NULL.
      * @param dlClaims token delivery claims delivered both for id token and userinfo response. May be NULL.
      * @param dlClaimsUI token delivery claims delivered for userinfo response. May be NULL.
-     * @param consentableClaims consentable claims. May be NULL.
      * @param consentedClaims consented claims. May be NULL.
+     * @param consentEnabled Whether consent has been enabled.
      * @throws RuntimeException if called with nonallowed null parameters
      */
     private AccessTokenClaimsSet(@Nonnull final IdentifierGenerationStrategy idGenerator,
@@ -100,10 +100,10 @@ public final class AccessTokenClaimsSet extends TokenClaimsSet {
             @Nonnull final Instant exp, @Nullable final Nonce nonce, @Nonnull final Instant authTime,
             @Nonnull final URI redirectURI, @Nonnull final Scope scope, @Nullable final OIDCClaimsRequest claims,
             @Nullable final ClaimsSet dlClaims, @Nullable final ClaimsSet dlClaimsUI,
-            @Nullable final List<Object> consentableClaims, @Nullable final List<Object> consentedClaims) {
+            @Nullable final List<Object> consentedClaims, final boolean consentEnabled) {
         super(VALUE_TYPE_AT, idGenerator.generateIdentifier(), clientID, issuer, userPrincipal, subject, acr, iat, exp,
-                nonce, authTime, redirectURI, scope, claims, dlClaims, null, dlClaimsUI, consentableClaims,
-                consentedClaims, null);
+                nonce, authTime, redirectURI, scope, claims, dlClaims, null, dlClaimsUI, consentedClaims, null,
+                consentEnabled);
     }
 
     /**
@@ -175,7 +175,7 @@ public final class AccessTokenClaimsSet extends TokenClaimsSet {
          */
         public AccessTokenClaimsSet build() {
             return new AccessTokenClaimsSet(idGen, rpId, iss, usrPrincipal, sub, acr, iat, exp, nonce, authTime,
-                    redirect, reqScope, claims, dlClaims, dlClaimsUI, cnsntlClaims, cnsntdClaims);
+                    redirect, reqScope, claims, dlClaims, dlClaimsUI, cnsntdClaims, cnsntEnabled);
         }
 
     }
diff --git a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/token/support/AuthorizeCodeClaimsSet.java b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/token/support/AuthorizeCodeClaimsSet.java
index 1a888742..f081a17a 100644
--- a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/token/support/AuthorizeCodeClaimsSet.java
+++ b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/token/support/AuthorizeCodeClaimsSet.java
@@ -61,9 +61,9 @@ public final class AuthorizeCodeClaimsSet extends TokenClaimsSet {
      * @param dlClaims Token delivery claims delivered both for id token and userinfo response. May be NULL.
      * @param dlClaimsID Token delivery claims delivered for id token. May be NULL.
      * @param dlClaimsUI Token delivery claims delivered for userinfo response. May be NULL.
-     * @param consentableClaims consentable claims. May be NULL.
      * @param consentedClaims consented claims. May be NULL.
      * @param codeChallenge Code Challenge. May be NULL.
+     * @param consentEnabled Whether consent has been enabled.
      * @throws RuntimeException if called with nonallowed null parameters
      */
     private AuthorizeCodeClaimsSet(@Nonnull final IdentifierGenerationStrategy idGenerator,
@@ -72,11 +72,11 @@ public final class AuthorizeCodeClaimsSet extends TokenClaimsSet {
             @Nonnull final Instant exp, @Nullable final Nonce nonce, @Nonnull final Instant authTime,
             @Nonnull final URI redirectURI, @Nonnull final Scope scope, @Nullable final OIDCClaimsRequest claims,
             @Nullable final ClaimsSet dlClaims, @Nullable final ClaimsSet dlClaimsID,
-            @Nullable final ClaimsSet dlClaimsUI, @Nullable final List<Object> consentableClaims,
-            @Nullable final List<Object> consentedClaims, @Nullable final String codeChallenge) {
+            @Nullable final ClaimsSet dlClaimsUI, @Nullable final List<Object> consentedClaims,
+            @Nullable final String codeChallenge, final boolean consentEnabled) {
         super(VALUE_TYPE_AC, idGenerator.generateIdentifier(), clientID, issuer, userPrincipal, subject, acr, iat, exp,
-                nonce, authTime, redirectURI, scope, claims, dlClaims, dlClaimsID, dlClaimsUI, consentableClaims,
-                consentedClaims, codeChallenge);
+                nonce, authTime, redirectURI, scope, claims, dlClaims, dlClaimsID, dlClaimsUI, consentedClaims,
+                codeChallenge, consentEnabled);
     }
 
     /**
@@ -148,8 +148,8 @@ public final class AuthorizeCodeClaimsSet extends TokenClaimsSet {
          */
         public AuthorizeCodeClaimsSet build() {
             return new AuthorizeCodeClaimsSet(idGen, rpId, iss, usrPrincipal, sub, acr, iat, exp, nonce, authTime,
-                    redirect, reqScope, claims, dlClaims, dlClaimsID, dlClaimsUI, cnsntlClaims, cnsntdClaims,
-                    codeChallenge);
+                    redirect, reqScope, claims, dlClaims, dlClaimsID, dlClaimsUI, cnsntdClaims,
+                    codeChallenge, cnsntEnabled);
         }
 
     }
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 26e4f191..8518241d 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
@@ -54,8 +54,8 @@ public final class RefreshTokenClaimsSet extends TokenClaimsSet {
                 tokenClaimsSet.getACR() == null ? null : new ACR(tokenClaimsSet.getACR()), iat, exp,
                 tokenClaimsSet.getNonce(), tokenClaimsSet.getAuthenticationTime(), tokenClaimsSet.getRedirectURI(),
                 tokenClaimsSet.getScope(), tokenClaimsSet.getClaimsRequest(), tokenClaimsSet.getDeliveryClaims(), null,
-                tokenClaimsSet.getUserinfoDeliveryClaims(), tokenClaimsSet.getConsentableClaims(),
-                tokenClaimsSet.getConsentedClaims(), null);
+                tokenClaimsSet.getUserinfoDeliveryClaims(), tokenClaimsSet.getConsentedClaims(), null,
+                tokenClaimsSet.isConsentEnabled());
     }
 
     /**
diff --git a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/token/support/TokenClaimsSet.java b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/token/support/TokenClaimsSet.java
index f04629f0..15e2345c 100644
--- a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/token/support/TokenClaimsSet.java
+++ b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/token/support/TokenClaimsSet.java
@@ -102,11 +102,11 @@ public class TokenClaimsSet {
     /** Claims set for token delivery, user info only. */
     public static final String KEY_DELIVERY_CLAIMS_USERINFO = "dl_claims_ui";
 
-    /** Claims/Attributes requiring consent. */
-    public static final String KEY_CONSENTABLE_CLAIMS = "cnsntbl_claims";
-
     /** Claims/Attributes having consent. */
     public static final String KEY_CONSENTED_CLAIMS = "cnsntd_claims";
+    
+    /** Whether consent has been enabled. */
+    public static final String KEY_CONSENT_ENABLED = "cnsnt";
 
     /** Code Challenge. */
     public static final String KEY_CODE_CHALLENGE = "cc";
@@ -144,9 +144,9 @@ public class TokenClaimsSet {
      * @param dlClaims token delivery claims delivered both for id token and userinfo response. May be NULL.
      * @param dlClaimsID token delivery claims delivered for id token. May be NULL.
      * @param dlClaimsUI token delivery claims delivered for userinfo response. May be NULL.
-     * @param consentableClaims consentable claims. May be NULL.
      * @param consentedClaims consented claims. May be NULL.
      * @param codeChallenge Code Challenge. May be NULL.
+     * @param consentEnabled Whether consent has been enabled.
      * @throws RuntimeException if called with not allowed null parameters
      */
     // Checkstyle: CyclomaticComplexity OFF
@@ -156,8 +156,8 @@ public class TokenClaimsSet {
             @Nullable final Nonce nonce, @Nonnull final Instant authTime, @Nonnull final URI redirectURI,
             @Nonnull final Scope scope, @Nullable final OIDCClaimsRequest claims, @Nullable final ClaimsSet dlClaims,
             @Nullable final ClaimsSet dlClaimsID, @Nullable final ClaimsSet dlClaimsUI,
-            @Nullable final List<Object> consentableClaims, @Nullable final List<Object> consentedClaims,
-            @Nullable final String codeChallenge) {
+            @Nullable final List<Object> consentedClaims, @Nullable final String codeChallenge,
+            final boolean consentEnabled) {
         if (tokenType == null || tokenID == null || clientID == null || issuer == null || userPrincipal == null
                 || iat == null || exp == null || authTime == null || redirectURI == null || scope == null
                 || subject == null) {
@@ -173,8 +173,8 @@ public class TokenClaimsSet {
                 .claim(KEY_DELIVERY_CLAIMS, dlClaims == null ? null : dlClaims.toJSONObject())
                 .claim(KEY_DELIVERY_CLAIMS_IDTOKEN, dlClaimsID == null ? null : dlClaimsID.toJSONObject())
                 .claim(KEY_DELIVERY_CLAIMS_USERINFO, dlClaimsUI == null ? null : dlClaimsUI.toJSONObject())
-                .claim(KEY_CONSENTABLE_CLAIMS, consentableClaims).claim(KEY_CONSENTED_CLAIMS, consentedClaims)
-                .claim(KEY_CODE_CHALLENGE, codeChallenge).build();
+                .claim(KEY_CONSENTED_CLAIMS, consentedClaims).claim(KEY_CODE_CHALLENGE, codeChallenge)
+                .claim(KEY_CONSENT_ENABLED, consentEnabled).build();
 
     }
 
@@ -228,14 +228,13 @@ public class TokenClaimsSet {
         if (tokenClaimsSet.getClaims().containsKey(KEY_ACR)) {
             tokenClaimsSet.getStringClaim(KEY_ACR);
         }
-        if (tokenClaimsSet.getClaims().containsKey(KEY_CONSENTABLE_CLAIMS)
-                && !(tokenClaimsSet.getClaim(KEY_CONSENTABLE_CLAIMS) instanceof List)) {
-            throw new ParseException("consentable claims is of wrong type", 0);
-        }
         if (tokenClaimsSet.getClaims().containsKey(KEY_CONSENTED_CLAIMS)
                 && !(tokenClaimsSet.getClaim(KEY_CONSENTED_CLAIMS) instanceof List)) {
             throw new ParseException("consented claims is of wrong type", 0);
         }
+        if (tokenClaimsSet.getClaims().containsKey(KEY_CONSENT_ENABLED)) {
+            tokenClaimsSet.getBooleanClaim(KEY_CONSENT_ENABLED);
+        }
         if (tokenClaimsSet.getClaims().containsKey(KEY_CLAIMS)) {
             tokenClaimsSet.getJSONObjectClaim(KEY_CLAIMS);
         }
@@ -463,21 +462,27 @@ public class TokenClaimsSet {
     }
 
     /**
-     * Get consentable claims.
+     * Get consented claims.
      * 
-     * @return consentable claims
+     * @return consented claims
      */
-    public List<Object> getConsentableClaims() {
-        return (List<Object>) tokenClaimsSet.getClaim(KEY_CONSENTABLE_CLAIMS);
+    public List<Object> getConsentedClaims() {
+        return (List<Object>) tokenClaimsSet.getClaim(KEY_CONSENTED_CLAIMS);
     }
 
     /**
-     * Get consented claims.
+     * Get whether consent has been enabled.
      * 
-     * @return consented claims
+     * @return whether consent has been enabled
      */
-    public List<Object> getConsentedClaims() {
-        return (List<Object>) tokenClaimsSet.getClaim(KEY_CONSENTED_CLAIMS);
+    public boolean isConsentEnabled() {
+        try {
+            return tokenClaimsSet.getBooleanClaim(KEY_CONSENT_ENABLED).booleanValue();
+        } catch (final ParseException e) {
+            log.error("Error parsing scope in request {}", tokenClaimsSet.getClaim(KEY_CONSENT_ENABLED));
+            // should never happen, programming error.
+            return false;
+        }
     }
 
     /**
@@ -600,13 +605,12 @@ public class TokenClaimsSet {
         @Nullable
         protected ClaimsSet dlClaimsUI;
 
-        /** Consentable claims. */
-        @Nullable
-        protected List<Object> cnsntlClaims;
-
         /** consented claims. */
         @Nullable
         protected List<Object> cnsntdClaims;
+        
+        /** Has consent been asked from the end-user. */
+        protected boolean cnsntEnabled;
 
         /** Code challenge. */
         @Nullable
@@ -717,26 +721,26 @@ public class TokenClaimsSet {
         }
 
         /**
-         * Set consentable claims.
+         * Set consented claims.
          * 
-         * @param consentableClaims consentable claims
+         * @param consentedClaims consented claims
          * 
          * @return the builder
          */
-        public Builder<T> setConsentableClaims(@Nullable final List<Object> consentableClaims) {
-            cnsntlClaims = consentableClaims;
+        public Builder<T> setConsentedClaims(@Nullable final List<Object> consentedClaims) {
+            cnsntdClaims = consentedClaims;
             return this;
         }
 
         /**
-         * Set consented claims.
+         * Set whether consent has been enabled.
          * 
-         * @param consentedClaims consented claims
+         * @param consentEnabled whether consent has been enabled.
          * 
          * @return the builder
          */
-        public Builder<T> setConsentedClaims(@Nullable final List<Object> consentedClaims) {
-            cnsntdClaims = consentedClaims;
+        public Builder<T> setConsentEnabled(final boolean consentEnabled) {
+            cnsntEnabled = consentEnabled;
             return this;
         }
 
diff --git a/idp-oidc-extension-api/src/test/java/net/shibboleth/idp/plugin/oidc/op/messaging/context/OIDCAuthenticationResponseConsentContextTest.java b/idp-oidc-extension-api/src/test/java/net/shibboleth/idp/plugin/oidc/op/messaging/context/OIDCAuthenticationResponseConsentContextTest.java
index f0446c58..28b76cb0 100644
--- a/idp-oidc-extension-api/src/test/java/net/shibboleth/idp/plugin/oidc/op/messaging/context/OIDCAuthenticationResponseConsentContextTest.java
+++ b/idp-oidc-extension-api/src/test/java/net/shibboleth/idp/plugin/oidc/op/messaging/context/OIDCAuthenticationResponseConsentContextTest.java
@@ -21,8 +21,6 @@ import org.testng.Assert;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
-import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCAuthenticationResponseConsentContext;
-
 /** Tests for {@link OIDCAuthenticationResponseConsentContext}. */
 public class OIDCAuthenticationResponseConsentContextTest {
 
@@ -35,7 +33,6 @@ public class OIDCAuthenticationResponseConsentContextTest {
 
     @Test
     public void testInitialState() {
-        Assert.assertNotNull(ctx.getConsentableAttributes());
         Assert.assertNotNull(ctx.getConsentedAttributes());
     }
 
diff --git a/idp-oidc-extension-api/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/BaseTokenRequestLookupFunctionTest.java b/idp-oidc-extension-api/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/BaseTokenRequestLookupFunctionTest.java
index cb72e4ac..d62c916a 100644
--- a/idp-oidc-extension-api/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/BaseTokenRequestLookupFunctionTest.java
+++ b/idp-oidc-extension-api/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/BaseTokenRequestLookupFunctionTest.java
@@ -19,7 +19,6 @@ package net.shibboleth.idp.plugin.oidc.op.profile.context.navigate;
 
 import net.minidev.json.JSONArray;
 import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCAuthenticationResponseContext;
-import net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.AbstractTokenRequestLookupFunction;
 import net.shibboleth.idp.plugin.oidc.op.token.support.AuthorizeCodeClaimsSet;
 import net.shibboleth.idp.plugin.oidc.op.token.support.TokenDeliveryClaimsClaimsSet;
 import net.shibboleth.idp.profile.context.navigate.WebflowRequestContextProfileRequestContextLookup;
@@ -84,8 +83,6 @@ public class BaseTokenRequestLookupFunctionTest {
 
     protected ClaimsSet tokenToUserInfoTokenDeliveryClaims = new TokenDeliveryClaimsClaimsSet();
 
-    protected JSONArray consentableClaims = new JSONArray();
-
     protected JSONArray consentedClaims = new JSONArray();
 
     BaseTokenRequestLookupFunctionTest() {
@@ -100,7 +97,6 @@ public class BaseTokenRequestLookupFunctionTest {
         tokenDeliveryClaims.setClaim("tokenDelivery", "value");
         tokenToIdTokenDeliveryClaims.setClaim("tokenToIdtokenDelivery", "value");
         tokenToUserInfoTokenDeliveryClaims.setClaim("tokenToUserInfotokenDeliveryClaim", "value");
-        consentableClaims.add("consentableClaim");
         consentedClaims.add("consentedClaim");
     }
 
@@ -115,7 +111,7 @@ public class BaseTokenRequestLookupFunctionTest {
                 cliendID, issuer, userPrin, subject, iat, exp, authTime, redirectUri, scope).setACR(acr).setNonce(nonce)
                         .setClaims(claimsRequest).setDlClaims(tokenDeliveryClaims)
                         .setDlClaimsID(tokenToIdTokenDeliveryClaims).setDlClaimsUI(tokenToUserInfoTokenDeliveryClaims)
-                        .setConsentableClaims(consentableClaims).setConsentedClaims(consentedClaims).build());
+                        .setConsentedClaims(consentedClaims).build());
     }
 
 }
\ No newline at end of file
diff --git a/idp-oidc-extension-api/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/TokenRequestConsentableAttributesLookupFunctionTest.java b/idp-oidc-extension-api/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/TokenRequestConsentableAttributesLookupFunctionTest.java
deleted file mode 100644
index eb06e4a3..00000000
--- a/idp-oidc-extension-api/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/TokenRequestConsentableAttributesLookupFunctionTest.java
+++ /dev/null
@@ -1,37 +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.context.navigate;
-
-import org.testng.annotations.Test;
-
-import net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.TokenRequestConsentableAttributesLookupFunction;
-
-import org.testng.Assert;
-
-/** Test for {@link TokenRequestConsentableAttributesLookupFunction}. */
-public class TokenRequestConsentableAttributesLookupFunctionTest extends BaseTokenRequestLookupFunctionTest {
-
-    private TokenRequestConsentableAttributesLookupFunction lookup =
-            new TokenRequestConsentableAttributesLookupFunction();
-
-    @Test
-    public void testLookup() {
-        Assert.assertTrue(lookup.apply(prc).contains("consentableClaim"));
-    }
-
-}
\ No newline at end of file
diff --git a/idp-oidc-extension-api/src/test/java/net/shibboleth/idp/plugin/oidc/op/token/support/TokenClaimsSetTest.java b/idp-oidc-extension-api/src/test/java/net/shibboleth/idp/plugin/oidc/op/token/support/TokenClaimsSetTest.java
index 126bb516..f76ec979 100644
--- a/idp-oidc-extension-api/src/test/java/net/shibboleth/idp/plugin/oidc/op/token/support/TokenClaimsSetTest.java
+++ b/idp-oidc-extension-api/src/test/java/net/shibboleth/idp/plugin/oidc/op/token/support/TokenClaimsSetTest.java
@@ -19,7 +19,6 @@ package net.shibboleth.idp.plugin.oidc.op.token.support;
 
 import org.testng.annotations.Test;
 
-import net.shibboleth.idp.plugin.oidc.op.token.support.TokenClaimsSet;
 import net.shibboleth.idp.plugin.oidc.op.token.support.testing.BaseTokenClaimsSetTest;
 
 import java.time.temporal.ChronoUnit;
@@ -39,8 +38,8 @@ public class TokenClaimsSetTest extends BaseTokenClaimsSetTest {
 
     protected void init() {
         tokenClaimsSet = new TokenClaimsSet(tokenType, tokenID, clientID, issuer, userPrincipal, subject, acr, iat, exp,
-                nonce, authTime, redirectURI, scope, claims, dlClaims, dlClaimsID, dlClaimsUI, consentableClaims,
-                consentedClaims, codeChallenge);
+                nonce, authTime, redirectURI, scope, claims, dlClaims, dlClaimsID, dlClaimsUI, consentedClaims,
+                codeChallenge, consentEnabled);
     }
 
     @Test
@@ -58,27 +57,27 @@ public class TokenClaimsSetTest extends BaseTokenClaimsSetTest {
         Assert.assertEquals(tokenClaimsSet.getUserinfoDeliveryClaims().getClaim("tokenToUserInfotokenDeliveryClaim"),
                 "value");
         Assert.assertEquals(tokenClaimsSet.getClientID(), clientID);
-        Assert.assertTrue(tokenClaimsSet.getConsentableClaims().contains("consentableClaim"));
         Assert.assertTrue(tokenClaimsSet.getConsentedClaims().contains("consentedClaim"));
         Assert.assertEquals(tokenClaimsSet.getExp(), exp.truncatedTo(ChronoUnit.MILLIS));
         Assert.assertEquals(tokenClaimsSet.getNonce(), nonce);
         Assert.assertEquals(tokenClaimsSet.getRedirectURI(), redirectURI);
         Assert.assertEquals(tokenClaimsSet.getScope(), scope);
         Assert.assertEquals(tokenClaimsSet.getCodeChallenge(), codeChallenge);
+        Assert.assertEquals(tokenClaimsSet.isConsentEnabled(), consentEnabled);
     }
 
     @Test
     public void testNullGetters() {
         tokenClaimsSet = new TokenClaimsSet(tokenType, tokenID, clientID, issuer, userPrincipal, subject, null, iat,
-                exp, null, authTime, redirectURI, scope, null, null, null, null, null, null, null);
+                exp, null, authTime, redirectURI, scope, null, null, null, null, null, null, false);
         Assert.assertNull(tokenClaimsSet.getACR());
         Assert.assertNull(tokenClaimsSet.getClaimsRequest());
         Assert.assertNull(tokenClaimsSet.getDeliveryClaims());
         Assert.assertNull(tokenClaimsSet.getIDTokenDeliveryClaims());
         Assert.assertNull(tokenClaimsSet.getUserinfoDeliveryClaims());
-        Assert.assertNull(tokenClaimsSet.getConsentableClaims());
         Assert.assertNull(tokenClaimsSet.getConsentedClaims());
         Assert.assertNull(tokenClaimsSet.getNonce());
+        Assert.assertFalse(tokenClaimsSet.isConsentEnabled());
     }
 
 }
\ No newline at end of file
diff --git a/idp-oidc-extension-api/src/test/java/net/shibboleth/idp/plugin/oidc/op/token/support/testing/BaseTokenClaimsSetTest.java b/idp-oidc-extension-api/src/test/java/net/shibboleth/idp/plugin/oidc/op/token/support/testing/BaseTokenClaimsSetTest.java
index 7673c86d..bd54167a 100644
--- a/idp-oidc-extension-api/src/test/java/net/shibboleth/idp/plugin/oidc/op/token/support/testing/BaseTokenClaimsSetTest.java
+++ b/idp-oidc-extension-api/src/test/java/net/shibboleth/idp/plugin/oidc/op/token/support/testing/BaseTokenClaimsSetTest.java
@@ -76,7 +76,7 @@ public class BaseTokenClaimsSetTest {
 
     protected Instant authTime = Instant.now();
 
-    protected JSONArray consentableClaims = new JSONArray();;
+    protected boolean consentEnabled = false;
 
     protected ClaimsSet dlClaimsID = new TokenDeliveryClaimsClaimsSet();
 
@@ -109,7 +109,6 @@ public class BaseTokenClaimsSetTest {
         dlClaims.setClaim("tokenDelivery", "value");
         dlClaimsID.setClaim("tokenToIdtokenDeliveryClaim", "value");
         dlClaimsUI.setClaim("tokenToUserInfotokenDeliveryClaim", "value");
-        consentableClaims.add("consentableClaim");
         consentedClaims.add("consentedClaim");
     }
 }
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/config/OIDCCoreProtocolConfiguration.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/config/OIDCCoreProtocolConfiguration.java
index 028be678..d863f72d 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/config/OIDCCoreProtocolConfiguration.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/config/OIDCCoreProtocolConfiguration.java
@@ -73,6 +73,9 @@ public class OIDCCoreProtocolConfiguration extends AbstractOIDCFlowAwareProfileC
     /** Whether client is allowed to use PKCE code challenge method plain. */
     @Nonnull private Predicate<ProfileRequestContext> allowPKCEPlainPredicate;
 
+    /** Whether to encode consent in authorization code and access/refresh tokens. */
+    @Nonnull private Predicate<ProfileRequestContext> encodeConsentInTokensPredicate;
+
     /** Lookup function to override issuer value. */
     @Nonnull private Function<ProfileRequestContext,String> issuerLookupStrategy;
     
@@ -141,6 +144,7 @@ public class OIDCCoreProtocolConfiguration extends AbstractOIDCFlowAwareProfileC
         acrRequestAlwaysEssentialPredicate = Predicates.alwaysFalse();
         forcePKCEPredicate = Predicates.alwaysFalse();
         allowPKCEPlainPredicate = Predicates.alwaysFalse();
+        encodeConsentInTokensPredicate = Predicates.alwaysFalse();
         
         defaultAuthenticationContextsLookupStrategy = FunctionSupport.constant(null);
         authenticationFlowsLookupStrategy = FunctionSupport.constant(null);
@@ -657,6 +661,36 @@ public class OIDCCoreProtocolConfiguration extends AbstractOIDCFlowAwareProfileC
     public void setAllowPKCEPlainPredicate(@Nonnull final Predicate<ProfileRequestContext> condition) {
         allowPKCEPlainPredicate = Constraint.isNotNull(condition, "Condition cannot be null");
     }
+    
+
+    /**
+     * Get whether to encode consent in authorization code and access/refresh tokens.
+     * 
+     * @param profileRequestContext profile request context
+     * 
+     * @return whether to encode consent in authorization code and access/refresh tokens
+     */
+    public boolean isEncodeConsentInTokens(@Nullable final ProfileRequestContext profileRequestContext) {
+        return encodeConsentInTokensPredicate.test(profileRequestContext);
+    }
+
+    /**
+     * Set whether to encode consent in authorization code and access/refresh tokens.
+     * 
+     * @param flag flag to set
+     */
+    public void setEncodeConsentInTokens(final boolean flag) {
+        encodeConsentInTokensPredicate = flag ? Predicates.alwaysTrue() : Predicates.alwaysFalse();
+    }
+
+    /**
+     * Set condition for whether to encode consent in authorization code and access/refresh tokens.
+     * 
+     * @param condition condition to set
+     */
+    public void setEncodeConsentInTokensPredicate(@Nonnull final Predicate<ProfileRequestContext> condition) {
+        encodeConsentInTokensPredicate = Constraint.isNotNull(condition, "Condition cannot be null");
+    }
 
     /**
      * Get the set of attribute IDs which should be encoded in encrypted form into the authorization code
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/config/logic/AttributeConsentEnabledInTokenClaimsSetPredicate.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/config/logic/AttributeConsentEnabledInTokenClaimsSetPredicate.java
new file mode 100644
index 00000000..884d6cfc
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/config/logic/AttributeConsentEnabledInTokenClaimsSetPredicate.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.plugin.oidc.op.config.logic;
+
+import javax.annotation.Nullable;
+
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCAuthenticationResponseContext;
+import net.shibboleth.idp.plugin.oidc.op.token.support.TokenClaimsSet;
+import net.shibboleth.idp.profile.logic.AbstractRelyingPartyPredicate;
+
+/**
+ * A predicate implementation that checks if attribute consent flag is enabled. The value is fetched from
+ * {@link TokenClaimsSet#isConsentEnabled()} via #{@link OIDCAuthenticationResponseContext#getTokenClaimsSet()}
+ * under outbound message context. Default value is false, if any of the objects in the chain is null.
+ */
+public class AttributeConsentEnabledInTokenClaimsSetPredicate extends AbstractRelyingPartyPredicate {
+    
+    /** {@inheritDoc} */
+    public boolean test(@Nullable final ProfileRequestContext input) {
+        final MessageContext outboundMessageCtx = input.getOutboundMessageContext();
+        if (outboundMessageCtx != null) {
+            final OIDCAuthenticationResponseContext oidcResponseContext = 
+                    outboundMessageCtx.getSubcontext(OIDCAuthenticationResponseContext.class, false);
+            if (oidcResponseContext != null && oidcResponseContext.getTokenClaimsSet() != null) {
+                return oidcResponseContext.getTokenClaimsSet().isConsentEnabled();
+            }
+        }
+        return false;
+    }
+
+}
diff --git a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/TokenRequestConsentableAttributesLookupFunction.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/config/logic/AttributeConsentFlowEnabledPredicate.java
similarity index 50%
rename from idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/TokenRequestConsentableAttributesLookupFunction.java
rename to idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/config/logic/AttributeConsentFlowEnabledPredicate.java
index ac12c60f..0c0a796a 100644
--- a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/context/navigate/TokenRequestConsentableAttributesLookupFunction.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/config/logic/AttributeConsentFlowEnabledPredicate.java
@@ -15,27 +15,24 @@
  * limitations under the License.
  */
 
-package net.shibboleth.idp.plugin.oidc.op.profile.context.navigate;
+package net.shibboleth.idp.plugin.oidc.op.config.logic;
 
-import java.util.List;
+import javax.annotation.Nullable;
 
-import javax.annotation.Nonnull;
+import org.opensaml.profile.context.ProfileRequestContext;
 
-import net.shibboleth.idp.plugin.oidc.op.token.support.TokenClaimsSet;
+import net.shibboleth.idp.authn.config.navigate.PostAuthenticationFlowsLookupFunction;
+import net.shibboleth.idp.profile.logic.AbstractRelyingPartyPredicate;
 
 /**
- * For Token and UserInfo end points.
- * 
- * A function that returns consentable claims via a lookup function. This lookup locates consentable claims from token
- * (Authorization Code / Access Token) for token request handling. If consentable claims are not available, null is
- * returned.
+ * A predicate implementation that checks if attribute-release is included in the list of post authentication flows
+ * returned by {@link PostAuthenticationFlowsLookupFunction}.
  */
-public class TokenRequestConsentableAttributesLookupFunction extends AbstractTokenClaimsLookupFunction<List<Object>> {
-
+public class AttributeConsentFlowEnabledPredicate extends AbstractRelyingPartyPredicate {
+    
     /** {@inheritDoc} */
-    @Override
-    List<Object> doLookup(@Nonnull final TokenClaimsSet tokenClaims) {
-        return tokenClaims.getConsentableClaims();
+    public boolean test(@Nullable final ProfileRequestContext input) {
+        final PostAuthenticationFlowsLookupFunction postAuthnFlowsLookup = new PostAuthenticationFlowsLookupFunction();
+        return postAuthnFlowsLookup.apply(input).contains("attribute-release");
     }
-
-}
\ 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 d10e68c2..c3b05180 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
@@ -341,8 +341,7 @@ public class AddAttributesToClaimsSet extends AbstractOIDCResponseAction {
                     continue;
                 }
                 
-                if (consentCtx != null && consentCtx.getConsentableAttributes().contains(name)
-                        && !consentCtx.getConsentedAttributes().contains(name)) {
+                if (consentCtx != null && !consentCtx.getConsentedAttributes().contains(name)) {
                     log.debug("{} Consentable attribute {} has no consent. Not added to claims set",
                             getLogPrefix(), name);
                     continue;
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
index 9e0e7ae0..2eb9ca2b 100644
--- 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
@@ -20,6 +20,7 @@ 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;
@@ -35,6 +36,7 @@ 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.config.OIDCCoreProtocolConfiguration;
+import net.shibboleth.idp.plugin.oidc.op.config.logic.AttributeConsentFlowEnabledPredicate;
 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;
@@ -84,6 +86,10 @@ public class SetAccessTokenToResponseContext extends AbstractOIDCResponseAction
     /** Strategy used to obtain the response issuer value. */
     @Nonnull private Function<ProfileRequestContext, String> issuerLookupStrategy;
 
+    /** 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;
     
@@ -121,6 +127,7 @@ public class SetAccessTokenToResponseContext extends AbstractOIDCResponseAction
                 new ChildContextLookup<>(OIDCAuthenticationResponseConsentContext.class).compose(
                         new OIDCAuthenticationResponseContextLookupFunction());
         relyingPartyContextLookupStrategy = new ChildContextLookup<>(RelyingPartyContext.class);
+        consentEnabledPredicate = new AttributeConsentFlowEnabledPredicate();
         dataSealer = Constraint.isNotNull(sealer, "DataSealer cannot be null");
         issuerLookupStrategy = new ResponderIdLookupFunction();
         idGeneratorLookupStrategy = FunctionSupport.constant(new SecureRandomIdentifierGenerationStrategy());
@@ -190,6 +197,18 @@ public class SetAccessTokenToResponseContext extends AbstractOIDCResponseAction
         issuerLookupStrategy = Constraint.isNotNull(strategy, "IssuerLookupStrategy 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");
+    }
+
     // Checkstyle: CyclomaticComplexity OFF
     /** {@inheritDoc} */
     @Override
@@ -268,14 +287,9 @@ public class SetAccessTokenToResponseContext extends AbstractOIDCResponseAction
             claimsSet = new AccessTokenClaimsSet(tokenClaimsSet, getOidcResponseContext().getScope(), claims, claimsUI,
                     Instant.now(), dateExp);
         } else {
-            JSONArray consentable = null;
-            JSONArray consented = null;
             final OIDCAuthenticationResponseConsentContext consentCtx =
                     consentContextLookupStrategy.apply(profileRequestContext);
-            if (consentCtx != null) {
-                consentable = consentCtx.getConsentableAttributes();
-                consented = consentCtx.getConsentedAttributes();
-            }
+            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(idGenerator, authenticationRequest.getClientID(),
                     issuerLookupStrategy.apply(profileRequestContext), subjectCtx.getPrincipalName(),
@@ -283,8 +297,9 @@ public class SetAccessTokenToResponseContext extends AbstractOIDCResponseAction
                     getOidcResponseContext().getAuthTime(), getOidcResponseContext().getRedirectURI(),
                     getOidcResponseContext().getScope())
                             .setACR(getOidcResponseContext().getAcr()).setClaims(authenticationRequest.getOIDCClaims())
-                            .setConsentableClaims(consentable).setConsentedClaims(consented).setDlClaims(claims)
-                            .setDlClaimsUI(claimsUI).setNonce(authenticationRequest.getNonce()).build();
+                            .setConsentedClaims(consented).setDlClaims(claims)
+                            .setDlClaimsUI(claimsUI).setNonce(authenticationRequest.getNonce())
+                            .setConsentEnabled(consentEnabledPredicate.test(profileRequestContext)).build();
         }
         try {
             getOidcResponseContext().setAccessToken(claimsSet.serialize(dataSealer), accessTokenLifetime);
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetAuthorizationCodeToResponseContext.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetAuthorizationCodeToResponseContext.java
index 8dc62e3a..2a7b3a8a 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetAuthorizationCodeToResponseContext.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetAuthorizationCodeToResponseContext.java
@@ -20,6 +20,7 @@ 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;
@@ -32,6 +33,7 @@ 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.config.OIDCCoreProtocolConfiguration;
+import net.shibboleth.idp.plugin.oidc.op.config.logic.AttributeConsentFlowEnabledPredicate;
 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;
@@ -94,6 +96,10 @@ public class SetAuthorizationCodeToResponseContext extends AbstractOIDCAuthentic
     @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 locate the code challenge. */
     @Nonnull private Function<ProfileRequestContext, String> codeChallengeLookupStrategy;
     
@@ -125,6 +131,7 @@ public class SetAuthorizationCodeToResponseContext extends AbstractOIDCAuthentic
                         new OIDCAuthenticationResponseContextLookupFunction());
         relyingPartyContextLookupStrategy = new ChildContextLookup<>(RelyingPartyContext.class);
         issuerLookupStrategy = new ResponderIdLookupFunction();
+        consentEnabledPredicate = new AttributeConsentFlowEnabledPredicate();
         dataSealer = Constraint.isNotNull(sealer, "DataSealer cannot be null");
         idGeneratorLookupStrategy = FunctionSupport.constant(new SecureRandomIdentifierGenerationStrategy());
     }
@@ -215,6 +222,18 @@ public class SetAuthorizationCodeToResponseContext extends AbstractOIDCAuthentic
         issuerLookupStrategy = Constraint.isNotNull(strategy, "IssuerLookupStrategy 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 boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
@@ -264,14 +283,9 @@ public class SetAuthorizationCodeToResponseContext extends AbstractOIDCAuthentic
     @Override
     protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
 
-        JSONArray consentable = null;
-        JSONArray consented = null;
         final OIDCAuthenticationResponseConsentContext consentCtx =
                 consentContextLookupStrategy.apply(profileRequestContext);
-        if (consentCtx != null) {
-            consentable = consentCtx.getConsentableAttributes();
-            consented = consentCtx.getConsentedAttributes();
-        }
+        final JSONArray consented = consentCtx != null ? consentCtx.getConsentedAttributes() : null;
         ClaimsSet claims = null;
         ClaimsSet claimsID = null;
         ClaimsSet claimsUI = null;
@@ -290,9 +304,9 @@ public class SetAuthorizationCodeToResponseContext extends AbstractOIDCAuthentic
                 getOidcResponseContext().getScope()).setACR(getOidcResponseContext().getAcr())
                         .setNonce(new DefaultRequestNonceLookupFunction().apply(profileRequestContext))
                         .setClaims(getOidcResponseContext().getRequestedClaims()).setDlClaims(claims)
-                        .setDlClaimsID(claimsID).setDlClaimsUI(claimsUI).setConsentableClaims(consentable)
-                        .setConsentedClaims(consented)
-                        .setCodeChallenge(codeChallenge).build();
+                        .setDlClaimsID(claimsID).setDlClaimsUI(claimsUI).setConsentedClaims(consented)
+                        .setCodeChallenge(codeChallenge)
+                        .setConsentEnabled(consentEnabledPredicate.test(profileRequestContext)).build();
         // We set token claims set to response context for possible access token generation.
         getOidcResponseContext().setTokenClaimsSet(claimsSet);
         try {
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetConsentFromTokenToResponseContext.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetConsentFromTokenToResponseContext.java
index f304e931..579a33e4 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetConsentFromTokenToResponseContext.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetConsentFromTokenToResponseContext.java
@@ -23,7 +23,6 @@ import java.util.function.Function;
 import javax.annotation.Nonnull;
 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.profile.context.navigate.TokenRequestConsentableAttributesLookupFunction;
 import net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.TokenRequestConsentedAttributesLookupFunction;
 import net.shibboleth.utilities.java.support.component.ComponentSupport;
 import net.shibboleth.utilities.java.support.logic.Constraint;
@@ -45,15 +44,11 @@ public class SetConsentFromTokenToResponseContext extends AbstractOIDCResponseAc
     /** Strategy used to obtain the consented attributes. */
     @Nonnull private Function<ProfileRequestContext, List<Object>> consentedAttributesLookupStrategy;
 
-    /** Strategy used to obtain the consentable attributes. */
-    @Nonnull private Function<ProfileRequestContext, List<Object>> consentableAttributesLookupStrategy;
-
     /**
      * Constructor.
      */
     public SetConsentFromTokenToResponseContext() {
         consentedAttributesLookupStrategy = new TokenRequestConsentedAttributesLookupFunction();
-        consentableAttributesLookupStrategy = new TokenRequestConsentableAttributesLookupFunction();
     }
 
     /**
@@ -69,28 +64,13 @@ public class SetConsentFromTokenToResponseContext extends AbstractOIDCResponseAc
                 Constraint.isNotNull(strategy, "ConsentedAttributesLookupStrategy lookup strategy cannot be null");
     }
 
-    /**
-     * Set the strategy used to locate the consentable attributes.
-     * 
-     * @param strategy lookup strategy
-     */
-    public void
-            setConsentableAttributesLookupStrategy(@Nonnull final Function<ProfileRequestContext,
-                    List<Object>> strategy) {
-        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-        consentableAttributesLookupStrategy =
-                Constraint.isNotNull(strategy, "ConsentableAttributesLookupStrategy lookup strategy cannot be null");
-    }
-
     /** {@inheritDoc} */
     @Override
     protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
         final List<Object> consentedAttributes = consentedAttributesLookupStrategy.apply(profileRequestContext);
-        final List<Object> consentableAttributes = consentableAttributesLookupStrategy.apply(profileRequestContext);
-        if (consentedAttributes != null || consentableAttributes != null) {
+        if (consentedAttributes != null) {
             final OIDCAuthenticationResponseConsentContext consentClaimsCtx =
                     getOidcResponseContext().getSubcontext(OIDCAuthenticationResponseConsentContext.class, true);
-            consentClaimsCtx.getConsentableAttributes().addAll(consentableAttributes);
             consentClaimsCtx.getConsentedAttributes().addAll(consentedAttributes);
         }
     }
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetConsentToResponseContext.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetConsentToResponseContext.java
index 33a9e4b4..7fcb23be 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetConsentToResponseContext.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetConsentToResponseContext.java
@@ -19,6 +19,7 @@ package net.shibboleth.idp.plugin.oidc.op.profile.impl;
 
 import java.util.Map;
 import java.util.function.Function;
+import java.util.function.Predicate;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
@@ -30,16 +31,22 @@ import org.opensaml.profile.context.ProfileRequestContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import net.shibboleth.idp.consent.context.AttributeReleaseContext;
-import net.shibboleth.idp.consent.context.ConsentContext;
+import net.shibboleth.idp.plugin.oidc.op.config.OIDCCoreProtocolConfiguration;
+import net.shibboleth.idp.plugin.oidc.op.config.logic.AttributeConsentFlowEnabledPredicate;
 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.consent.Consent;
+import net.shibboleth.idp.profile.IdPEventIds;
+import net.shibboleth.idp.profile.config.ProfileConfiguration;
+import net.shibboleth.idp.profile.context.RelyingPartyContext;
+import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.idp.attribute.context.AttributeContext;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
 import net.shibboleth.utilities.java.support.logic.Constraint;
 
 /**
- * Action that checks for any existing consent information for token delivery. Consent information is stored to
+ * Action that checks for adds the currently existing attributes from {@link AttributeContext} for token delivery. They
+ * are assumed to be consented, if they exist in the context. The (consent) information is stored to
  * {@link OIDCAuthenticationResponseTokenClaimsContext} that is created under {@link OIDCAuthenticationResponseContext}.
  **/
 public class SetConsentToResponseContext extends AbstractOIDCResponseAction {
@@ -47,47 +54,72 @@ public class SetConsentToResponseContext extends AbstractOIDCResponseAction {
     /** Class logger. */
     @Nonnull private Logger log = LoggerFactory.getLogger(SetConsentToResponseContext.class);
 
-    /** Consent context. */
-    @Nullable private ConsentContext consentContext;
-
     /**
-     * Strategy used to find the {@link ConsentContext} from the {@link ProfileRequestContext}.
+     * Strategy used to locate the {@link RelyingPartyContext} associated with a given {@link ProfileRequestContext}.
      */
-    @Nonnull private Function<ProfileRequestContext, ConsentContext> consentContextLookupStrategy;
-
-    /** The {@link AttributeReleaseContext} to operate on. */
-    @Nullable private AttributeReleaseContext attributeReleaseContext;
+    @Nonnull private Function<ProfileRequestContext, RelyingPartyContext> relyingPartyContextLookupStrategy;
 
     /**
-     * Strategy used to find the {@link AttributeReleaseContext} from the {@link ProfileRequestContext}.
+     * Strategy used to locate the {@link AttributeContext} associated with a given {@link ProfileRequestContext}.
+     */
+    @Nonnull private Function<ProfileRequestContext,AttributeContext> attributeContextLookupStrategy;
+    
+    /**
+     * Predicate used to check if consent is enabled with a given {@link ProfileRequestContext}.
      */
-    @Nonnull private Function<ProfileRequestContext, AttributeReleaseContext> attributeReleaseContextLookupStrategy;
+    @Nonnull private Predicate<ProfileRequestContext> consentEnabledPredicate;
+
+    /** AttributeContext to use. */
+    @Nullable private AttributeContext attributeCtx;
 
     /** Constructor. */
     SetConsentToResponseContext() {
-        consentContextLookupStrategy = new ChildContextLookup<>(ConsentContext.class, false);
-        attributeReleaseContextLookupStrategy = new ChildContextLookup<>(AttributeReleaseContext.class, false);
+        relyingPartyContextLookupStrategy = new ChildContextLookup<>(RelyingPartyContext.class);
+        attributeContextLookupStrategy = new ChildContextLookup<>(AttributeContext.class).compose(
+                new ChildContextLookup<>(RelyingPartyContext.class));
+        consentEnabledPredicate = new AttributeConsentFlowEnabledPredicate();
     }
 
     /**
-     * Set the consent context lookup strategy.
+     * Set the strategy used to locate the {@link RelyingPartyContext} associated with a given
+     * {@link ProfileRequestContext}.
      * 
-     * @param strategy the consent context lookup strategy
+     * @param strategy strategy used to locate the {@link RelyingPartyContext} associated with a given
+     *            {@link ProfileRequestContext}
      */
-    public void
-            setConsentContextLookupStrategy(@Nonnull final Function<ProfileRequestContext, ConsentContext> strategy) {
-        consentContextLookupStrategy = Constraint.isNotNull(strategy, "Consent context lookup strategy cannot be null");
+    public void setRelyingPartyContextLookupStrategy(
+            @Nonnull final Function<ProfileRequestContext, RelyingPartyContext> strategy) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+        relyingPartyContextLookupStrategy =
+                Constraint.isNotNull(strategy, "RelyingPartyContext lookup strategy cannot be null");
     }
 
     /**
-     * Set the attribute release context lookup strategy.
+     * Set the strategy used to locate the {@link AttributeContext} associated with a given
+     * {@link ProfileRequestContext}.
      * 
-     * @param strategy the attribute release context lookup strategy
+     * @param strategy strategy used to locate the {@link AttributeContext} associated with a given
+     *            {@link ProfileRequestContext}
      */
-    public void setAttributeReleaseContextLookupStrategy(
-            @Nonnull final Function<ProfileRequestContext, AttributeReleaseContext> strategy) {
-        attributeReleaseContextLookupStrategy =
-                Constraint.isNotNull(strategy, "Attribute release context lookup strategy cannot be null");
+    public void setAttributeContextLookupStrategy(
+            @Nonnull final Function<ProfileRequestContext, AttributeContext> strategy) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+        attributeContextLookupStrategy =
+                Constraint.isNotNull(strategy, "AttributeContext 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} */
@@ -97,18 +129,40 @@ public class SetConsentToResponseContext extends AbstractOIDCResponseAction {
             return false;
         }
         
-        consentContext = consentContextLookupStrategy.apply(profileRequestContext);
-        if (consentContext == null) {
-            log.debug("{} Unable to locate consent context within profile request context, nothing to do",
-                    getLogPrefix());
+        final RelyingPartyContext rpCtx = relyingPartyContextLookupStrategy.apply(profileRequestContext);
+        if (rpCtx == null) {
+            log.error("{} No relying party context associated with this profile request", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, IdPEventIds.INVALID_RELYING_PARTY_CTX);
             return false;
         }
-        attributeReleaseContext = attributeReleaseContextLookupStrategy.apply(profileRequestContext);
-        if (attributeReleaseContext == null) {
-            log.debug("{} Unable to locate attribute release context within profile request context", getLogPrefix());
+
+        if (!consentEnabledPredicate.test(profileRequestContext)) {
+            log.debug("{} The attribute consent has not been enabled, nothing to do", getLogPrefix());
+            return false;
+        }
+
+        final ProfileConfiguration pc = rpCtx.getProfileConfig();
+        if (pc != null && pc instanceof OIDCCoreProtocolConfiguration) {
+            log.debug("{} TODO Returning {}", ((OIDCCoreProtocolConfiguration) pc).isEncodeConsentInTokens(profileRequestContext));
+            if (!((OIDCCoreProtocolConfiguration) pc).isEncodeConsentInTokens(profileRequestContext)) {
+                log.debug("{} The consent encoding to token has not been enabled, nothing to do", 
+                        getLogPrefix());
+                return false;                
+            }
+        } else {
+            log.error("{} No oidc profile configuration associated with this profile request", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, IdPEventIds.INVALID_PROFILE_CONFIG);
+            return false;
+        }
+                
+        attributeCtx = attributeContextLookupStrategy.apply(profileRequestContext);
+        if (attributeCtx == null) {
+            log.debug("{} No AttributeSubcontext available, nothing to do", getLogPrefix());
             ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
             return false;
+        
         }
+
         return true;
     }
 
@@ -118,20 +172,11 @@ public class SetConsentToResponseContext extends AbstractOIDCResponseAction {
 
         final OIDCAuthenticationResponseConsentContext oidcConsentCtx =
                 getOidcResponseContext().getSubcontext(OIDCAuthenticationResponseConsentContext.class, true);
-        final Map<String, Consent> consents = consentContext.getCurrentConsents().isEmpty()
-                ? consentContext.getPreviousConsents() : consentContext.getCurrentConsents();
-        for (final String key : consents.keySet()) {
-            if (consents.get(key) != null && consents.get(key).isApproved()) {
-                oidcConsentCtx.getConsentedAttributes().add(key);
-            }
-        }
-        if (attributeReleaseContext.getConsentableAttributes() != null) {
-            oidcConsentCtx.getConsentableAttributes()
-                    .addAll(attributeReleaseContext.getConsentableAttributes().keySet());
-        }
+
+        final Map<String, IdPAttribute> consented = attributeCtx.getIdPAttributes();
+        oidcConsentCtx.getConsentedAttributes().addAll(consented.keySet());
         log.debug("{} Set to response context consented attributes {} and consentable attributes {}", getLogPrefix(),
-                oidcConsentCtx.getConsentedAttributes().toJSONString(),
-                oidcConsentCtx.getConsentableAttributes().toJSONString());
+                oidcConsentCtx.getConsentedAttributes().toJSONString());
 
     }
 }
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/consent-lookup/consent-lookup-beans.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/consent-lookup/consent-lookup-beans.xml
new file mode 100644
index 00000000..ab93d5af
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/consent-lookup/consent-lookup-beans.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans" xmlns:c="http://www.springframework.org/schema/c"
+    xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
+    xmlns:util="http://www.springframework.org/schema/util" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
+                           http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
+    default-init-method="initialize" default-destroy-method="destroy">
+
+    <bean id="ConsentEnabledPredicate"
+        class="net.shibboleth.idp.plugin.oidc.op.config.logic.AttributeConsentEnabledInTokenClaimsSetPredicate" />
+
+    <bean id="PopulateConsentInterceptContext"
+            class="net.shibboleth.idp.profile.interceptor.impl.PopulateProfileInterceptorContext" scope="prototype"
+            p:availableFlows="#{@'shibboleth.ProfileInterceptorFlowDescriptorManager'.getComponents()}">
+        <property name="activeFlowsLookupStrategy">
+            <bean parent="shibboleth.Functions.Constant" c:target="#{ {'attribute-release-query' } }" />
+        </property>
+    </bean>
+
+</beans>
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/consent-lookup/consent-lookup-flow.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/consent-lookup/consent-lookup-flow.xml
new file mode 100644
index 00000000..ecfb9fcf
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/consent-lookup/consent-lookup-flow.xml
@@ -0,0 +1,29 @@
+<flow xmlns="http://www.springframework.org/schema/webflow" 
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow.xsd"
+    abstract="true">
+
+    <decision-state id="DoConsentLookup">
+        <if test="ConsentEnabledPredicate.test(opensamlProfileRequestContext)"
+            then="CheckConsentSetup" else="BuildResponse" />
+    </decision-state>
+    
+    <decision-state id="CheckConsentSetup">
+        <if test="opensamlProfileRequestContext.getOutboundMessageContext().getSubcontext(T(net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCAuthenticationResponseContext)).containsSubcontext(T(net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCAuthenticationResponseConsentContext))"
+            then="BuildResponse" else="ConsentFlowSetup" />
+    </decision-state>    
+
+    <action-state id="ConsentFlowSetup">
+        <evaluate expression="PopulateConsentInterceptContext" />
+        <evaluate expression="opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.profile.context.ProfileInterceptorContext)).setAttemptedFlow(flowRequestContext.getActiveFlow().getApplicationContext().getBean('shibboleth.ProfileInterceptorFlowDescriptorManager').getComponents().?[id matches 'intercept/attribute-release-query'])" />
+        <transition on="success" to="ConsentFlow" />
+    </action-state>
+
+    <subflow-state id="ConsentFlow" subflow="intercept/attribute-release-query">
+        <input name="calledAsSubflow" value="true" />
+        <transition on="proceed" to="BuildResponse"/>
+    </subflow-state>
+    
+    <bean-import resource="../../oidc/consent-lookup/consent-lookup-beans.xml" />
+        
+</flow>
\ No newline at end of file
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 a032b6d6..4ce1d038 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
@@ -1,6 +1,6 @@
 <flow xmlns="http://www.springframework.org/schema/webflow" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow.xsd"
-    parent="oidc/abstract-api, oidc/metadata-lookup">
+    parent="oidc/abstract-api, oidc/metadata-lookup, oidc/consent-lookup">
 
     <action-state id="InitializeMandatoryContexts">
         <evaluate expression="InitializeProfileRequestContext" />
@@ -56,9 +56,9 @@
         <evaluate expression="ResolveAttributes" />
         <evaluate expression="FilterAttributes" />
         <evaluate expression="'proceed'" />
-        <transition on="proceed" to="BuildResponse" />
+        <transition on="proceed" to="DoConsentLookup" />
     </action-state>
-
+    
     <action-state id="BuildResponse">
         <evaluate expression="SetAccessTokenToResponseContext" />
         <evaluate expression="SetRefreshTokenToResponseContext" />
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/userinfo/token-flow.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/userinfo/token-flow.xml
index ee8226ee..e86fda70 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/userinfo/token-flow.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/userinfo/token-flow.xml
@@ -1,7 +1,7 @@
 <flow xmlns="http://www.springframework.org/schema/webflow"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow.xsd"
-    parent="oidc/abstract-api, oidc/metadata-lookup">
+    parent="oidc/abstract-api, oidc/metadata-lookup, oidc/consent-lookup">
 
     <action-state id="InitializeMandatoryContexts">
         <evaluate expression="InitializeProfileRequestContext" />
@@ -49,9 +49,9 @@
         <evaluate expression="ResolveAttributes" />
         <evaluate expression="FilterAttributes" />
         <evaluate expression="'proceed'" />
-        <transition on="proceed" to="BuildResponse" />
+        <transition on="proceed" to="DoConsentLookup" />
     </action-state>
-
+    
     <action-state id="BuildResponse">
         <evaluate expression="AddUserInfoShell" />
         <evaluate expression="AddAttributeClaimsToUserInfo" />
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml
index 6ff09290..a7b41064 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml
@@ -28,6 +28,7 @@
         p:tokenEndpointAuthMethods="%{idp.oidc.tokenEndpointAuthMethods:client_secret_basic,client_secret_post,client_secret_jwt,private_key_jwt}"
         p:forcePKCE="%{idp.oidc.forcePKCE:false}"
         p:allowPKCEPlain="%{idp.oidc.allowPKCEPlain:false}"
+        p:encodeConsentInTokens="%{idp.oidc.encodeConsentInTokens:false}"
         p:encodedAttributes="%{idp.oidc.encodedAttributes:}"
         p:alwaysIncludedAttributes="%{idp.oidc.alwaysIncludedAttributes:}"
         p:deniedUserInfoAttributes="%{idp.oidc.deniedUserInfoAttributes:}" />
@@ -146,6 +147,14 @@
                 <constructor-arg value="false" />
             </bean>
         </property>
+        <property name="encodeConsentInTokensPredicate">
+            <bean class="net.shibboleth.utilities.java.support.logic.PredicateSupport" factory-method="fromFunction">
+                <constructor-arg>
+                    <bean parent="shibboleth.MDDrivenBoolProperty" p:propertyName="encodeConsentInTokens" />
+                </constructor-arg>
+                <constructor-arg value="%{idp.oidc.encodeConsentInTokens:false}" />
+            </bean>
+        </property>
         <property name="issuerLookupStrategy">
             <bean parent="shibboleth.MDDrivenStringProperty" p:propertyName="issuer" p:defaultValue-ref="issuer" />
         </property>
diff --git a/idp-oidc-extension-impl/src/main/resources/net/shibboleth/idp/plugin/oidc/op/conf/oidc.properties b/idp-oidc-extension-impl/src/main/resources/net/shibboleth/idp/plugin/oidc/op/conf/oidc.properties
index da44fdbc..cbdd8f32 100644
--- a/idp-oidc-extension-impl/src/main/resources/net/shibboleth/idp/plugin/oidc/op/conf/oidc.properties
+++ b/idp-oidc-extension-impl/src/main/resources/net/shibboleth/idp/plugin/oidc/op/conf/oidc.properties
@@ -43,6 +43,9 @@ idp.signing.oidc.rsa.enc.key = %{idp.home}/credentials/idp-encryption-rsa.jwk
 #idp.oidc.forcePKCE = false
 #idp.oidc.allowPKCEPlain = false
 
+# Store user consent to authorization code & access/refresh tokens instead of exploiting consent storage
+#idp.oidc.encodeConsentInTokens = false
+
 # shibboleth.ClientInformationResolverService properties
 #idp.service.clientinfo.failFast = false
 #idp.service.clientinfo.checkInterval = PT0S
@@ -68,4 +71,4 @@ idp.oidc.subject.sourceAttribute = uid
 idp.oidc.subject.salt = this_too_should_be_ch4ng3d
 
 # Bean to determine whether SAML metadata should be exploited for trusted OIDC RP resolution
-#idp.oidc.metadata.saml = shibboleth.Conditions.TRUE
\ No newline at end of file
+#idp.oidc.metadata.saml = shibboleth.Conditions.TRUE
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 6629ada7..3148919f 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
@@ -184,5 +184,15 @@ public abstract class AbstractOidcFlowTest extends AbstractFlowTest {
     protected void removeMetadata(final StorageService storageService, final String clientId) throws IOException {
         storageService.delete(BaseStorageServiceClientInformationComponent.CONTEXT_NAME, clientId);
     }
+    
+    protected void storeConsent(final StorageService storageService, final String uid, final String clientId,
+            final String... ids) throws IOException {
+        final StringBuilder consented = new StringBuilder("[{\"id\":\"subject-public\"}");
+        for (final String id : ids) {
+            consented.append(",{\"id\":\"" + id + "\"}");
+        }
+        storageService.create("intercept/attribute-release", uid + ":" + clientId, 
+                consented.append("]").toString(), System.currentTimeMillis() + (60 * 60 * 1000));
+    }
 
 }
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 28169209..2b48da9e 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
@@ -34,6 +34,7 @@ import org.testng.annotations.Test;
 
 import com.nimbusds.jose.JOSEException;
 import com.nimbusds.jose.JWSAlgorithm;
+import com.nimbusds.jwt.JWT;
 import com.nimbusds.oauth2.sdk.ParseException;
 import com.nimbusds.oauth2.sdk.auth.ClientAuthenticationMethod;
 import com.nimbusds.oauth2.sdk.auth.ClientSecretJWT;
@@ -141,20 +142,26 @@ public class TokenFlowTest extends AbstractOidcFlowTest {
         DataSealerException, ComponentInitializationException {
         initializeGrantAndRequest(clientId, createRequestParameters(redirectUri, "authorization_code",
                 buildAuthorizationCode(clientId), clientId));
+        storeConsent(storageService, "jdoe", clientId, "mail");
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
         OIDCTokenResponse response = parseSuccessResponse(result, OIDCTokenResponse.class);
         Assert.assertNotNull(response.getTokens().getAccessToken());
+        Assert.assertNotNull(response.getOIDCTokens().getIDToken());
     }
 
     @Test
     public void testValidGrantSaml() throws ParseException, IOException, NoSuchAlgorithmException, URISyntaxException,
-        DataSealerException, ComponentInitializationException {
+        DataSealerException, ComponentInitializationException, java.text.ParseException {
         initializeGrantAndRequest(clientId, createRequestParameters(redirectUri, "authorization_code",
                 buildAuthorizationCode(clientIdSaml), clientIdSaml));
         setBasicAuth(clientIdSaml, clientSecret);
+        storeConsent(storageService, "jdoe", clientIdSaml, "mail");
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
         OIDCTokenResponse response = parseSuccessResponse(result, OIDCTokenResponse.class);
         Assert.assertNotNull(response.getTokens().getAccessToken());
+        final JWT idToken = response.getOIDCTokens().getIDToken();
+        Assert.assertNotNull(idToken);
+        Assert.assertEquals(idToken.getJWTClaimsSet().getClaim("email"), "jdoe at example.org");
     }
 
     protected String buildAuthorizationCode(String clientId) throws NoSuchAlgorithmException, URISyntaxException,
@@ -171,7 +178,8 @@ public class TokenFlowTest extends AbstractOidcFlowTest {
             JSONObject deliveryClaimsIDToken, JSONObject deliveryClaimsUserInfo) throws NoSuchAlgorithmException,
             URISyntaxException, DataSealerException, ComponentInitializationException {
         return ValidateGrantTest.buildAuthorizationCode(clientId, "https://op.example.org", "jdoe", "mock",
-                redirectUri, verifier, deliveryClaims, deliveryClaimsIDToken, deliveryClaimsUserInfo).toString();
+                redirectUri, verifier, deliveryClaims, deliveryClaimsIDToken, deliveryClaimsUserInfo, 
+                "openid profile email").toString();
     }
     
     @Test
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/UserInfoTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/UserInfoTest.java
index 5cccc347..b9ea3416 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/UserInfoTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/UserInfoTest.java
@@ -123,6 +123,7 @@ public class UserInfoTest extends AbstractOidcApiFlowTest {
         ComponentInitializationException, IOException {
         BearerAccessToken token = buildToken(clientId, subject, new Scope("openid", "email", "profile"));
         storeMetadata(storageService, clientId, "mockSecret");
+        storeConsent(storageService, "jdoe", clientId, "mail");
         request.addHeader("Authorization", token.toAuthorizationHeader());
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
         UserInfoSuccessResponse response = parseSuccessResponse(result, UserInfoSuccessResponse.class);
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/AddAttributesToClaimsSetTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/AddAttributesToClaimsSetTest.java
index 9efa67ef..b8fd96eb 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/AddAttributesToClaimsSetTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/AddAttributesToClaimsSetTest.java
@@ -37,7 +37,6 @@ import net.shibboleth.idp.attribute.transcoding.TranscodingRule;
 import net.shibboleth.idp.attribute.transcoding.impl.AttributeTranscoderRegistryImpl;
 import net.shibboleth.idp.plugin.oidc.op.config.OIDCCoreProtocolConfiguration;
 import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCAuthenticationResponseConsentContext;
-import net.shibboleth.idp.plugin.oidc.op.profile.impl.AddAttributesToClaimsSet;
 import net.shibboleth.idp.profile.context.RelyingPartyContext;
 import net.shibboleth.idp.profile.context.navigate.WebflowRequestContextProfileRequestContextLookup;
 import net.shibboleth.idp.profile.testing.ActionTestingSupport;
@@ -221,9 +220,8 @@ public class AddAttributesToClaimsSetTest extends BaseOIDCResponseActionTest {
         setAttributeContext();
         final OIDCAuthenticationResponseConsentContext ctx = (OIDCAuthenticationResponseConsentContext) respCtx
                 .addSubcontext(new OIDCAuthenticationResponseConsentContext());
-        ctx.getConsentableAttributes().add("test1");
-        ctx.getConsentableAttributes().add("test3");
         ctx.getConsentedAttributes().add("test1");
+        ctx.getConsentedAttributes().add("test4");
         final Event event = action.execute(requestCtx);
         ActionTestingSupport.assertProceedEvent(event);
         Assert.assertTrue(respCtx.getIDToken().getClaim("test1").equals("value1 value2"));
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
index f06187c2..f50632ff 100644
--- 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
@@ -20,7 +20,6 @@ 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.profile.impl.SetAccessTokenToResponseContext;
 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;
@@ -110,15 +109,12 @@ public class SetAccessTokenToResponseContextTest extends BaseOIDCResponseActionT
         respCtx.setTokenClaimsSet(null);
         OIDCAuthenticationResponseConsentContext consCtx = (OIDCAuthenticationResponseConsentContext) respCtx
                 .addSubcontext(new OIDCAuthenticationResponseConsentContext());
-        consCtx.getConsentableAttributes().add("1");
-        consCtx.getConsentableAttributes().add("2");
         consCtx.getConsentedAttributes().add("3");
         final Event event = action.execute(requestCtx);
         ActionTestingSupport.assertProceedEvent(event);
         Assert.assertNotNull(respCtx.getAccessToken());
         AccessTokenClaimsSet at = AccessTokenClaimsSet.parse(respCtx.getAccessToken().getValue(), getDataSealer());
         Assert.assertNotNull(at);
-        Assert.assertEquals(at.getConsentableClaims(), consCtx.getConsentableAttributes());
         Assert.assertEquals(at.getConsentedClaims(), consCtx.getConsentedAttributes());
     }
 
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetAuthorizationCodeToResponseContextTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetAuthorizationCodeToResponseContextTest.java
index 35dad681..3a313e2b 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetAuthorizationCodeToResponseContextTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetAuthorizationCodeToResponseContextTest.java
@@ -20,7 +20,6 @@ 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.profile.impl.SetAuthorizationCodeToResponseContext;
 import net.shibboleth.idp.plugin.oidc.op.token.support.AuthorizeCodeClaimsSet;
 import net.shibboleth.idp.profile.IdPEventIds;
 import net.shibboleth.idp.profile.context.RelyingPartyContext;
@@ -81,8 +80,6 @@ public class SetAuthorizationCodeToResponseContextTest extends BaseOIDCResponseA
         init();
         final OIDCAuthenticationResponseConsentContext consCtx = (OIDCAuthenticationResponseConsentContext) respCtx
                 .addSubcontext(new OIDCAuthenticationResponseConsentContext());
-        consCtx.getConsentableAttributes().add("1");
-        consCtx.getConsentableAttributes().add("2");
         consCtx.getConsentedAttributes().add("3");
         final Event event = action.execute(requestCtx);
         ActionTestingSupport.assertProceedEvent(event);
@@ -90,7 +87,6 @@ public class SetAuthorizationCodeToResponseContextTest extends BaseOIDCResponseA
         final AuthorizeCodeClaimsSet ac =
                 AuthorizeCodeClaimsSet.parse(respCtx.getAuthorizationCode().getValue(), getDataSealer());
         Assert.assertNotNull(ac);
-        Assert.assertEquals(ac.getConsentableClaims(), consCtx.getConsentableAttributes());
         Assert.assertEquals(ac.getConsentedClaims(), consCtx.getConsentedAttributes());
     }
 
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetConsentFromTokenToResponseContextTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetConsentFromTokenToResponseContextTest.java
index 2eaf861c..1cb32427 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetConsentFromTokenToResponseContextTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetConsentFromTokenToResponseContextTest.java
@@ -19,7 +19,6 @@ package net.shibboleth.idp.plugin.oidc.op.profile.impl;
 
 import net.minidev.json.JSONArray;
 import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCAuthenticationResponseConsentContext;
-import net.shibboleth.idp.plugin.oidc.op.profile.impl.SetConsentFromTokenToResponseContext;
 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.testing.ActionTestingSupport;
@@ -63,21 +62,17 @@ public class SetConsentFromTokenToResponseContextTest extends BaseOIDCResponseAc
      */
     @Test
     public void testSuccess() throws ComponentInitializationException, URISyntaxException {
-        final JSONArray consentableClaims = new JSONArray();
-        consentableClaims.add("1");
-        consentableClaims.add("2");
         final JSONArray consentedClaims = new JSONArray();
         consentedClaims.add("1");
         final TokenClaimsSet claims = new AuthorizeCodeClaimsSet.Builder(idGenerator, new ClientID(), "issuer", "userPrin",
                 "subject", Instant.now(), Instant.now(), Instant.now(), new URI("http://example.com"), new Scope())
-                        .setConsentableClaims(consentableClaims).setConsentedClaims(consentedClaims).build();
+                        .setConsentedClaims(consentedClaims).build();
         respCtx.setTokenClaimsSet(claims);
         final Event event = action.execute(requestCtx);
         ActionTestingSupport.assertProceedEvent(event);
         final OIDCAuthenticationResponseConsentContext ctx =
                 respCtx.getSubcontext(OIDCAuthenticationResponseConsentContext.class, false);
         Assert.assertNotNull(ctx);
-        Assert.assertEquals(ctx.getConsentableAttributes(), consentableClaims);
         Assert.assertEquals(ctx.getConsentedAttributes(), consentedClaims);
     }
 
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetConsentToResponseContextTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetConsentToResponseContextTest.java
index 54543b45..3e31a9e0 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetConsentToResponseContextTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetConsentToResponseContextTest.java
@@ -17,111 +17,101 @@
 
 package net.shibboleth.idp.plugin.oidc.op.profile.impl;
 
-import net.shibboleth.idp.consent.context.AttributeReleaseContext;
-import net.shibboleth.idp.consent.context.ConsentContext;
+import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.idp.attribute.context.AttributeContext;
+import net.shibboleth.idp.plugin.oidc.op.config.OIDCCoreProtocolConfiguration;
 import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCAuthenticationResponseConsentContext;
-import net.shibboleth.idp.plugin.oidc.op.profile.impl.SetConsentToResponseContext;
+import net.shibboleth.idp.profile.context.RelyingPartyContext;
 import net.shibboleth.idp.profile.testing.ActionTestingSupport;
-import net.shibboleth.idp.consent.Consent;
 import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
 
+import java.util.HashSet;
+import java.util.Set;
+
 import org.opensaml.profile.action.EventIds;
 import org.springframework.webflow.execution.Event;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
+import com.google.common.base.Predicates;
+
 /** {@link SetConsentToResponseContext} unit test. */
 public class SetConsentToResponseContextTest extends BaseOIDCResponseActionTest {
 
     private SetConsentToResponseContext action;
 
-    private AttributeReleaseContext attrRelCtx;
+    private AttributeContext attributeCtx;
 
-    private ConsentContext consCtx;
-
-    private void init() throws ComponentInitializationException {
-        attrRelCtx = (AttributeReleaseContext) profileRequestCtx.addSubcontext(new AttributeReleaseContext());
-        attrRelCtx.getConsentableAttributes().put("1", null);
-        attrRelCtx.getConsentableAttributes().put("2", null);
-        consCtx = (ConsentContext) profileRequestCtx.addSubcontext(new ConsentContext());
-        Consent yes = new Consent();
-        yes.setApproved(true);
-        Consent no = new Consent();
-        no.setApproved(false);
-        consCtx.getPreviousConsents().put("1", yes);
-        consCtx.getPreviousConsents().put("2", no);
-        consCtx.getCurrentConsents().put("3", yes);
+    private void init(boolean encodeConsent, boolean consentEnabled) throws ComponentInitializationException {
+        attributeCtx = new AttributeContext();
+        final Set<IdPAttribute> attributes = new HashSet<>();
+        attributes.add(new IdPAttribute("1"));
+        attributes.add(new IdPAttribute("2"));
+        attributeCtx.setIdPAttributes(attributes);
+        rpCtx.addSubcontext(attributeCtx);
+        ((OIDCCoreProtocolConfiguration) rpCtx.getProfileConfig()).setEncodeConsentInTokens(encodeConsent);
         action = new SetConsentToResponseContext();
+        action.setConsentEnabledPredicate(consentEnabled ? Predicates.alwaysTrue() : Predicates.alwaysFalse());
+        
         action.initialize();
     }
-
+    
     /**
-     * Test that action handles no consent being available.
+     * Test that action doesn't create context when consent encoding flag in profile config is disabled.
      * 
      * @throws ComponentInitializationException
      */
     @Test
-    public void testSuccessNoConsent() throws ComponentInitializationException {
-        init();
-        profileRequestCtx.removeSubcontext(ConsentContext.class);
-        respCtx.removeSubcontext(OIDCAuthenticationResponseConsentContext.class);
+    public void testWithEncodingDisabled() throws ComponentInitializationException {
+        init(false, false);
         final Event event = action.execute(requestCtx);
         ActionTestingSupport.assertProceedEvent(event);
         Assert.assertNull(respCtx.getSubcontext(OIDCAuthenticationResponseConsentContext.class, false));
     }
 
     /**
-     * Test that action handles consent but not attrib release context being available.
+     * Test that action handles missing attribute context.
      * 
      * @throws ComponentInitializationException
      */
     @Test
-    public void testFailNoAttribRelConsent() throws ComponentInitializationException {
-        init();
-        profileRequestCtx.removeSubcontext(AttributeReleaseContext.class);
+    public void testFailNoAttributeContext() throws ComponentInitializationException {
+        init(true, true);
+        profileRequestCtx.getSubcontext(RelyingPartyContext.class).removeSubcontext(AttributeContext.class);
         final Event event = action.execute(requestCtx);
         ActionTestingSupport.assertEvent(event, EventIds.INVALID_PROFILE_CTX);
+        Assert.assertNull(respCtx.getSubcontext(OIDCAuthenticationResponseConsentContext.class, false));
     }
 
     /**
-     * Test that action handles basic success case.
+     * Test that action doesn't create context when attribute-release flow is not returned by the lookup function.
      * 
      * @throws ComponentInitializationException
      */
     @Test
-    public void testSuccess() throws ComponentInitializationException {
-        init();
+    public void testNoAttributeReleaseFlow() throws ComponentInitializationException {
+        init(true, false);
         final Event event = action.execute(requestCtx);
         ActionTestingSupport.assertProceedEvent(event);
-        OIDCAuthenticationResponseConsentContext ctx =
-                respCtx.getSubcontext(OIDCAuthenticationResponseConsentContext.class, false);
-        Assert.assertNotNull(ctx);
-        Assert.assertTrue(ctx.getConsentableAttributes().contains("1"));
-        Assert.assertTrue(ctx.getConsentableAttributes().contains("2"));
-        Assert.assertTrue(ctx.getConsentableAttributes().size() == 2);
-        Assert.assertTrue(ctx.getConsentedAttributes().contains("3"));
-        Assert.assertTrue(ctx.getConsentedAttributes().size() == 1);
+        Assert.assertNull(respCtx.getSubcontext(OIDCAuthenticationResponseConsentContext.class, false));
     }
 
     /**
-     * Test that action handles basic success case of having only previous consent.
+     * Test that action handles basic success case.
      * 
      * @throws ComponentInitializationException
      */
     @Test
-    public void testSuccessPrev() throws ComponentInitializationException {
-        init();
-        consCtx.getCurrentConsents().clear();
+    public void testSuccess() throws ComponentInitializationException {
+        init(true, true);
         final Event event = action.execute(requestCtx);
         ActionTestingSupport.assertProceedEvent(event);
         OIDCAuthenticationResponseConsentContext ctx =
                 respCtx.getSubcontext(OIDCAuthenticationResponseConsentContext.class, false);
         Assert.assertNotNull(ctx);
-        Assert.assertTrue(ctx.getConsentableAttributes().contains("1"));
-        Assert.assertTrue(ctx.getConsentableAttributes().contains("2"));
-        Assert.assertTrue(ctx.getConsentableAttributes().size() == 2);
         Assert.assertTrue(ctx.getConsentedAttributes().contains("1"));
-        Assert.assertTrue(ctx.getConsentedAttributes().size() == 1);
+        Assert.assertTrue(ctx.getConsentedAttributes().contains("2"));
+        Assert.assertTrue(ctx.getConsentedAttributes().size() == 2);
     }
 
 }
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetSubjectToResponseContextTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetSubjectToResponseContextTest.java
index eb5c0059..4cdef58e 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetSubjectToResponseContextTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/SetSubjectToResponseContextTest.java
@@ -17,9 +17,7 @@
 
 package net.shibboleth.idp.plugin.oidc.op.profile.impl;
 
-import net.shibboleth.idp.consent.context.ConsentContext;
 import net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.TokenRequestSubjectLookupFunction;
-import net.shibboleth.idp.plugin.oidc.op.profile.impl.SetSubjectToResponseContext;
 import net.shibboleth.idp.plugin.oidc.op.token.support.AccessTokenClaimsSet;
 import net.shibboleth.idp.plugin.oidc.op.token.support.TokenClaimsSet;
 import net.shibboleth.idp.profile.testing.ActionTestingSupport;
@@ -86,7 +84,6 @@ public class SetSubjectToResponseContextTest extends BaseOIDCResponseActionTest
                 "issuer", "userPrin", "subject", Instant.now(), Instant.now(), Instant.now(),
                 new URI("http://example.com"), new Scope()).build();
         respCtx.setTokenClaimsSet(claims);
-        profileRequestCtx.removeSubcontext(ConsentContext.class);
         final Event event = action.execute(requestCtx);
         ActionTestingSupport.assertProceedEvent(event);
         Assert.assertEquals(respCtx.getSubject(), "subject");
diff --git a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateGrantTest.java b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateGrantTest.java
index 635f7dbf..6c997f42 100644
--- a/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateGrantTest.java
+++ b/idp-oidc-extension-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/impl/ValidateGrantTest.java
@@ -95,20 +95,26 @@ public class ValidateGrantTest extends BaseOIDCResponseActionTest {
     }
 
     public static AuthorizationCode buildAuthorizationCode(String clientId, String issuer, String userPrincipal,
-            String sub, String callbackUrl, String codeChallenge)
+            String sub, String callbackUrl, String scope)
             throws URISyntaxException, NoSuchAlgorithmException, DataSealerException, ComponentInitializationException {
-        return buildAuthorizationCode(clientId, issuer, userPrincipal, sub, callbackUrl, codeChallenge, null, null, null);
+        return buildAuthorizationCode(clientId, issuer, userPrincipal, sub, callbackUrl, null, null);
+    }
+
+    public static AuthorizationCode buildAuthorizationCode(String clientId, String issuer, String userPrincipal,
+            String sub, String callbackUrl, String codeChallenge, String scope)
+            throws URISyntaxException, NoSuchAlgorithmException, DataSealerException, ComponentInitializationException {
+        return buildAuthorizationCode(clientId, issuer, userPrincipal, sub, callbackUrl, codeChallenge, null, null, null, null);
     }
 
     public static AuthorizationCode buildAuthorizationCode(String clientId, String issuer, String userPrincipal,
             String sub, String callbackUrl, String codeChallenge, JSONObject deliveryClaims,
-            JSONObject deliveryClaimsIDToken, JSONObject deliveryClaimsUserInfo)
+            JSONObject deliveryClaimsIDToken, JSONObject deliveryClaimsUserInfo, String scope)
             throws URISyntaxException, NoSuchAlgorithmException, DataSealerException, ComponentInitializationException {
         final Instant now = Instant.now();
         final ValidateGrantTest test = new ValidateGrantTest();
         final AuthorizeCodeClaimsSet.Builder builder = new AuthorizeCodeClaimsSet.Builder(
                 new SecureRandomIdentifierGenerationStrategy(), new ClientID(clientId), issuer, userPrincipal, sub,
-                now, now.plusSeconds(100), now, new URI(callbackUrl), new Scope());
+                now, now.plusSeconds(100), now, new URI(callbackUrl), scope == null ? new Scope() : Scope.parse(scope));
         if (codeChallenge != null) {
             builder.setCodeChallenge(codeChallenge);
         }
diff --git a/idp-oidc-extension-impl/src/test/resources/conf/idp.properties b/idp-oidc-extension-impl/src/test/resources/conf/idp.properties
index cd09a526..b4137101 100644
--- a/idp-oidc-extension-impl/src/test/resources/conf/idp.properties
+++ b/idp-oidc-extension-impl/src/test/resources/conf/idp.properties
@@ -125,6 +125,7 @@ idp.session.secondaryServiceIndex = true
 
 # Set to "shibboleth.StorageService" or custom bean for alternate storage of consent
 #idp.consent.StorageService = shibboleth.ClientPersistentStorageService
+idp.consent.StorageService = shibboleth.StorageService
 
 # Set to "shibboleth.consent.AttributeConsentStorageKey" to use an attribute
 # to key user consent storage records (and set the attribute name)
diff --git a/idp-oidc-extension-impl/src/test/resources/net/shibboleth/idp/oidc/metadata/impl/EntityDescriptor-with-oidcmd-clientsecret.xml b/idp-oidc-extension-impl/src/test/resources/net/shibboleth/idp/oidc/metadata/impl/EntityDescriptor-with-oidcmd-clientsecret.xml
index 7a35ea85..1630118b 100644
--- a/idp-oidc-extension-impl/src/test/resources/net/shibboleth/idp/oidc/metadata/impl/EntityDescriptor-with-oidcmd-clientsecret.xml
+++ b/idp-oidc-extension-impl/src/test/resources/net/shibboleth/idp/oidc/metadata/impl/EntityDescriptor-with-oidcmd-clientsecret.xml
@@ -1,5 +1,13 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" entityID="mockSamlClientId">
+    <md:Extensions xmlns:mdattr="urn:oasis:names:tc:SAML:metadata:attribute">
+        <mdattr:EntityAttributes xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">
+            <saml:Attribute Name="http://shibboleth.net/ns/profiles/oidc/sso/browser/alwaysIncludedAttributes"
+                NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
+                <saml:AttributeValue>mail</saml:AttributeValue>
+            </saml:Attribute>
+        </mdattr:EntityAttributes>
+    </md:Extensions>
     <md:SPSSODescriptor xmlns:oidcmd="urn:mace:shibboleth:metadata:oidc:1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" protocolSupportEnumeration="http://openid.net/specs/openid-connect-core-1_0.html">
         <md:KeyDescriptor>
             <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
@@ -16,7 +24,7 @@
                 response_types="code"
                 application_type="web"
                 token_endpoint_auth_method="client_secret_basic"
-                scopes="openid profile" />
+                scopes="openid profile email" />
         </md:Extensions>
         <md:AssertionConsumerService
                 Binding="https://tools.ietf.org/html/rfc6749#section-3.1.2"

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


More information about the commits mailing list