mirror of
https://github.com/keycloak/keycloak.git
synced 2025-12-20 05:50:08 -06:00
Promote DPoP feature to supported by default
Closes #42032 Signed-off-by: Giuseppe Graziano <g.graziano94@gmail.com>
This commit is contained in:
committed by
Marek Posolda
parent
14e4e1aed2
commit
e4114e6c74
@@ -106,7 +106,7 @@ public class Profile {
|
|||||||
|
|
||||||
FIPS("FIPS 140-2 mode", Type.DISABLED_BY_DEFAULT),
|
FIPS("FIPS 140-2 mode", Type.DISABLED_BY_DEFAULT),
|
||||||
|
|
||||||
DPOP("OAuth 2.0 Demonstrating Proof-of-Possession at the Application Layer", Type.PREVIEW),
|
DPOP("OAuth 2.0 Demonstrating Proof-of-Possession at the Application Layer", Type.DEFAULT),
|
||||||
|
|
||||||
DEVICE_FLOW("OAuth 2.0 Device Authorization Grant", Type.DEFAULT),
|
DEVICE_FLOW("OAuth 2.0 Device Authorization Grant", Type.DEFAULT),
|
||||||
|
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ import org.keycloak.services.CorsErrorResponseException;
|
|||||||
import org.keycloak.services.cors.Cors;
|
import org.keycloak.services.cors.Cors;
|
||||||
import org.keycloak.util.JWKSUtils;
|
import org.keycloak.util.JWKSUtils;
|
||||||
import org.keycloak.util.TokenUtil;
|
import org.keycloak.util.TokenUtil;
|
||||||
|
import org.keycloak.utils.StringUtil;
|
||||||
|
|
||||||
import static org.keycloak.OAuth2Constants.DPOP_JWT_HEADER_TYPE;
|
import static org.keycloak.OAuth2Constants.DPOP_JWT_HEADER_TYPE;
|
||||||
import static org.keycloak.OAuth2Constants.DPOP_HTTP_HEADER;
|
import static org.keycloak.OAuth2Constants.DPOP_HTTP_HEADER;
|
||||||
@@ -233,22 +234,26 @@ public class DPoPUtil {
|
|||||||
if (Profile.isFeatureEnabled(Profile.Feature.DPOP)) {
|
if (Profile.isFeatureEnabled(Profile.Feature.DPOP)) {
|
||||||
verifier = verifier.tokenType(List.of(TokenUtil.TOKEN_TYPE_BEARER, TokenUtil.TOKEN_TYPE_DPOP))
|
verifier = verifier.tokenType(List.of(TokenUtil.TOKEN_TYPE_BEARER, TokenUtil.TOKEN_TYPE_DPOP))
|
||||||
.withChecks(token -> {
|
.withChecks(token -> {
|
||||||
|
boolean isSchemeDPoP = false;
|
||||||
|
if (StringUtil.isNotBlank(validator.authHeader)) {
|
||||||
String[] split = WHITESPACES.split(validator.authHeader);
|
String[] split = WHITESPACES.split(validator.authHeader);
|
||||||
String typeString = split[0];
|
isSchemeDPoP = TokenUtil.TOKEN_TYPE_DPOP.equals(split[0]);
|
||||||
if (!typeString.equals(TokenUtil.TOKEN_TYPE_DPOP) && DPoPUtil.DPOP_TOKEN_TYPE.equals(token.getType())) {
|
}
|
||||||
|
|
||||||
|
if (!isSchemeDPoP && DPoPUtil.DPOP_TOKEN_TYPE.equals(token.getType())) {
|
||||||
throw new VerificationException("The access token type is DPoP but Authorization Header is not DPoP");
|
throw new VerificationException("The access token type is DPoP but Authorization Header is not DPoP");
|
||||||
}
|
}
|
||||||
if (typeString.equals(TokenUtil.TOKEN_TYPE_DPOP) && !DPoPUtil.DPOP_TOKEN_TYPE.equals(token.getType())) {
|
if (isSchemeDPoP && !DPoPUtil.DPOP_TOKEN_TYPE.equals(token.getType())) {
|
||||||
throw new VerificationException("The access token type is not DPoP but Authorization Header is DPoP");
|
throw new VerificationException("The access token type is not DPoP but Authorization Header is DPoP");
|
||||||
}
|
}
|
||||||
ClientModel clientModel = realm.getClientByClientId(token.getIssuedFor());
|
ClientModel clientModel = realm.getClientByClientId(token.getIssuedFor());
|
||||||
if (clientModel == null) {
|
if (clientModel == null) {
|
||||||
throw new VerificationException("Client not found");
|
throw new VerificationException("Client not found");
|
||||||
}
|
}
|
||||||
if (OIDCAdvancedConfigWrapper.fromClientModel(clientModel).isUseDPoP() && !typeString.equals(TokenUtil.TOKEN_TYPE_DPOP)) {
|
if (OIDCAdvancedConfigWrapper.fromClientModel(clientModel).isUseDPoP() && !isSchemeDPoP) {
|
||||||
throw new VerificationException("This client requires DPoP, but no DPoP Authorization header is present");
|
throw new VerificationException("This client requires DPoP, but no DPoP Authorization header is present");
|
||||||
}
|
}
|
||||||
if (typeString.equals(TokenUtil.TOKEN_TYPE_DPOP)) {
|
if (isSchemeDPoP) {
|
||||||
if (validator.accessToken == null) {
|
if (validator.accessToken == null) {
|
||||||
throw new VerificationException("Access Token not set for validator");
|
throw new VerificationException("Access Token not set for validator");
|
||||||
}
|
}
|
||||||
@@ -591,7 +596,6 @@ public class DPoPUtil {
|
|||||||
if (!isDPoPSupported) {
|
if (!isDPoPSupported) {
|
||||||
return super.transformAccessToken(token, mappingModel, session, userSession, clientSessionCtx);
|
return super.transformAccessToken(token, mappingModel, session, userSession, clientSessionCtx);
|
||||||
}
|
}
|
||||||
|
|
||||||
DPoP dPoP = session.getAttribute(DPOP_SESSION_ATTRIBUTE, DPoP.class);
|
DPoP dPoP = session.getAttribute(DPOP_SESSION_ATTRIBUTE, DPoP.class);
|
||||||
if (dPoP == null) {
|
if (dPoP == null) {
|
||||||
return super.transformAccessToken(token, mappingModel, session, userSession, clientSessionCtx);
|
return super.transformAccessToken(token, mappingModel, session, userSession, clientSessionCtx);
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ import org.keycloak.OAuthErrorException;
|
|||||||
import org.keycloak.admin.client.resource.ClientResource;
|
import org.keycloak.admin.client.resource.ClientResource;
|
||||||
import org.keycloak.authentication.authenticators.client.JWTClientAuthenticator;
|
import org.keycloak.authentication.authenticators.client.JWTClientAuthenticator;
|
||||||
import org.keycloak.authentication.authenticators.client.X509ClientAuthenticator;
|
import org.keycloak.authentication.authenticators.client.X509ClientAuthenticator;
|
||||||
import org.keycloak.common.Profile;
|
|
||||||
import org.keycloak.common.util.KeyUtils;
|
import org.keycloak.common.util.KeyUtils;
|
||||||
import org.keycloak.common.util.Time;
|
import org.keycloak.common.util.Time;
|
||||||
import org.keycloak.crypto.Algorithm;
|
import org.keycloak.crypto.Algorithm;
|
||||||
@@ -41,7 +40,6 @@ import org.keycloak.representations.AccessToken;
|
|||||||
import org.keycloak.representations.RefreshToken;
|
import org.keycloak.representations.RefreshToken;
|
||||||
import org.keycloak.representations.idm.ClientRepresentation;
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
import org.keycloak.testsuite.AssertEvents;
|
import org.keycloak.testsuite.AssertEvents;
|
||||||
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
|
||||||
import org.keycloak.testsuite.client.resources.TestApplicationResourceUrls;
|
import org.keycloak.testsuite.client.resources.TestApplicationResourceUrls;
|
||||||
import org.keycloak.testsuite.rest.resource.TestingOIDCEndpointsApplicationResource;
|
import org.keycloak.testsuite.rest.resource.TestingOIDCEndpointsApplicationResource;
|
||||||
import org.keycloak.testsuite.util.MutualTLSUtils;
|
import org.keycloak.testsuite.util.MutualTLSUtils;
|
||||||
@@ -65,7 +63,6 @@ import static org.keycloak.testsuite.util.ClientPoliciesUtil.createRsaJwk;
|
|||||||
import static org.keycloak.testsuite.util.ClientPoliciesUtil.generateEcdsaKey;
|
import static org.keycloak.testsuite.util.ClientPoliciesUtil.generateEcdsaKey;
|
||||||
import static org.keycloak.testsuite.util.ClientPoliciesUtil.generateSignedDPoPProof;
|
import static org.keycloak.testsuite.util.ClientPoliciesUtil.generateSignedDPoPProof;
|
||||||
|
|
||||||
@EnableFeature(value = Profile.Feature.DPOP, skipRestart = true)
|
|
||||||
public class FAPI2DPoPTest extends AbstractFAPI2Test {
|
public class FAPI2DPoPTest extends AbstractFAPI2Test {
|
||||||
|
|
||||||
private static final String DPOP_JWT_HEADER_TYPE = "dpop+jwt";
|
private static final String DPOP_JWT_HEADER_TYPE = "dpop+jwt";
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ import org.junit.Test;
|
|||||||
import org.keycloak.OAuth2Constants;
|
import org.keycloak.OAuth2Constants;
|
||||||
import org.keycloak.OAuthErrorException;
|
import org.keycloak.OAuthErrorException;
|
||||||
import org.keycloak.client.registration.ClientRegistrationException;
|
import org.keycloak.client.registration.ClientRegistrationException;
|
||||||
import org.keycloak.common.Profile;
|
|
||||||
import org.keycloak.common.util.SecretGenerator;
|
import org.keycloak.common.util.SecretGenerator;
|
||||||
import org.keycloak.common.util.Time;
|
import org.keycloak.common.util.Time;
|
||||||
import org.keycloak.crypto.Algorithm;
|
import org.keycloak.crypto.Algorithm;
|
||||||
@@ -39,7 +38,6 @@ import org.keycloak.representations.oidc.OIDCClientRepresentation;
|
|||||||
import org.keycloak.services.clientpolicy.ClientPolicyException;
|
import org.keycloak.services.clientpolicy.ClientPolicyException;
|
||||||
import org.keycloak.services.clientpolicy.condition.AnyClientConditionFactory;
|
import org.keycloak.services.clientpolicy.condition.AnyClientConditionFactory;
|
||||||
import org.keycloak.testsuite.AssertEvents;
|
import org.keycloak.testsuite.AssertEvents;
|
||||||
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
|
||||||
import org.keycloak.testsuite.client.resources.TestApplicationResourceUrls;
|
import org.keycloak.testsuite.client.resources.TestApplicationResourceUrls;
|
||||||
import org.keycloak.testsuite.util.ClientPoliciesUtil;
|
import org.keycloak.testsuite.util.ClientPoliciesUtil;
|
||||||
import org.keycloak.testsuite.util.oauth.AccessTokenResponse;
|
import org.keycloak.testsuite.util.oauth.AccessTokenResponse;
|
||||||
@@ -59,7 +57,6 @@ import static org.keycloak.testsuite.util.ClientPoliciesUtil.createEcJwk;
|
|||||||
import static org.keycloak.testsuite.util.ClientPoliciesUtil.generateEcdsaKey;
|
import static org.keycloak.testsuite.util.ClientPoliciesUtil.generateEcdsaKey;
|
||||||
import static org.keycloak.testsuite.util.ClientPoliciesUtil.generateSignedDPoPProof;
|
import static org.keycloak.testsuite.util.ClientPoliciesUtil.generateSignedDPoPProof;
|
||||||
|
|
||||||
@EnableFeature(value = Profile.Feature.DPOP, skipRestart = true)
|
|
||||||
public class OAuth2_1PublicClientTest extends AbstractFAPITest {
|
public class OAuth2_1PublicClientTest extends AbstractFAPITest {
|
||||||
|
|
||||||
private static final String OAUTH2_1_PUBLIC_CLIENT_PROFILE_NAME = "oauth-2-1-for-public-client";
|
private static final String OAUTH2_1_PUBLIC_CLIENT_PROFILE_NAME = "oauth-2-1-for-public-client";
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ import org.keycloak.admin.client.resource.ClientResource;
|
|||||||
import org.keycloak.client.registration.Auth;
|
import org.keycloak.client.registration.Auth;
|
||||||
import org.keycloak.client.registration.ClientRegistration;
|
import org.keycloak.client.registration.ClientRegistration;
|
||||||
import org.keycloak.client.registration.ClientRegistrationException;
|
import org.keycloak.client.registration.ClientRegistrationException;
|
||||||
import org.keycloak.common.Profile;
|
|
||||||
import org.keycloak.common.util.KeyUtils;
|
import org.keycloak.common.util.KeyUtils;
|
||||||
import org.keycloak.common.util.Time;
|
import org.keycloak.common.util.Time;
|
||||||
import org.keycloak.crypto.Algorithm;
|
import org.keycloak.crypto.Algorithm;
|
||||||
@@ -74,7 +73,6 @@ import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
|
|||||||
import org.keycloak.testsuite.Assert;
|
import org.keycloak.testsuite.Assert;
|
||||||
import org.keycloak.testsuite.AssertEvents;
|
import org.keycloak.testsuite.AssertEvents;
|
||||||
import org.keycloak.testsuite.admin.ApiUtil;
|
import org.keycloak.testsuite.admin.ApiUtil;
|
||||||
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
|
||||||
import org.keycloak.testsuite.broker.util.SimpleHttpDefault;
|
import org.keycloak.testsuite.broker.util.SimpleHttpDefault;
|
||||||
import org.keycloak.testsuite.util.AdminClientUtil;
|
import org.keycloak.testsuite.util.AdminClientUtil;
|
||||||
import org.keycloak.testsuite.util.ClientPoliciesUtil.ClientPoliciesBuilder;
|
import org.keycloak.testsuite.util.ClientPoliciesUtil.ClientPoliciesBuilder;
|
||||||
@@ -128,7 +126,6 @@ import static org.keycloak.testsuite.util.ClientPoliciesUtil.createRsaJwk;
|
|||||||
import static org.keycloak.testsuite.util.ClientPoliciesUtil.generateEcdsaKey;
|
import static org.keycloak.testsuite.util.ClientPoliciesUtil.generateEcdsaKey;
|
||||||
import static org.keycloak.testsuite.util.ClientPoliciesUtil.generateSignedDPoPProof;
|
import static org.keycloak.testsuite.util.ClientPoliciesUtil.generateSignedDPoPProof;
|
||||||
|
|
||||||
@EnableFeature(value = Profile.Feature.DPOP, skipRestart = true)
|
|
||||||
public class DPoPTest extends AbstractTestRealmKeycloakTest {
|
public class DPoPTest extends AbstractTestRealmKeycloakTest {
|
||||||
|
|
||||||
private static final String REALM_NAME = "test";
|
private static final String REALM_NAME = "test";
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ import org.keycloak.OAuth2Constants;
|
|||||||
import org.keycloak.OAuthErrorException;
|
import org.keycloak.OAuthErrorException;
|
||||||
import org.keycloak.admin.client.resource.ClientResource;
|
import org.keycloak.admin.client.resource.ClientResource;
|
||||||
import org.keycloak.client.registration.ClientRegistrationException;
|
import org.keycloak.client.registration.ClientRegistrationException;
|
||||||
import org.keycloak.common.Profile;
|
|
||||||
import org.keycloak.common.util.Base64Url;
|
import org.keycloak.common.util.Base64Url;
|
||||||
import org.keycloak.common.util.KeyUtils;
|
import org.keycloak.common.util.KeyUtils;
|
||||||
import org.keycloak.common.util.Time;
|
import org.keycloak.common.util.Time;
|
||||||
@@ -48,7 +47,6 @@ import org.keycloak.representations.idm.RealmRepresentation;
|
|||||||
import org.keycloak.representations.oidc.OIDCClientRepresentation;
|
import org.keycloak.representations.oidc.OIDCClientRepresentation;
|
||||||
import org.keycloak.testsuite.AssertEvents;
|
import org.keycloak.testsuite.AssertEvents;
|
||||||
import org.keycloak.testsuite.admin.ApiUtil;
|
import org.keycloak.testsuite.admin.ApiUtil;
|
||||||
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
|
||||||
|
|
||||||
import org.keycloak.testsuite.client.policies.AbstractClientPoliciesTest;
|
import org.keycloak.testsuite.client.policies.AbstractClientPoliciesTest;
|
||||||
import org.keycloak.testsuite.client.resources.TestApplicationResourceUrls;
|
import org.keycloak.testsuite.client.resources.TestApplicationResourceUrls;
|
||||||
@@ -68,7 +66,6 @@ import static org.keycloak.testsuite.util.ClientPoliciesUtil.createRsaJwk;
|
|||||||
import static org.keycloak.testsuite.util.ClientPoliciesUtil.generateEcdsaKey;
|
import static org.keycloak.testsuite.util.ClientPoliciesUtil.generateEcdsaKey;
|
||||||
import static org.keycloak.testsuite.util.ClientPoliciesUtil.generateSignedDPoPProof;
|
import static org.keycloak.testsuite.util.ClientPoliciesUtil.generateSignedDPoPProof;
|
||||||
|
|
||||||
@EnableFeature(value = Profile.Feature.DPOP, skipRestart = true)
|
|
||||||
public class ParWithDPoPTest extends AbstractClientPoliciesTest {
|
public class ParWithDPoPTest extends AbstractClientPoliciesTest {
|
||||||
@Rule
|
@Rule
|
||||||
public AssertEvents events = new AssertEvents(this);
|
public AssertEvents events = new AssertEvents(this);
|
||||||
|
|||||||
@@ -52,7 +52,6 @@ import org.keycloak.services.resources.RealmsResource;
|
|||||||
import org.keycloak.testsuite.AbstractKeycloakTest;
|
import org.keycloak.testsuite.AbstractKeycloakTest;
|
||||||
import org.keycloak.testsuite.Assert;
|
import org.keycloak.testsuite.Assert;
|
||||||
import org.keycloak.testsuite.AbstractAdminTest;
|
import org.keycloak.testsuite.AbstractAdminTest;
|
||||||
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
|
||||||
import org.keycloak.testsuite.broker.util.SimpleHttpDefault;
|
import org.keycloak.testsuite.broker.util.SimpleHttpDefault;
|
||||||
import org.keycloak.testsuite.forms.BrowserFlowTest;
|
import org.keycloak.testsuite.forms.BrowserFlowTest;
|
||||||
import org.keycloak.testsuite.forms.LevelOfAssuranceFlowTest;
|
import org.keycloak.testsuite.forms.LevelOfAssuranceFlowTest;
|
||||||
@@ -222,10 +221,6 @@ public abstract class AbstractWellKnownProviderTest extends AbstractKeycloakTest
|
|||||||
// frontchannel logout
|
// frontchannel logout
|
||||||
assertTrue(oidcConfig.getFrontChannelLogoutSessionSupported());
|
assertTrue(oidcConfig.getFrontChannelLogoutSessionSupported());
|
||||||
assertTrue(oidcConfig.getFrontChannelLogoutSupported());
|
assertTrue(oidcConfig.getFrontChannelLogoutSupported());
|
||||||
|
|
||||||
// DPoP - negative test for preview profile - see testDpopSigningAlgValuesSupportedWithDpop for actual test
|
|
||||||
assertNull("dpop_signing_alg_values_supported should not be present unless DPoP feature is enabled",
|
|
||||||
oidcConfig.getDpopSigningAlgValuesSupported());
|
|
||||||
} finally {
|
} finally {
|
||||||
client.close();
|
client.close();
|
||||||
}
|
}
|
||||||
@@ -380,7 +375,6 @@ public abstract class AbstractWellKnownProviderTest extends AbstractKeycloakTest
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@EnableFeature(value = Profile.Feature.DPOP, skipRestart = true)
|
|
||||||
public void testDpopSigningAlgValuesSupportedWithDpop() throws IOException {
|
public void testDpopSigningAlgValuesSupportedWithDpop() throws IOException {
|
||||||
Client client = AdminClientUtil.createResteasyClient();
|
Client client = AdminClientUtil.createResteasyClient();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user