Supporting OAuth 2.1 for public clients
closes #25316 Co-authored-by: shigeyuki kabano <shigeyuki.kabano.sj@hitachi.com> Signed-off-by: Takashi Norimatsu <takashi.norimatsu.ws@hitachi.com>
This commit is contained in:
parent
17a4902c4a
commit
1e12b15890
9 changed files with 334 additions and 14 deletions
|
@ -12,6 +12,8 @@ side may need to be still done manually or through some other third-party soluti
|
|||
==== OAuth 2.1 client profiles
|
||||
|
||||
To make sure that your clients are OAuth 2.1 compliant, you can configure Client Policies in your realm as described in the link:{adminguide_link}#_client_policies[{adminguide_name}]
|
||||
and link them to the global client profiles for OAuth 2.1 support, which are automatically available in each realm. You can use `oauth-2-1-for-confidential-client` profile for confidential clients.
|
||||
and link them to the global client profiles for OAuth 2.1 support, which are automatically available in each realm. You can use either `oauth-2-1-for-confidential-client` profile for confidential clients or `oauth-2-1-for-public-client` profile for public clients.
|
||||
|
||||
NOTE: OAuth 2.1 specification is still a draft and it may change in the future. Hence the {project_name} built-in OAuth 2.1 client profiles can change as well.
|
||||
|
||||
NOTE: When using OAuth 2.1 profile for public clients, it is recommended to use DPoP preview feature as described in the link:{adminguide_link}#_dpop-bound-tokens[{adminguide_name}] because DPoP binds an access token and a refresh token together with the public part of a client's key pair. This binding prevents an attacker from using stolen tokens.
|
|
@ -85,6 +85,12 @@ public class DPoPBindEnforcerExecutor implements ClientPolicyExecutorProvider<DP
|
|||
|
||||
@Override
|
||||
public void executeOnEvent(ClientPolicyContext context) throws ClientPolicyException {
|
||||
if (!Profile.isFeatureEnabled(Feature.DPOP)) {
|
||||
logger.warnf("DPoP executor is used, but DPOP feature is disabled. So DPOP is not enforced for the clients. " +
|
||||
"Please enable DPOP feature in order to be able to have DPOP checks applied.");
|
||||
return;
|
||||
}
|
||||
|
||||
HttpRequest request = session.getContext().getHttpRequest();
|
||||
switch (context.getEvent()) {
|
||||
case REGISTER:
|
||||
|
@ -105,7 +111,6 @@ public class DPoPBindEnforcerExecutor implements ClientPolicyExecutorProvider<DP
|
|||
break;
|
||||
case TOKEN_REVOKE:
|
||||
checkTokenRevoke((TokenRevokeContext) context, request);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ import org.keycloak.models.KeycloakSessionFactory;
|
|||
import org.keycloak.provider.EnvironmentDependentProviderFactory;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
|
||||
public class DPoPBindEnforcerExecutorFactory implements ClientPolicyExecutorProviderFactory, EnvironmentDependentProviderFactory {
|
||||
public class DPoPBindEnforcerExecutorFactory implements ClientPolicyExecutorProviderFactory {
|
||||
|
||||
public static final String PROVIDER_ID = "dpop-bind-enforcer";
|
||||
|
||||
|
@ -68,9 +68,4 @@ public class DPoPBindEnforcerExecutorFactory implements ClientPolicyExecutorPro
|
|||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
return Collections.singletonList(AUTO_CONFIGURE_PROPERTY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSupported() {
|
||||
return Profile.isFeatureEnabled(Feature.DPOP);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -336,6 +336,44 @@
|
|||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "oauth-2-1-for-public-client",
|
||||
"description": "Client profile, which enforce public clients to conform 'OAuth 2.1' specification.",
|
||||
"executors": [
|
||||
{
|
||||
"executor": "secure-redirect-uris-enforcer",
|
||||
"configuration": {
|
||||
"allow-ipv4-loopback-address": "true",
|
||||
"allow-ipv6-loopback-address": "true",
|
||||
"allow-private-use-uri-scheme": "true"
|
||||
}
|
||||
},
|
||||
{
|
||||
"executor": "pkce-enforcer",
|
||||
"configuration": {
|
||||
"auto-configure": "true"
|
||||
}
|
||||
},
|
||||
{
|
||||
"executor": "dpop-bind-enforcer",
|
||||
"configuration": {
|
||||
"auto-configure": "true"
|
||||
}
|
||||
},
|
||||
{
|
||||
"executor": "reject-implicit-grant",
|
||||
"configuration": {
|
||||
"auto-configure": "true"
|
||||
}
|
||||
},
|
||||
{
|
||||
"executor": "reject-ropc-grant",
|
||||
"configuration": {
|
||||
"auto-configure": "true"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,280 @@
|
|||
/*
|
||||
* Copyright 2024 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed 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 org.keycloak.testsuite.client;
|
||||
|
||||
import jakarta.ws.rs.HttpMethod;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.OAuthErrorException;
|
||||
import org.keycloak.client.registration.ClientRegistrationException;
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.common.util.SecretGenerator;
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.crypto.Algorithm;
|
||||
import org.keycloak.jose.jwk.JWK;
|
||||
import org.keycloak.jose.jws.JWSHeader;
|
||||
import org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper;
|
||||
import org.keycloak.protocol.oidc.utils.OIDCResponseType;
|
||||
import org.keycloak.protocol.oidc.utils.PkceUtils;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.oidc.OIDCClientRepresentation;
|
||||
import org.keycloak.representations.oidc.TokenMetadataRepresentation;
|
||||
import org.keycloak.services.clientpolicy.ClientPolicyException;
|
||||
import org.keycloak.testsuite.AssertEvents;
|
||||
import org.keycloak.services.clientpolicy.condition.AnyClientConditionFactory;
|
||||
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
||||
import org.keycloak.testsuite.util.ClientPoliciesUtil;
|
||||
import org.keycloak.testsuite.util.Matchers;
|
||||
import org.keycloak.testsuite.util.OAuthClient;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
import java.security.KeyPair;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.keycloak.testsuite.util.ClientPoliciesUtil.createAnyClientConditionConfig;
|
||||
import static org.keycloak.testsuite.util.ClientPoliciesUtil.createEcJwk;
|
||||
import static org.keycloak.testsuite.util.ClientPoliciesUtil.generateEcdsaKey;
|
||||
import static org.keycloak.testsuite.util.ClientPoliciesUtil.generateSignedDPoPProof;
|
||||
|
||||
@EnableFeature(value = Profile.Feature.DPOP, skipRestart = true)
|
||||
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 DPOP_JWT_HEADER_TYPE = "dpop+jwt";
|
||||
|
||||
private KeyPair ecKeyPair;
|
||||
|
||||
private JWK jwkEc;
|
||||
|
||||
@Before
|
||||
public void beforeDPoPTest() throws Exception {
|
||||
ecKeyPair = generateEcdsaKey("secp256r1");
|
||||
jwkEc = createEcJwk(ecKeyPair.getPublic());
|
||||
}
|
||||
|
||||
@After
|
||||
public void revertPolicies() throws ClientPolicyException {
|
||||
oauth.openid(true);
|
||||
oauth.responseType(OIDCResponseType.CODE);
|
||||
oauth.nonce(null);
|
||||
oauth.codeChallenge(null);
|
||||
oauth.codeChallengeMethod(null);
|
||||
oauth.dpopProof(null);
|
||||
updatePolicies("{}");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOAuth2_1NotAllowImplicitGrant() throws Exception {
|
||||
String clientId = generateSuffixedName(CLIENT_NAME);
|
||||
|
||||
createClientByAdmin(clientId, (ClientRepresentation clientRep) -> {
|
||||
clientRep.setStandardFlowEnabled(Boolean.TRUE);
|
||||
clientRep.setImplicitFlowEnabled(Boolean.TRUE);
|
||||
clientRep.setPublicClient(Boolean.TRUE);
|
||||
});
|
||||
|
||||
// setup profiles and policies
|
||||
setupPolicyOAuth2_1PublicClientForAllClient();
|
||||
|
||||
setValidPkce(clientId);
|
||||
|
||||
// implicit grant
|
||||
testProhibitedImplicitOrHybridFlow(false, OIDCResponseType.TOKEN, generateNonce()
|
||||
);
|
||||
|
||||
// hybrid grant
|
||||
testProhibitedImplicitOrHybridFlow(true, OIDCResponseType.TOKEN + " " + OIDCResponseType.ID_TOKEN,
|
||||
generateNonce());
|
||||
|
||||
// hybrid grant
|
||||
testProhibitedImplicitOrHybridFlow(true, OIDCResponseType.TOKEN + " " + OIDCResponseType.CODE,
|
||||
generateNonce());
|
||||
|
||||
// hybrid grant
|
||||
testProhibitedImplicitOrHybridFlow(true, OIDCResponseType.TOKEN + " " + OIDCResponseType.CODE + " " + OIDCResponseType.ID_TOKEN,
|
||||
generateNonce());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOAuth2_1NotAllowResourceOwnerPasswordCredentialsGrant() throws Exception {
|
||||
String clientId = generateSuffixedName(CLIENT_NAME);
|
||||
|
||||
createClientByAdmin(clientId, (ClientRepresentation clientRep) -> {
|
||||
clientRep.setStandardFlowEnabled(Boolean.TRUE);
|
||||
clientRep.setPublicClient(Boolean.TRUE);
|
||||
});
|
||||
|
||||
// setup profiles and policies
|
||||
setupPolicyOAuth2_1PublicClientForAllClient();
|
||||
|
||||
oauth.clientId(clientId);
|
||||
OAuthClient.AccessTokenResponse response = oauth.doGrantAccessTokenRequest(null, TEST_USER_NAME, TEST_USER_PASSWORD, null);
|
||||
|
||||
assertEquals(400, response.getStatusCode());
|
||||
assertEquals(OAuthErrorException.INVALID_GRANT, response.getError());
|
||||
assertEquals("resource owner password credentials grant is prohibited.", response.getErrorDescription());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOAuth2_1ProofKeyForCodeExchange() throws Exception {
|
||||
// setup profiles and policies
|
||||
setupPolicyOAuth2_1PublicClientForAllClient();
|
||||
|
||||
String clientId = generateSuffixedName(CLIENT_NAME);
|
||||
String cId = createClientByAdmin(clientId, (ClientRepresentation clientRep) -> {
|
||||
clientRep.setPublicClient(Boolean.TRUE);
|
||||
clientRep.setRedirectUris(List.of(AssertEvents.DEFAULT_REDIRECT_URI));
|
||||
});
|
||||
assertEquals(OAuth2Constants.PKCE_METHOD_S256, OIDCAdvancedConfigWrapper.fromClientRepresentation(getClientByAdmin(cId)).getPkceCodeChallengeMethod());
|
||||
|
||||
failLoginByNotFollowingPKCE(clientId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOAuth2_1RedirectUris() throws Exception {
|
||||
// setup profiles and policies
|
||||
setupPolicyOAuth2_1PublicClientForAllClient();
|
||||
|
||||
// registration with invalid redirect_uri - fail
|
||||
try {
|
||||
createClientDynamically(generateSuffixedName(CLIENT_NAME), (OIDCClientRepresentation clientRep) ->
|
||||
clientRep.setRedirectUris(List.of("http://example.com/app")));
|
||||
} catch (ClientRegistrationException cre) {
|
||||
assertEquals(ERR_MSG_CLIENT_REG_FAIL, cre.getMessage());
|
||||
}
|
||||
|
||||
// registration with valid redirect_uri- success
|
||||
String clientId = generateSuffixedName(CLIENT_NAME);
|
||||
String cId = createClientByAdmin(clientId, (ClientRepresentation clientRep) -> {
|
||||
clientRep.setPublicClient(Boolean.TRUE);
|
||||
clientRep.setRedirectUris(List.of(AssertEvents.DEFAULT_REDIRECT_URI));
|
||||
});
|
||||
assertEquals(AssertEvents.DEFAULT_REDIRECT_URI, getClientByAdmin(cId).getRedirectUris().get(0));
|
||||
|
||||
// update with valid redirect_uri - fail
|
||||
try {
|
||||
createClientDynamically(clientId, (OIDCClientRepresentation clientRep) ->
|
||||
clientRep.setRedirectUris(List.of("https://localhost/app")));
|
||||
} catch (ClientRegistrationException cre) {
|
||||
assertEquals(ERR_MSG_CLIENT_REG_FAIL, cre.getMessage());
|
||||
}
|
||||
|
||||
// authorization with invalid redirect_uri request - fail
|
||||
setValidPkce(clientId);
|
||||
oauth.redirectUri("https://localhost/app");
|
||||
oauth.openLoginForm();
|
||||
assertTrue(errorPage.isCurrent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOAuth2_1DPoPSenderConstrainedToken() throws Exception {
|
||||
// setup profiles and policies
|
||||
setupPolicyOAuth2_1PublicClientForAllClient();
|
||||
|
||||
// registration (auto-config) - success
|
||||
String clientId = generateSuffixedName(CLIENT_NAME);
|
||||
String cId = createClientByAdmin(clientId, (ClientRepresentation clientRep) -> {
|
||||
clientRep.setPublicClient(Boolean.TRUE);
|
||||
clientRep.setRedirectUris(List.of(AssertEvents.DEFAULT_REDIRECT_URI));
|
||||
});
|
||||
assertTrue(OIDCAdvancedConfigWrapper.fromClientRepresentation(getClientByAdmin(cId)).isUseDPoP());
|
||||
|
||||
// authorization request - success
|
||||
setValidPkce(clientId);
|
||||
oauth.clientId(clientId);
|
||||
oauth.doLogin(TEST_USER_NAME, TEST_USER_PASSWORD);
|
||||
|
||||
// token request with DPoP Proof - success
|
||||
JWSHeader jwsEcHeader = new JWSHeader(org.keycloak.jose.jws.Algorithm.ES256, DPOP_JWT_HEADER_TYPE, jwkEc.getKeyId(), jwkEc);
|
||||
String dpopProofEcEncoded = generateSignedDPoPProof(UUID.randomUUID().toString(), HttpMethod.POST, oauth.getAccessTokenUrl(), (long) Time.currentTime(), Algorithm.ES256, jwsEcHeader, ecKeyPair.getPrivate());
|
||||
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
|
||||
oauth.dpopProof(dpopProofEcEncoded);
|
||||
OAuthClient.AccessTokenResponse response = oauth.doAccessTokenRequest(code, null);
|
||||
assertEquals(Response.Status.OK.getStatusCode(), response.getStatusCode());
|
||||
oauth.verifyToken(response.getAccessToken());
|
||||
|
||||
// token refresh request with DPoP Proof - success
|
||||
dpopProofEcEncoded = generateSignedDPoPProof(UUID.randomUUID().toString(), HttpMethod.POST, oauth.getAccessTokenUrl(), (long) Time.currentTime(), Algorithm.ES256, jwsEcHeader, ecKeyPair.getPrivate());
|
||||
oauth.dpopProof(dpopProofEcEncoded);
|
||||
response = oauth.doRefreshTokenRequest(response.getRefreshToken(), null);
|
||||
assertEquals(Response.Status.OK.getStatusCode(), response.getStatusCode());
|
||||
|
||||
// userinfo request with DPoP Proof - success
|
||||
dpopProofEcEncoded = generateSignedDPoPProof(UUID.randomUUID().toString(), HttpMethod.GET, oauth.getUserInfoUrl(), (long) Time.currentTime(), Algorithm.ES256, jwsEcHeader, ecKeyPair.getPrivate());
|
||||
oauth.dpopProof(dpopProofEcEncoded);
|
||||
OAuthClient.UserInfoResponse userInfoResponse = oauth.doUserInfoRequestByGet(response.getAccessToken());
|
||||
assertEquals(TEST_USER_NAME, userInfoResponse.getUserInfo().getPreferredUsername());
|
||||
|
||||
oauth.idTokenHint(response.getIdToken()).openLogout();
|
||||
|
||||
// revoke token with a valid DPoP proof - success
|
||||
dpopProofEcEncoded = generateSignedDPoPProof(UUID.randomUUID().toString(), HttpMethod.POST, oauth.getTokenRevocationUrl(), (long) Time.currentTime(), Algorithm.ES256, jwsEcHeader, ecKeyPair.getPrivate());
|
||||
oauth.dpopProof(dpopProofEcEncoded);
|
||||
CloseableHttpResponse closableHttpResponse = oauth.doTokenRevoke(response.getAccessToken(), "access_token", null);
|
||||
assertThat(closableHttpResponse, Matchers.statusCodeIsHC(Response.Status.OK));
|
||||
String introspectionResponse = oauth.introspectAccessTokenWithClientCredential(clientId, null, response.getAccessToken());
|
||||
TokenMetadataRepresentation tokenMetadataRepresentation = JsonSerialization.readValue(introspectionResponse, TokenMetadataRepresentation.class);
|
||||
assertFalse(tokenMetadataRepresentation.isActive());
|
||||
|
||||
oauth.idTokenHint(response.getIdToken()).openLogout();
|
||||
}
|
||||
|
||||
private void setupPolicyOAuth2_1PublicClientForAllClient() throws Exception {
|
||||
String json = (new ClientPoliciesUtil.ClientPoliciesBuilder()).addPolicy(
|
||||
(new ClientPoliciesUtil.ClientPolicyBuilder()).createPolicy("MyPolicy", "Policy for enable OAuth 2.1 public client profile for all clients", Boolean.TRUE)
|
||||
.addCondition(AnyClientConditionFactory.PROVIDER_ID,
|
||||
createAnyClientConditionConfig())
|
||||
.addProfile(OAUTH2_1_PUBLIC_CLIENT_PROFILE_NAME)
|
||||
.toRepresentation()
|
||||
).toString();
|
||||
updatePolicies(json);
|
||||
}
|
||||
|
||||
private void testProhibitedImplicitOrHybridFlow(boolean isOpenid, String responseType, String nonce) {
|
||||
oauth.openid(isOpenid);
|
||||
oauth.responseType(responseType);
|
||||
oauth.nonce(nonce);
|
||||
oauth.openLoginForm();
|
||||
assertEquals(OAuthErrorException.INVALID_REQUEST, oauth.getCurrentFragment().get(OAuth2Constants.ERROR));
|
||||
assertEquals("Implicit/Hybrid flow is prohibited.", oauth.getCurrentFragment().get(OAuth2Constants.ERROR_DESCRIPTION));
|
||||
}
|
||||
|
||||
private void setValidPkce(String clientId) throws Exception {
|
||||
oauth.clientId(clientId);
|
||||
String codeVerifier = PkceUtils.generateCodeVerifier();
|
||||
String codeChallenge = generateS256CodeChallenge(codeVerifier);
|
||||
oauth.codeChallenge(codeChallenge);
|
||||
oauth.codeChallengeMethod(OAuth2Constants.PKCE_METHOD_S256);
|
||||
oauth.codeVerifier(codeVerifier);
|
||||
}
|
||||
|
||||
private String generateNonce() {
|
||||
return SecretGenerator.getInstance().randomString(16);
|
||||
}
|
||||
}
|
|
@ -202,8 +202,8 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest {
|
|||
protected static final String FAPI_CIBA_PROFILE_NAME = "fapi-ciba";
|
||||
protected static final String FAPI2_SECURITY_PROFILE_NAME = "fapi-2-security-profile";
|
||||
protected static final String FAPI2_MESSAGE_SIGNING_PROFILE_NAME = "fapi-2-message-signing";
|
||||
|
||||
protected static final String OAUTH2_1_CONFIDENTIAL_CLIENT_PROFILE_NAME = "oauth-2-1-for-confidential-client";
|
||||
protected static final String OAUTH2_1_PUBLIC_CLIENT_PROFILE_NAME = "oauth-2-1-for-public-client";
|
||||
|
||||
protected static final String ERR_MSG_MISSING_NONCE = "Missing parameter: nonce";
|
||||
protected static final String ERR_MSG_MISSING_STATE = "Missing parameter: state";
|
||||
|
@ -338,7 +338,7 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest {
|
|||
ClientProfilesRepresentation actualProfilesRep = getProfilesWithGlobals();
|
||||
|
||||
// same profiles
|
||||
assertExpectedProfiles(actualProfilesRep, Arrays.asList(FAPI1_BASELINE_PROFILE_NAME, FAPI1_ADVANCED_PROFILE_NAME, FAPI_CIBA_PROFILE_NAME, FAPI2_SECURITY_PROFILE_NAME, FAPI2_MESSAGE_SIGNING_PROFILE_NAME, OAUTH2_1_CONFIDENTIAL_CLIENT_PROFILE_NAME), Arrays.asList("ordinal-test-profile", "lack-of-builtin-field-test-profile"));
|
||||
assertExpectedProfiles(actualProfilesRep, Arrays.asList(FAPI1_BASELINE_PROFILE_NAME, FAPI1_ADVANCED_PROFILE_NAME, FAPI_CIBA_PROFILE_NAME, FAPI2_SECURITY_PROFILE_NAME, FAPI2_MESSAGE_SIGNING_PROFILE_NAME, OAUTH2_1_CONFIDENTIAL_CLIENT_PROFILE_NAME, OAUTH2_1_PUBLIC_CLIENT_PROFILE_NAME), Arrays.asList("ordinal-test-profile", "lack-of-builtin-field-test-profile"));
|
||||
|
||||
// each profile - fapi-1-baseline
|
||||
ClientProfileRepresentation actualProfileRep = getProfileRepresentation(actualProfilesRep, FAPI1_BASELINE_PROFILE_NAME, true);
|
||||
|
|
|
@ -84,7 +84,7 @@ public class ClientPoliciesLoadUpdateTest extends AbstractClientPoliciesTest {
|
|||
ClientProfilesRepresentation actualProfilesRep = getProfilesWithGlobals();
|
||||
|
||||
// same profiles
|
||||
assertExpectedProfiles(actualProfilesRep, Arrays.asList(FAPI1_BASELINE_PROFILE_NAME, FAPI1_ADVANCED_PROFILE_NAME, FAPI_CIBA_PROFILE_NAME, FAPI2_SECURITY_PROFILE_NAME, FAPI2_MESSAGE_SIGNING_PROFILE_NAME, OAUTH2_1_CONFIDENTIAL_CLIENT_PROFILE_NAME), Collections.emptyList());
|
||||
assertExpectedProfiles(actualProfilesRep, Arrays.asList(FAPI1_BASELINE_PROFILE_NAME, FAPI1_ADVANCED_PROFILE_NAME, FAPI_CIBA_PROFILE_NAME, FAPI2_SECURITY_PROFILE_NAME, FAPI2_MESSAGE_SIGNING_PROFILE_NAME, OAUTH2_1_CONFIDENTIAL_CLIENT_PROFILE_NAME, OAUTH2_1_PUBLIC_CLIENT_PROFILE_NAME), Collections.emptyList());
|
||||
|
||||
// each profile - fapi-1-baseline
|
||||
ClientProfileRepresentation actualProfileRep = getProfileRepresentation(actualProfilesRep, FAPI1_BASELINE_PROFILE_NAME, true);
|
||||
|
|
Loading…
Reference in a new issue