[java-idp-oidc] branch main updated: JOIDC-201 - Support for OAuth 2.0 Demonstrating Proof of Possession (DPoP)

Henri Mikkonen henri.mikkonen at iki.fi
Tue Aug 13 11:43:55 UTC 2024


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=c2355c6c2386a35eab785a6b1d48c5360bd9a189

The following commit(s) were added to refs/heads/main by this push:
     new c2355c6c JOIDC-201 - Support for OAuth 2.0 Demonstrating Proof of Possession (DPoP)
c2355c6c is described below

commit c2355c6c2386a35eab785a6b1d48c5360bd9a189
Author: Henri Mikkonen <henri.mikkonen at iki.fi>
AuthorDate: Tue Aug 13 14:43:15 2024 +0300

    JOIDC-201 - Support for OAuth 2.0 Demonstrating Proof of Possession (DPoP)
    
    https://shibboleth.atlassian.net/browse/JOIDC-201
    
    Add support for 'alwaysIssueBearerAccessToken' profile configuration option
    - If the flag is true, the DPoP proof thumbprint is not included to the access tokens
    - SetAccessTokenToResponseContext action exploits the flag when creating the access token object (with type)
---
 .../context/OIDCAuthenticationResponseContext.java | 24 ++++---
 .../op/oauth2/profile/impl/BuildAccessToken.java   | 24 ++++++-
 .../impl/SetAccessTokenToResponseContext.java      | 33 ++++++++-
 .../plugin/oidc/op/profile/flow/TokenFlowTest.java | 84 ++++++++++++++++++++++
 .../plugin/oidc/op/profile/flow/UserInfoTest.java  | 12 ++++
 .../shibboleth/idp/module/conf/relying-party.xml   | 16 +++++
 6 files changed, 177 insertions(+), 16 deletions(-)

diff --git a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/messaging/context/OIDCAuthenticationResponseContext.java b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/messaging/context/OIDCAuthenticationResponseContext.java
index ce7d7bac..3a9d5680 100644
--- a/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/messaging/context/OIDCAuthenticationResponseContext.java
+++ b/idp-oidc-extension-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/messaging/context/OIDCAuthenticationResponseContext.java
@@ -29,7 +29,6 @@ import com.nimbusds.oauth2.sdk.AuthorizationCode;
 import com.nimbusds.oauth2.sdk.Scope;
 import com.nimbusds.oauth2.sdk.token.AccessToken;
 import com.nimbusds.oauth2.sdk.token.BearerAccessToken;
-import com.nimbusds.oauth2.sdk.token.DPoPAccessToken;
 import com.nimbusds.oauth2.sdk.token.RefreshToken;
 import com.nimbusds.openid.connect.sdk.OIDCClaimsRequest;
 import com.nimbusds.openid.connect.sdk.claims.ACR;
@@ -476,17 +475,20 @@ public class OIDCAuthenticationResponseContext extends BaseContext {
     */
    public void setAccessToken(@Nullable final String token, @Nonnull final Duration lifeTime,
            @Nullable final Scope scope) {
-       if (token == null) {
-           accessToken = null;
-       } else {
-           if (getDpopProofJwkThumbprint() == null) {
-               accessToken = new BearerAccessToken(token, lifeTime.getSeconds(), scope);
-           } else {
-               accessToken = new DPoPAccessToken(token, lifeTime.getSeconds(), scope);
-           }
-       }
+       setAccessToken(token == null ? null : new BearerAccessToken(token, lifeTime.getSeconds(), scope));
    }
-   
+
+   /**
+    * Set access token.
+    * 
+    * @param token the access token
+    * 
+    * @since 4.2.0
+    */
+   public void setAccessToken(@Nullable final AccessToken token) {
+       accessToken = token;
+   }
+
    /**
     * Get refresh token.
     * 
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/BuildAccessToken.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/BuildAccessToken.java
index 4cf16f6d..383fc041 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/BuildAccessToken.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/BuildAccessToken.java
@@ -58,6 +58,7 @@ import net.shibboleth.idp.plugin.oidc.op.token.support.RefreshTokenClaimsSet;
 import net.shibboleth.idp.plugin.oidc.op.token.support.TokenClaimsSet;
 import net.shibboleth.idp.plugin.oidc.op.token.support.AccessTokenClaimsSet.Builder;
 import net.shibboleth.idp.profile.IdPEventIds;
+import net.shibboleth.oidc.profile.config.logic.AlwaysIssueBearerAccessTokenPredicate;
 import net.shibboleth.oidc.profile.config.logic.AttributeConsentFlowEnabledPredicate;
 import net.shibboleth.oidc.profile.config.navigate.AccessTokenClaimsSetManipulationStrategyLookupFunction;
 import net.shibboleth.oidc.profile.config.navigate.AccessTokenLifetimeLookupFunction;
@@ -145,6 +146,9 @@ public class BuildAccessToken extends AbstractOIDCResponseAction {
     private Function<ProfileRequestContext,BiFunction<ProfileRequestContext,Map<String,Object>,Map<String,Object>>>
         tokenClaimsSetManipulationStrategyLookupStrategy;
 
+    /** Condition used to determine whether to always issue bearer access token. */
+    @Nonnull private Predicate<ProfileRequestContext> alwaysIssueBearerAccessTokenCondition;
+
     /** The strategy used for manipulating the token claims set. */
     @Nullable private BiFunction<ProfileRequestContext,Map<String,Object>,Map<String,Object>> manipulationStrategy;
 
@@ -208,6 +212,7 @@ public class BuildAccessToken extends AbstractOIDCResponseAction {
         tokenClaimsSetManipulationStrategyLookupStrategy =
                 new AccessTokenClaimsSetManipulationStrategyLookupFunction();
         xmlSafeIdentifier = true;
+        alwaysIssueBearerAccessTokenCondition = new AlwaysIssueBearerAccessTokenPredicate();
     }
     
     /**
@@ -368,6 +373,18 @@ public class BuildAccessToken extends AbstractOIDCResponseAction {
         xmlSafeIdentifier = flag;
     }
 
+    /**
+     * Set the condition used to determine whether to always issue bearer access token.
+     * 
+     * @param condition condition to use
+     * 
+     * @since 4.2.0
+     */
+    public void setAlwaysIssueBearerAccessTokenCondition(@Nonnull final Predicate<ProfileRequestContext> condition) {
+        alwaysIssueBearerAccessTokenCondition =
+                Constraint.isNotNull(condition, "Always issue bearer access token condition cannot be null");
+    }
+
     /** {@inheritDoc} */
     @Override
     protected void doInitialize() throws ComponentInitializationException {
@@ -499,6 +516,9 @@ public class BuildAccessToken extends AbstractOIDCResponseAction {
         
         final AccessTokenClaimsSet.Builder builder;
 
+        final boolean alwaysBearerToken = alwaysIssueBearerAccessTokenCondition.test(profileRequestContext);
+        log.debug("{} Always issue bearer access token set to {}", getLogPrefix(), alwaysBearerToken);
+
         if (tokenClaimsSet != null) {
             final TokenClaimsSet nonNullClaimsSet = tokenClaimsSet;
             // We may not use original claims as input for scope / delivery claims as they may have been reduced.
@@ -514,7 +534,7 @@ public class BuildAccessToken extends AbstractOIDCResponseAction {
             assert idGenerator != null;
             builder.setJWTID(idGenerator, xmlSafeIdentifier);
             builder.setSessionIdentifier(responseCtx.getSessionId());
-            builder.setDpopProofJwkThumbprint(responseCtx.getDpopProofJwkThumbprint());
+            builder.setDpopProofJwkThumbprint(alwaysBearerToken ? null : responseCtx.getDpopProofJwkThumbprint());
             // Set root token identifier to contain jit from the claims set used for building the new token
             if (StringSupport.trimOrNull(nonNullClaimsSet.getRootTokenIdentifier()) == null) {
                 builder.setRootTokenIdentifier(nonNullClaimsSet.getID());
@@ -536,7 +556,7 @@ public class BuildAccessToken extends AbstractOIDCResponseAction {
                     .setConsentedClaims(consented)
                     .setConsentEnabled(consentEnabledPredicate.test(profileRequestContext))
                     .setSessionIdentifier(responseCtx.getSessionId())
-                    .setDpopProofJwkThumbprint(responseCtx.getDpopProofJwkThumbprint());
+                    .setDpopProofJwkThumbprint(alwaysBearerToken ? null : responseCtx.getDpopProofJwkThumbprint());
 
             assert subjectCtx != null;
             final String principal = subjectCtx.getPrincipalName();
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/SetAccessTokenToResponseContext.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/SetAccessTokenToResponseContext.java
index f1a245c0..7d876351 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/SetAccessTokenToResponseContext.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oauth2/profile/impl/SetAccessTokenToResponseContext.java
@@ -16,6 +16,7 @@ package net.shibboleth.idp.plugin.oidc.op.oauth2.profile.impl;
 
 import java.time.Duration;
 import java.util.function.Function;
+import java.util.function.Predicate;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
@@ -26,10 +27,13 @@ import org.opensaml.profile.context.navigate.OutboundMessageContextLookup;
 import org.slf4j.Logger;
 
 import com.nimbusds.jwt.JWT;
+import com.nimbusds.oauth2.sdk.token.BearerAccessToken;
+import com.nimbusds.oauth2.sdk.token.DPoPAccessToken;
 
 import net.shibboleth.idp.plugin.oidc.op.messaging.context.AccessTokenContext;
 import net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCAuthenticationResponseContext;
 import net.shibboleth.idp.plugin.oidc.op.profile.impl.AbstractOIDCResponseAction;
+import net.shibboleth.oidc.profile.config.logic.AlwaysIssueBearerAccessTokenPredicate;
 import net.shibboleth.shared.logic.Constraint;
 import net.shibboleth.shared.primitive.LoggerFactory;
 
@@ -48,7 +52,10 @@ public class SetAccessTokenToResponseContext extends AbstractOIDCResponseAction
 
     /** Strategy used to locate the subcontext with the token. */
     @Nonnull private Function<ProfileRequestContext,AccessTokenContext> accessTokenContextLookupStrategy;
-    
+
+    /** Condition used to determine whether to always issue bearer access token. */
+    @Nonnull private Predicate<ProfileRequestContext> alwaysIssueBearerAccessTokenCondition;
+
     /** Token context. */
     @Nullable private AccessTokenContext tokenCtx; 
     
@@ -61,6 +68,7 @@ public class SetAccessTokenToResponseContext extends AbstractOIDCResponseAction
                                 new OutboundMessageContextLookup()));
         assert atcls != null;
         accessTokenContextLookupStrategy = atcls;
+        alwaysIssueBearerAccessTokenCondition = new AlwaysIssueBearerAccessTokenPredicate();
     }
 
     /**
@@ -75,7 +83,19 @@ public class SetAccessTokenToResponseContext extends AbstractOIDCResponseAction
         accessTokenContextLookupStrategy =
                 Constraint.isNotNull(strategy, "AccessTokenContext lookup strategy cannot be null");
     }
-    
+
+    /**
+     * Set the condition used to determine whether to always issue bearer access token.
+     * 
+     * @param condition condition to use
+     * 
+     * @since 4.2.0
+     */
+    public void setAlwaysIssueBearerAccessTokenCondition(@Nonnull final Predicate<ProfileRequestContext> condition) {
+        alwaysIssueBearerAccessTokenCondition =
+                Constraint.isNotNull(condition, "Always issue bearer access token condition cannot be null");
+    }
+
     /** {@inheritDoc} */
     @Override
     protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
@@ -125,7 +145,14 @@ public class SetAccessTokenToResponseContext extends AbstractOIDCResponseAction
         assert oidcResponseContext != null;
         final Duration lifetime = nonNullTokenCtx.getLifetime();
         assert lifetime != null;
-        oidcResponseContext.setAccessToken(token, lifetime, oidcResponseContext.getScope());
+        final boolean alwaysBearer = alwaysIssueBearerAccessTokenCondition.test(profileRequestContext);
+        if (oidcResponseContext.getDpopProofJwkThumbprint() == null || alwaysBearer) {
+            oidcResponseContext.setAccessToken(
+                    new BearerAccessToken(token, lifetime.getSeconds(), oidcResponseContext.getScope()));
+        } else {
+            oidcResponseContext.setAccessToken(
+                    new DPoPAccessToken(token, lifetime.getSeconds(), oidcResponseContext.getScope()));
+        }
     }
 
 }
\ No newline at end of file
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 9b6a49ba..dd17561f 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
@@ -101,6 +101,8 @@ public class TokenFlowTest extends AbstractOidcClientAuthenticationFlowTest {
     String clientIdJwtRefreshToken = "mockClientIdNotMDDrivenRefreshTokenJwt";
     String clientIdDPoPAccessToken = "mockClientIdDPoPAccessToken";
     String clientIdDPoPJwtAccessToken = "mockClientIdDPoPJwtAccessToken";
+    String clientIdDPoPJwtBearerAccessToken = "mockClientIdDPoPJwtAlwaysBearerAccessToken";
+    String clientIdAlwaysBearerAccessToken = "mockClientIdAlwaysBearerAccessToken";
     String codeVerifier = "9234567812345678123456781234567812345678123456781234567812345678";
 
     String resourceUri = "https://rp.example.org";
@@ -135,6 +137,8 @@ public class TokenFlowTest extends AbstractOidcClientAuthenticationFlowTest {
         removeMetadata(storageService, clientIdNoIdTokenViaRefreshToken);
         removeMetadata(storageService, clientIdDPoPAccessToken);
         removeMetadata(storageService, clientIdDPoPJwtAccessToken);
+        removeMetadata(storageService, clientIdDPoPJwtBearerAccessToken);
+        removeMetadata(storageService, clientIdAlwaysBearerAccessToken);
     }
 
     @Test
@@ -747,6 +751,47 @@ public class TokenFlowTest extends AbstractOidcClientAuthenticationFlowTest {
         Assert.assertNotNull(response.getOIDCTokens().getIDToken());
     }
 
+    @Test
+    public void testDPoPValidGrantThumbprintIncludedMatchingProof_alwaysBearerToken() throws Exception {
+        final String clientId = clientIdAlwaysBearerAccessToken;
+        final AuthorizeCodeClaimsSet.Builder builder = new AuthorizeCodeClaimsSet.Builder();
+        final SignedJWT dpopProof = buildDPoPProof("POST", "http://localhost/idp/profile/oidc/token",
+                createValidDPoPNonce());
+        final String jkt = dpopProof.getHeader().getJWK().computeThumbprint().toString();
+        builder.setJWTID(new SecureRandomIdentifierGenerationStrategy())
+            .setClientID(new ClientID(clientId))
+            .setIssuer("https://op.example.org")
+            .setPrincipal("jdoe")
+            .setSubject("mock")
+            .setIssuedAt(Instant.now())
+            .setExpiresAt(Instant.now().plusSeconds(100))
+            .setAuthenticationTime(Instant.now())
+            .setRedirectURI(new URI(redirectUri))
+            .setScope(scope)
+            .setDpopProofJwkThumbprint(jkt);
+        final String authorizationCode = builder.build().serialize(getDataSealer());
+        request.addHeader("DPoP", dpopProof.serialize());
+
+        initializeGrantAndRequest(clientId, createRequestParameters(redirectUri,
+                "authorization_code",
+                authorizationCode, clientId));
+        storeConsent(storageService, "jdoe", clientId, "mail");
+        final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+        final OIDCTokenResponse response = parseSuccessResponse(result, OIDCTokenResponse.class);
+        final AccessToken accessToken = response.getTokens().getAccessToken();
+        Assert.assertNotNull(accessToken);
+        Assert.assertEquals(accessToken.getType(), AccessTokenType.BEARER);
+        final AccessTokenClaimsSet accessTokenClaims = AccessTokenClaimsSet.parse(accessToken.getValue(),
+                getDataSealer());
+        Assert.assertNull(accessTokenClaims.getDpopProofJwkThumbprint());
+        final RefreshToken refreshToken = response.getTokens().getRefreshToken();
+        Assert.assertNotNull(refreshToken);
+        final RefreshTokenClaimsSet refreshTokenClaims = RefreshTokenClaimsSet.parse(refreshToken.getValue(),
+                getDataSealer());
+        Assert.assertEquals(refreshTokenClaims.getDpopProofJwkThumbprint(), jkt);
+        Assert.assertNotNull(response.getOIDCTokens().getIDToken());
+    }
+
     @Test
     public void testDPoPValidGrantThumbprintIncludedMatchingProof_publicClient() throws Exception {
         final String clientId = clientIdPkcePlainPublic;
@@ -1162,6 +1207,45 @@ public class TokenFlowTest extends AbstractOidcClientAuthenticationFlowTest {
         Assert.assertNotNull(response.getOIDCTokens().getIDToken());
     }
 
+    @Test
+    public void testDPoPJwtEnforcedValidGrantThumbprintIncludedMatchingProof_alwaysBearerToken() throws Exception {
+        final String clientId = clientIdDPoPJwtBearerAccessToken;
+        final AuthorizeCodeClaimsSet.Builder builder = new AuthorizeCodeClaimsSet.Builder();
+        final SignedJWT dpopProof = buildDPoPProof("POST", "http://localhost/idp/profile/oidc/token",
+                createValidDPoPNonce());
+        builder.setJWTID(new SecureRandomIdentifierGenerationStrategy())
+            .setClientID(new ClientID(clientId))
+            .setIssuer("https://op.example.org")
+            .setPrincipal("jdoe")
+            .setSubject("mock")
+            .setIssuedAt(Instant.now())
+            .setExpiresAt(Instant.now().plusSeconds(100))
+            .setAuthenticationTime(Instant.now())
+            .setRedirectURI(new URI(redirectUri))
+            .setScope(scope)
+            .setDpopProofJwkThumbprint(dpopProof.getHeader().getJWK().computeThumbprint().toString());
+        final String authorizationCode = builder.build().serialize(getDataSealer());
+        request.addHeader("DPoP", dpopProof.serialize());
+
+        initializeGrantAndRequest(clientId, createRequestParameters(redirectUri,
+                "authorization_code",
+                authorizationCode, clientId));
+        storeConsent(storageService, "jdoe", clientId, "mail");
+        final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+        final OIDCTokenResponse response = parseSuccessResponse(result, OIDCTokenResponse.class);
+        Assert.assertNotNull(response.getTokens().getAccessToken());
+        Assert.assertEquals(response.getTokens().getAccessToken().getType(), AccessTokenType.BEARER);
+        final SignedJWT signedJwt = SignedJWT.parse(response.getTokens().getAccessToken().getValue());
+        Assert.assertNull(signedJwt.getJWTClaimsSet().getJSONObjectClaim("cnf"));
+        final RefreshToken refreshToken = response.getTokens().getRefreshToken();
+        Assert.assertNotNull(refreshToken);
+        final RefreshTokenClaimsSet refreshTokenClaims = RefreshTokenClaimsSet.parse(refreshToken.getValue(),
+                getDataSealer());
+        Assert.assertEquals(refreshTokenClaims.getDpopProofJwkThumbprint(),
+                dpopProof.getHeader().getJWK().computeThumbprint().toString());
+        Assert.assertNotNull(response.getOIDCTokens().getIDToken());
+    }
+
     @Test
     public void testDPoPJwtEnforcedValidGrantThumbprintNotIncluded() throws Exception {
         final String clientId = clientIdDPoPJwtAccessToken;
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 e90d1fb2..74d61b2c 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
@@ -407,6 +407,18 @@ public class UserInfoTest extends AbstractOidcApiFlowTest {
         Assert.assertNull(response.getUserInfoJWT());
     }
 
+    @Test
+    public void testFailWithBearerTokenWhenDPoPEnforced() throws URISyntaxException, NoSuchAlgorithmException, DataSealerException,
+        ComponentInitializationException, IOException {
+        final String clientId = "mockClientIdDPoPProofEnforced";
+        final BearerAccessToken token = buildToken(clientId, subject, new Scope("openid"));
+        storeMetadata(storageService, clientId, "mockSecret", scope);
+        request.addHeader("Authorization", getTokenHeaderValue(token));
+        final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+        assertInvalidDPoPToken(result);
+        removeMetadata(storageService, clientId);
+    }
+
     @SuppressWarnings("null")
     @Test
     public void testFailWithJWTDPoP_noAth() throws URISyntaxException, NoSuchAlgorithmException, DataSealerException,
diff --git a/idp-oidc-extension-impl/src/test/resources/net/shibboleth/idp/module/conf/relying-party.xml b/idp-oidc-extension-impl/src/test/resources/net/shibboleth/idp/module/conf/relying-party.xml
index c4c44acc..c75c3c7b 100644
--- a/idp-oidc-extension-impl/src/test/resources/net/shibboleth/idp/module/conf/relying-party.xml
+++ b/idp-oidc-extension-impl/src/test/resources/net/shibboleth/idp/module/conf/relying-party.xml
@@ -149,6 +149,15 @@
                  </list>
             </property>
         </bean>
+        <bean parent="RelyingPartyByName" c:relyingPartyIds="mockClientIdAlwaysBearerAccessToken">
+            <property name="profileConfigurations">
+                 <list>
+                     <bean parent="OAUTH2.Token.MDDriven" p:alwaysIssueBearerAccessToken="true"/>
+                     <bean parent="OIDC.UserInfo.MDDriven" />
+                     <bean parent="OAUTH2.Introspection.MDDriven"/>
+                 </list>
+            </property>
+        </bean>
         <bean parent="RelyingPartyByName" c:relyingPartyIds="mockClientIdRequestObjectEnforced">
             <property name="profileConfigurations">
                  <list>
@@ -263,6 +272,13 @@
                  </list>
             </property>
         </bean>
+        <bean parent="RelyingPartyByName" c:relyingPartyIds="mockClientIdDPoPJwtAlwaysBearerAccessToken">
+            <property name="profileConfigurations">
+                 <list>
+                     <bean parent="OAUTH2.Token.MDDriven" p:requireDpopProof="true" p:accessTokenType="JWT" p:encryptionOptional="true" p:alwaysIssueBearerAccessToken="true" />
+                 </list>
+            </property>
+        </bean>
         <bean parent="RelyingPartyByName" c:relyingPartyIds="mockClientIdNoRefreshTokensInSSOProfile">
             <property name="profileConfigurations">
                  <list>

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


More information about the commits mailing list