KEYCLOAK-7635 : Authenticate clients with x509 certificate

This commit is contained in:
sebastienblanc 2018-07-25 13:31:50 +02:00 committed by Sebastien Blanc
parent 575851d45c
commit 02b2a8aab0
12 changed files with 392 additions and 33 deletions

View file

@ -17,6 +17,7 @@ env:
- TESTS=old
- TESTS=crossdc-server
- TESTS=crossdc-adapter
- TESTS=ssl
jdk:
- oraclejdk8

View file

@ -394,6 +394,14 @@ public class DefaultAuthenticationFlows {
execution.setAuthenticatorFlow(false);
realm.addAuthenticatorExecution(execution);
execution = new AuthenticationExecutionModel();
execution.setParentFlow(clients.getId());
execution.setRequirement(AuthenticationExecutionModel.Requirement.ALTERNATIVE);
execution.setAuthenticator("client-x509");
execution.setPriority(40);
execution.setAuthenticatorFlow(false);
realm.addAuthenticatorExecution(execution);
}
public static void firstBrokerLoginFlow(RealmModel realm, boolean migrate) {

View file

@ -0,0 +1,142 @@
package org.keycloak.authentication.authenticators.client;
import org.keycloak.OAuth2Constants;
import org.keycloak.authentication.AuthenticationFlowError;
import org.keycloak.authentication.ClientAuthenticationFlowContext;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.ClientModel;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.services.ServicesLogger;
import org.keycloak.services.x509.X509ClientCertificateLookup;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import java.security.GeneralSecurityException;
import java.security.cert.X509Certificate;
import java.util.*;
public class X509ClientAuthenticator extends AbstractClientAuthenticator {
public static final String PROVIDER_ID = "client-x509";
protected static ServicesLogger logger = ServicesLogger.LOGGER;
public static final AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = {
AuthenticationExecutionModel.Requirement.ALTERNATIVE,
AuthenticationExecutionModel.Requirement.DISABLED
};
@Override
public void authenticateClient(ClientAuthenticationFlowContext context) {
X509ClientCertificateLookup provider = context.getSession().getProvider(X509ClientCertificateLookup.class);
if (provider == null) {
logger.errorv("\"{0}\" Spi is not available, did you forget to update the configuration?",
X509ClientCertificateLookup.class);
return;
}
X509Certificate[] certs = new X509Certificate[0];
try {
certs = provider.getCertificateChain(context.getHttpRequest());
String client_id = null;
MediaType mediaType = context.getHttpRequest().getHttpHeaders().getMediaType();
boolean hasFormData = mediaType != null && mediaType.isCompatible(MediaType.APPLICATION_FORM_URLENCODED_TYPE);
MultivaluedMap<String, String> formData = hasFormData ? context.getHttpRequest().getDecodedFormParameters() : null;
MultivaluedMap<String, String> queryParams = context.getHttpRequest().getUri().getQueryParameters();
if (formData != null) {
client_id = formData.getFirst(OAuth2Constants.CLIENT_ID);
}
if (client_id == null) {
if (queryParams != null) {
client_id = queryParams.getFirst(OAuth2Constants.CLIENT_ID);
} else {
Response challengeResponse = ClientAuthUtil.errorResponse(Response.Status.BAD_REQUEST.getStatusCode(), "invalid_client", "Missing client_id parameter");
context.challenge(challengeResponse);
return;
}
}
ClientModel client = context.getRealm().getClientByClientId(client_id);
if (client == null) {
context.failure(AuthenticationFlowError.CLIENT_NOT_FOUND, null);
return;
}
context.getEvent().client(client_id);
context.setClient(client);
if (!client.isEnabled()) {
context.failure(AuthenticationFlowError.CLIENT_DISABLED, null);
return;
}
} catch (GeneralSecurityException e) {
logger.errorf("[X509ClientCertificateAuthenticator:authenticate] Exception: %s", e.getMessage());
context.attempted();
}
if (certs == null || certs.length == 0) {
// No x509 client cert, fall through and
// continue processing the rest of the authentication flow
logger.debug("[X509ClientCertificateAuthenticator:authenticate] x509 client certificate is not available for mutual SSL.");
context.attempted();
return;
}
context.success();
}
public String getDisplayType() {
return "X509 Certificate";
}
@Override
public boolean isConfigurable() {
return false;
}
@Override
public AuthenticationExecutionModel.Requirement[] getRequirementChoices() {
return REQUIREMENT_CHOICES;
}
@Override
public List<ProviderConfigProperty> getConfigPropertiesPerClient() {
return Collections.emptyList();
}
@Override
public Map<String, Object> getAdapterConfiguration(ClientModel client) {
Map<String, Object> result = new HashMap<>();
return result;
}
@Override
public Set<String> getProtocolAuthenticatorMethods(String loginProtocol) {
if (loginProtocol.equals(OIDCLoginProtocol.LOGIN_PROTOCOL)) {
Set<String> results = new HashSet<>();
return results;
} else {
return Collections.emptySet();
}
}
@Override
public String getHelpText() {
return "Validates client based on a X509 Certificate";
}
@Override
public List<ProviderConfigProperty> getConfigProperties() {
return Collections.emptyList();
}
@Override
public String getId() {
return PROVIDER_ID;
}
}

View file

@ -18,3 +18,4 @@
org.keycloak.authentication.authenticators.client.ClientIdAndSecretAuthenticator
org.keycloak.authentication.authenticators.client.JWTClientAuthenticator
org.keycloak.authentication.authenticators.client.JWTClientSecretAuthenticator
org.keycloak.authentication.authenticators.client.X509ClientAuthenticator

View file

@ -463,6 +463,17 @@ To run the Mutual TLS Client Certificate Bound Access Tokens tests:
-Dbrowser=phantomjs \
-Dtest=org.keycloak.testsuite.hok.HoKTest
## Run Mutual TLS for the Client tests
To run the Mutual TLS test for the client:
mvn -f testsuite/integration-arquillian/pom.xml \
clean install \
-Pauth-server-wildfly \
-Dauth.server.ssl.required \
-Dbrowser=phantomjs \
-Dtest=org.keycloak.testsuite.client.MutualTLSClientTest
## Cluster tests
Cluster tests use 2 backend servers (Keycloak on Wildfly/EAP) and 1 frontend loadbalancer server node. Invalidation tests don't use loadbalancer.

View file

@ -18,9 +18,13 @@ import org.apache.http.impl.client.HttpClientBuilder;
import org.keycloak.common.util.Base64Url;
import org.keycloak.common.util.KeystoreUtil;
public class HoKTokenUtils {
// KEYCLOAK-6771 Certificate Bound Token
// https://tools.ietf.org/html/draft-ietf-oauth-mtls-08#section-3
/**
* Utilities for Holder of key mechanism and other Mutual TLS tests.
*
* @see https://issues.jboss.org/browse/KEYCLOAK-6771
* @see https://tools.ietf.org/html/draft-ietf-oauth-mtls-08#section-3
*/
public class MutualTLSUtils {
public static final String DEFAULT_KEYSTOREPATH = System.getProperty("client.certificate.keystore");
public static final String DEFAULT_KEYSTOREPASSWORD = System.getProperty("client.certificate.keystore.passphrase");

View file

@ -21,6 +21,7 @@ import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.apache.http.Header;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
@ -76,6 +77,8 @@ import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Form;
@ -145,6 +148,8 @@ public class OAuthClient {
private String codeChallengeMethod;
private String origin;
private Supplier<CloseableHttpClient> httpClient = OAuthClient::newCloseableHttpClient;
public class LogoutUrlBuilder {
private final UriBuilder b = OIDCLoginProtocolService.logoutUrl(UriBuilder.fromUri(baseUrl));
@ -243,7 +248,12 @@ public class OAuthClient {
fillLoginForm(username, password);
}
private static CloseableHttpClient newCloseableHttpClient() {
public OAuthClient httpClient(Supplier<CloseableHttpClient> client) {
this.httpClient = client;
return this;
}
public static CloseableHttpClient newCloseableHttpClient() {
if (sslRequired) {
KeyStore keystore = null;
// load the keystore containing the client certificate - keystore type is probably jks or pkcs12
@ -274,7 +284,7 @@ public class OAuthClient {
}
public CloseableHttpResponse doPreflightRequest() {
try (CloseableHttpClient client = newCloseableHttpClient()) {
try (CloseableHttpClient client = httpClient.get()) {
HttpOptions options = new HttpOptions(getAccessTokenUrl());
options.setHeader("Origin", "http://example.com");
@ -286,7 +296,7 @@ public class OAuthClient {
// KEYCLOAK-6771 Certificate Bound Token
public AccessTokenResponse doAccessTokenRequest(String code, String password) {
try (CloseableHttpClient client = newCloseableHttpClient()) {
try (CloseableHttpClient client = httpClient.get()) {
return doAccessTokenRequest(code, password, client);
} catch (IOException ioe) {
throw new RuntimeException(ioe);
@ -398,7 +408,7 @@ public class OAuthClient {
public AccessTokenResponse doGrantAccessTokenRequest(String realm, String username, String password, String totp,
String clientId, String clientSecret) throws Exception {
try (CloseableHttpClient client = newCloseableHttpClient()) {
try (CloseableHttpClient client = httpClient.get()) {
HttpPost post = new HttpPost(getResourceOwnerPasswordCredentialGrantUrl(realm));
List<NameValuePair> parameters = new LinkedList<>();
@ -444,7 +454,7 @@ public class OAuthClient {
public AccessTokenResponse doTokenExchange(String realm, String token, String targetAudience,
String clientId, String clientSecret) throws Exception {
try (CloseableHttpClient client = newCloseableHttpClient()) {
try (CloseableHttpClient client = httpClient.get()) {
HttpPost post = new HttpPost(getResourceOwnerPasswordCredentialGrantUrl(realm));
List<NameValuePair> parameters = new LinkedList<>();
@ -484,7 +494,7 @@ public class OAuthClient {
}
public AccessTokenResponse doTokenExchange(String realm, String clientId, String clientSecret, Map<String, String> params) throws Exception {
try (CloseableHttpClient client = newCloseableHttpClient()) {
try (CloseableHttpClient client = httpClient.get()) {
HttpPost post = new HttpPost(getResourceOwnerPasswordCredentialGrantUrl(realm));
List<NameValuePair> parameters = new LinkedList<>();

View file

@ -141,11 +141,13 @@ public class InitialFlowsTest extends AbstractAuthenticationTest {
addExecExport(flow, null, false, "client-secret", false, null, ALTERNATIVE, 10);
addExecExport(flow, null, false, "client-jwt", false, null, ALTERNATIVE, 20);
addExecExport(flow, null, false, "client-secret-jwt", false, null, ALTERNATIVE, 30);
addExecExport(flow, null, false, "client-x509", false, null, ALTERNATIVE, 40);
execs = new LinkedList<>();
addExecInfo(execs, "Client Id and Secret", "client-secret", false, 0, 0, ALTERNATIVE, null, new String[]{ALTERNATIVE, DISABLED});
addExecInfo(execs, "Signed Jwt", "client-jwt", false, 0, 1, ALTERNATIVE, null, new String[]{ALTERNATIVE, DISABLED});
addExecInfo(execs, "Signed Jwt with Client Secret", "client-secret-jwt", false, 0, 2, ALTERNATIVE, null, new String[]{ALTERNATIVE, DISABLED});
addExecInfo(execs, "X509 Certificate", "client-x509", false, 0, 3, ALTERNATIVE, null, new String[]{ALTERNATIVE, DISABLED});
expected.add(new FlowExecutions(flow, execs));
flow = newFlow("direct grant", "OpenID Connect Resource Owner Grant", "basic-flow", true, true);

View file

@ -81,9 +81,12 @@ public class ProvidersTest extends AbstractAuthenticationTest {
"'client_secret' sent either in request parameters or in 'Authorization: Basic' header");
addProviderInfo(expected, "testsuite-client-passthrough", "Testsuite Dummy Client Validation", "Testsuite dummy authenticator, " +
"which automatically authenticates hardcoded client (like 'test-app' )");
addProviderInfo(expected, "client-x509", "X509 Certificate",
"Validates client based on a X509 Certificate");
addProviderInfo(expected, "client-secret-jwt", "Signed Jwt with Client Secret",
"Validates client based on signed JWT issued by client and signed with the Client Secret");
compareProviders(expected, result);
}

View file

@ -0,0 +1,173 @@
package org.keycloak.testsuite.client;
import java.io.IOException;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.function.Supplier;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.junit.Assert;
import org.junit.Assume;
import org.junit.BeforeClass;
import org.junit.Test;
import org.keycloak.OAuth2Constants;
import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.authentication.authenticators.client.X509ClientAuthenticator;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
import org.keycloak.testsuite.util.KeycloakModelUtils;
import org.keycloak.testsuite.util.MutualTLSUtils;
import org.keycloak.testsuite.util.OAuthClient;
import com.google.common.base.Charsets;
/**
* Mutual TLS Client tests.
*/
public class MutualTLSClientTest extends AbstractTestRealmKeycloakTest {
private static final boolean sslRequired = Boolean.parseBoolean(System.getProperty("auth.server.ssl.required"));
private static final String CLIENT_ID = "confidential-x509";
private static final String DISABLED_CLIENT_ID = "confidential-disabled-x509";
private static final String USER = "keycloak-user@localhost";
private static final String PASSWORD = "password";
private static final String REALM = "test";
@Override
public void configureTestRealm(RealmRepresentation testRealm) {
ClientRepresentation properConfiguration = KeycloakModelUtils.createClient(testRealm, CLIENT_ID);
properConfiguration.setServiceAccountsEnabled(Boolean.TRUE);
properConfiguration.setRedirectUris(Arrays.asList("https://localhost:8543/auth/realms/master/app/auth"));
properConfiguration.setClientAuthenticatorType(X509ClientAuthenticator.PROVIDER_ID);
ClientRepresentation disabledConfiguration = KeycloakModelUtils.createClient(testRealm, DISABLED_CLIENT_ID);
disabledConfiguration.setServiceAccountsEnabled(Boolean.TRUE);
disabledConfiguration.setRedirectUris(Arrays.asList("https://localhost:8543/auth/realms/master/app/auth"));
disabledConfiguration.setClientAuthenticatorType(X509ClientAuthenticator.PROVIDER_ID);
}
@BeforeClass
public static void sslRequired() {
Assume.assumeTrue("\"auth.server.ssl.required\" is required for Mutual TLS tests", sslRequired);
}
@Test
public void testSuccessfulClientInvocationWithProperCertificate() throws Exception {
//given
Supplier<CloseableHttpClient> clientWithProperCertificate = MutualTLSUtils::newCloseableHttpClientWithDefaultKeyStoreAndTrustStore;
//when
OAuthClient.AccessTokenResponse token = loginAndGetAccessTokenResponse(CLIENT_ID, clientWithProperCertificate);
//then
assertTokenObtained(token);
}
@Test
public void testSuccessfulClientInvocationWithClientIdInQueryParams() throws Exception {
//given//when
OAuthClient.AccessTokenResponse token = null;
try (CloseableHttpClient client = MutualTLSUtils.newCloseableHttpClientWithDefaultKeyStoreAndTrustStore()) {
login(CLIENT_ID);
token = getAccessTokenResponseWithQueryParams(CLIENT_ID, client);
}
//then
assertTokenObtained(token);
}
@Test
public void testFailedClientInvocationWithoutCertificateCertificate() throws Exception {
//given
Supplier<CloseableHttpClient> clientWithoutCertificate = MutualTLSUtils::newCloseableHttpClientWithoutKeyStoreAndTrustStore;
//when
OAuthClient.AccessTokenResponse token = loginAndGetAccessTokenResponse(CLIENT_ID, clientWithoutCertificate);
//then
assertTokenNotObtained(token);
}
@Test
public void testFailedClientInvocationWithDisabledClient() throws Exception {
//given//when
OAuthClient.AccessTokenResponse token = null;
try (CloseableHttpClient client = MutualTLSUtils.newCloseableHttpClientWithDefaultKeyStoreAndTrustStore()) {
login(DISABLED_CLIENT_ID);
disableClient(DISABLED_CLIENT_ID);
token = getAccessTokenResponse(DISABLED_CLIENT_ID, client);
}
//then
assertTokenNotObtained(token);
}
private OAuthClient.AccessTokenResponse loginAndGetAccessTokenResponse(String clientId, Supplier<CloseableHttpClient> client) throws IOException{
try (CloseableHttpClient closeableHttpClient = client.get()) {
login(clientId);
return getAccessTokenResponse(clientId, closeableHttpClient);
} catch (IOException ioe) {
throw ioe;
}
}
private OAuthClient.AccessTokenResponse getAccessTokenResponse(String clientId, CloseableHttpClient closeableHttpClient) {
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
// Call protected endpoint with supplied client.
return oauth
.httpClient(() -> closeableHttpClient)
.clientId(clientId)
.doAccessTokenRequest(code, null, closeableHttpClient);
}
private void login(String clientId) {
// Login with default client, despite what has been supplied into this method.
oauth
.httpClient(OAuthClient::newCloseableHttpClient)
.clientId(clientId)
.doLogin(USER, PASSWORD);
}
private void assertTokenObtained(OAuthClient.AccessTokenResponse token) {
Assert.assertEquals(200, token.getStatusCode());
Assert.assertNotNull(token.getAccessToken());
}
private void assertTokenNotObtained(OAuthClient.AccessTokenResponse token) {
Assert.assertEquals(400, token.getStatusCode());
Assert.assertNull(token.getAccessToken());
}
/*
* This is a very simplified version of OAuthClient#doAccessTokenRequest.
* It test a scenario, where we do not follow the spec and specify client_id in Query Params (for in a form).
*/
private OAuthClient.AccessTokenResponse getAccessTokenResponseWithQueryParams(String clientId, CloseableHttpClient client) throws Exception {
OAuthClient.AccessTokenResponse token;// This is a very simplified version of
HttpPost post = new HttpPost(oauth.getAccessTokenUrl() + "?client_id=" + clientId);
List<NameValuePair> parameters = new LinkedList<>();
parameters.add(new BasicNameValuePair(OAuth2Constants.GRANT_TYPE, OAuth2Constants.AUTHORIZATION_CODE));
parameters.add(new BasicNameValuePair(OAuth2Constants.CODE, oauth.getCurrentQuery().get(OAuth2Constants.CODE)));
parameters.add(new BasicNameValuePair(OAuth2Constants.REDIRECT_URI, oauth.getRedirectUri()));
UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(parameters, Charsets.UTF_8);
post.setEntity(formEntity);
return new OAuthClient.AccessTokenResponse(client.execute(post));
}
private void disableClient(String clientId) {
ClientRepresentation disabledClientRepresentation = adminClient.realm(REALM).clients().findByClientId(clientId).get(0);
ClientResource disabledClientResource = adminClient.realms().realm(REALM).clients().get(disabledClientRepresentation.getId());
disabledClientRepresentation.setEnabled(false);
disabledClientResource.update(disabledClientRepresentation);
}
}

View file

@ -61,7 +61,7 @@ import org.keycloak.testsuite.AssertEvents;
import org.keycloak.testsuite.admin.ApiUtil;
import org.keycloak.testsuite.drone.Different;
import org.keycloak.testsuite.util.ClientManager;
import org.keycloak.testsuite.util.HoKTokenUtils;
import org.keycloak.testsuite.util.MutualTLSUtils;
import org.keycloak.testsuite.util.KeycloakModelUtils;
import org.keycloak.testsuite.util.OAuthClient;
import org.keycloak.testsuite.util.UserInfoClientUtil;
@ -174,7 +174,7 @@ public class HoKTest extends AbstractTestRealmKeycloakTest {
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
AccessTokenResponse response;
try (CloseableHttpClient client = HoKTokenUtils.newCloseableHttpClientWithDefaultKeyStoreAndTrustStore()) {
try (CloseableHttpClient client = MutualTLSUtils.newCloseableHttpClientWithDefaultKeyStoreAndTrustStore()) {
response = oauth.doAccessTokenRequest(code, "password", client);
} catch (IOException ioe) {
throw new RuntimeException(ioe);
@ -191,7 +191,7 @@ public class HoKTest extends AbstractTestRealmKeycloakTest {
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
AccessTokenResponse response;
try (CloseableHttpClient client = HoKTokenUtils.newCloseableHttpClientWithoutKeyStoreAndTrustStore()) {
try (CloseableHttpClient client = MutualTLSUtils.newCloseableHttpClientWithoutKeyStoreAndTrustStore()) {
response = oauth.doAccessTokenRequest(code, "password", client);
} catch (IOException ioe) {
throw new RuntimeException(ioe);
@ -238,7 +238,7 @@ public class HoKTest extends AbstractTestRealmKeycloakTest {
assertEquals(sessionId, token.getSessionState());
//assertEquals(1, token.getRealmAccess().getRoles().size());
assertEquals(2, token.getRealmAccess().getRoles().size());
assertTrue(token.getRealmAccess().isUserInRole("user"));
assertEquals(1, token.getResourceAccess(oauth.getClientId()).getRoles().size());
@ -258,7 +258,7 @@ public class HoKTest extends AbstractTestRealmKeycloakTest {
oauth.doLogin("test-user@localhost", "password");
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
AccessTokenResponse tokenResponse = null;
try (CloseableHttpClient client = HoKTokenUtils.newCloseableHttpClientWithDefaultKeyStoreAndTrustStore()) {
try (CloseableHttpClient client = MutualTLSUtils.newCloseableHttpClientWithDefaultKeyStoreAndTrustStore()) {
tokenResponse = oauth.doAccessTokenRequest(code, "password", client);
} catch (IOException ioe) {
throw new RuntimeException(ioe);
@ -272,7 +272,7 @@ public class HoKTest extends AbstractTestRealmKeycloakTest {
oauth2.doLogin("john-doh@localhost", "password");
String code2 = oauth2.getCurrentQuery().get(OAuth2Constants.CODE);
AccessTokenResponse tokenResponse2 = null;
try (CloseableHttpClient client = HoKTokenUtils.newCloseableHttpClientWithOtherKeyStoreAndTrustStore()) {
try (CloseableHttpClient client = MutualTLSUtils.newCloseableHttpClientWithOtherKeyStoreAndTrustStore()) {
tokenResponse2 = oauth2.doAccessTokenRequest(code2, "password", client);
} catch (IOException ioe) {
throw new RuntimeException(ioe);
@ -281,7 +281,7 @@ public class HoKTest extends AbstractTestRealmKeycloakTest {
// token refresh by second client by first client's refresh token
AccessTokenResponse response = null;
try (CloseableHttpClient client = HoKTokenUtils.newCloseableHttpClientWithOtherKeyStoreAndTrustStore()) {
try (CloseableHttpClient client = MutualTLSUtils.newCloseableHttpClientWithOtherKeyStoreAndTrustStore()) {
response = oauth2.doRefreshTokenRequest(refreshTokenString, "password", client);
} catch (IOException ioe) {
throw new RuntimeException(ioe);
@ -303,7 +303,7 @@ public class HoKTest extends AbstractTestRealmKeycloakTest {
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
AccessTokenResponse tokenResponse = null;
try (CloseableHttpClient client = HoKTokenUtils.newCloseableHttpClientWithDefaultKeyStoreAndTrustStore()) {
try (CloseableHttpClient client = MutualTLSUtils.newCloseableHttpClientWithDefaultKeyStoreAndTrustStore()) {
tokenResponse = oauth.doAccessTokenRequest(code, "password", client);
} catch (IOException ioe) {
throw new RuntimeException(ioe);
@ -325,7 +325,7 @@ public class HoKTest extends AbstractTestRealmKeycloakTest {
setTimeOffset(2);
AccessTokenResponse response = null;
try (CloseableHttpClient client = HoKTokenUtils.newCloseableHttpClientWithDefaultKeyStoreAndTrustStore()) {
try (CloseableHttpClient client = MutualTLSUtils.newCloseableHttpClientWithDefaultKeyStoreAndTrustStore()) {
response = oauth.doRefreshTokenRequest(refreshTokenString, "password", client);
} catch (IOException ioe) {
throw new RuntimeException(ioe);
@ -362,7 +362,7 @@ public class HoKTest extends AbstractTestRealmKeycloakTest {
setTimeOffset(2);
AccessTokenResponse response = null;
try (CloseableHttpClient client = HoKTokenUtils.newCloseableHttpClientWithoutKeyStoreAndTrustStore()) {
try (CloseableHttpClient client = MutualTLSUtils.newCloseableHttpClientWithoutKeyStoreAndTrustStore()) {
response = oauth.doRefreshTokenRequest(refreshTokenString, "password", client);
} catch (IOException ioe) {
throw new RuntimeException(ioe);
@ -405,7 +405,7 @@ public class HoKTest extends AbstractTestRealmKeycloakTest {
assertEquals(findUserByUsername(adminClient.realm("test"), username).getId(), refreshedToken.getSubject());
Assert.assertNotEquals(username, refreshedToken.getSubject());
//assertEquals(1, refreshedToken.getRealmAccess().getRoles().size());
assertEquals(2, refreshedToken.getRealmAccess().getRoles().size());
Assert.assertTrue(refreshedToken.getRealmAccess().isUserInRole("user"));
assertEquals(1, refreshedToken.getResourceAccess(oauth.getClientId()).getRoles().size());
@ -431,7 +431,7 @@ public class HoKTest extends AbstractTestRealmKeycloakTest {
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
AccessTokenResponse tokenResponse = null;
try (CloseableHttpClient client = HoKTokenUtils.newCloseableHttpClientWithDefaultKeyStoreAndTrustStore()) {
try (CloseableHttpClient client = MutualTLSUtils.newCloseableHttpClientWithDefaultKeyStoreAndTrustStore()) {
tokenResponse = oauth.doAccessTokenRequest(code, "password", client);
} catch (IOException ioe) {
throw new RuntimeException(ioe);
@ -442,8 +442,8 @@ public class HoKTest extends AbstractTestRealmKeycloakTest {
// execute the access token to get UserInfo with token binded client certificate in mutual authentication TLS
ClientBuilder clientBuilder = ClientBuilder.newBuilder();
KeyStore keystore = null;
keystore = KeystoreUtil.loadKeyStore(HoKTokenUtils.DEFAULT_KEYSTOREPATH, HoKTokenUtils.DEFAULT_KEYSTOREPASSWORD);
clientBuilder.keyStore(keystore, HoKTokenUtils.DEFAULT_KEYSTOREPASSWORD);
keystore = KeystoreUtil.loadKeyStore(MutualTLSUtils.DEFAULT_KEYSTOREPATH, MutualTLSUtils.DEFAULT_KEYSTOREPASSWORD);
clientBuilder.keyStore(keystore, MutualTLSUtils.DEFAULT_KEYSTOREPASSWORD);
Client client = clientBuilder.build();
WebTarget userInfoTarget = null;
Response response = null;
@ -469,7 +469,7 @@ public class HoKTest extends AbstractTestRealmKeycloakTest {
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
AccessTokenResponse tokenResponse = null;
try (CloseableHttpClient client = HoKTokenUtils.newCloseableHttpClientWithDefaultKeyStoreAndTrustStore()) {
try (CloseableHttpClient client = MutualTLSUtils.newCloseableHttpClientWithDefaultKeyStoreAndTrustStore()) {
tokenResponse = oauth.doAccessTokenRequest(code, "password", client);
} catch (IOException ioe) {
throw new RuntimeException(ioe);
@ -510,7 +510,7 @@ public class HoKTest extends AbstractTestRealmKeycloakTest {
String refreshTokenString = execPreProcessPostLogout();
CloseableHttpResponse response = null;
try (CloseableHttpClient client = HoKTokenUtils.newCloseableHttpClientWithDefaultKeyStoreAndTrustStore()) {
try (CloseableHttpClient client = MutualTLSUtils.newCloseableHttpClientWithDefaultKeyStoreAndTrustStore()) {
response = oauth.doLogout(refreshTokenString, "password", client);
} catch (IOException ioe) {
throw new RuntimeException(ioe);
@ -526,7 +526,7 @@ public class HoKTest extends AbstractTestRealmKeycloakTest {
String refreshTokenString = execPreProcessPostLogout();
CloseableHttpResponse response = null;
try (CloseableHttpClient client = HoKTokenUtils.newCloseableHttpClientWithoutKeyStoreAndTrustStore()) {
try (CloseableHttpClient client = MutualTLSUtils.newCloseableHttpClientWithoutKeyStoreAndTrustStore()) {
response = oauth.doLogout(refreshTokenString, "password", client);
} catch (IOException ioe) {
throw new RuntimeException(ioe);
@ -596,7 +596,7 @@ public class HoKTest extends AbstractTestRealmKeycloakTest {
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
EventRepresentation loginEvent = events.expectLogin().assertEvent();
AccessTokenResponse accessTokenResponse = null;
try (CloseableHttpClient client = HoKTokenUtils.newCloseableHttpClientWithDefaultKeyStoreAndTrustStore()) {
try (CloseableHttpClient client = MutualTLSUtils.newCloseableHttpClientWithDefaultKeyStoreAndTrustStore()) {
accessTokenResponse = oauth.doAccessTokenRequest(code, "password", client);
} catch (IOException ioe) {
throw new RuntimeException(ioe);
@ -605,7 +605,7 @@ public class HoKTest extends AbstractTestRealmKeycloakTest {
// Do token introspection
// mimic Resource Server
String tokenResponse;
try (CloseableHttpClient client = HoKTokenUtils.newCloseableHttpClientWithoutKeyStoreAndTrustStore()) {
try (CloseableHttpClient client = MutualTLSUtils.newCloseableHttpClientWithoutKeyStoreAndTrustStore()) {
tokenResponse = oauth.introspectTokenWithClientCredential("confidential-cli", "secret1", "access_token", accessTokenResponse.getAccessToken(), client);
} catch (IOException ioe) {
throw new RuntimeException(ioe);
@ -618,7 +618,7 @@ public class HoKTest extends AbstractTestRealmKeycloakTest {
String certThumprintFromAccessToken = at.getCertConf().getCertThumbprint();
String certThumprintFromRefreshToken = rt.getCertConf().getCertThumbprint();
String certThumprintFromTokenIntrospection = rep.getCertConf().getCertThumbprint();
String certThumprintFromBoundClientCertificate = HoKTokenUtils.getThumbprintFromDefaultClientCert();
String certThumprintFromBoundClientCertificate = MutualTLSUtils.getThumbprintFromDefaultClientCert();
assertTrue(rep.isActive());
assertEquals("test-user@localhost", rep.getUserName());
@ -633,11 +633,11 @@ public class HoKTest extends AbstractTestRealmKeycloakTest {
private void verifyHoKTokenDefaultCertThumbPrint(AccessTokenResponse response) throws Exception {
verifyHoKTokenCertThumbPrint(response, HoKTokenUtils.getThumbprintFromDefaultClientCert());
verifyHoKTokenCertThumbPrint(response, MutualTLSUtils.getThumbprintFromDefaultClientCert());
}
private void verifyHoKTokenOtherCertThumbPrint(AccessTokenResponse response) throws Exception {
verifyHoKTokenCertThumbPrint(response, HoKTokenUtils.getThumbprintFromOtherClientCert());
verifyHoKTokenCertThumbPrint(response, MutualTLSUtils.getThumbprintFromOtherClientCert());
}
private void verifyHoKTokenCertThumbPrint(AccessTokenResponse response, String certThumbPrint) {

View file

@ -5,7 +5,7 @@ function run-server-tests() {
mvn install -B -nsu -Pauth-server-wildfly -DskipTests
cd tests/base
mvn test -B -nsu -Pauth-server-wildfly -Dtest=$1 2>&1 | java -cp ../../../utils/target/classes org.keycloak.testsuite.LogTrimmer
mvn test -B -nsu -Pauth-server-wildfly -Dtest=$1 $2 2>&1 | java -cp ../../../utils/target/classes org.keycloak.testsuite.LogTrimmer
exit ${PIPESTATUS[0]}
}
@ -99,3 +99,7 @@ if [ $1 == "crossdc-adapter" ]; then
java -cp ../../../utils/target/classes org.keycloak.testsuite.LogTrimmer
exit ${PIPESTATUS[0]}
fi
if [ $1 == "ssl" ]; then
run-server-tests org.keycloak.testsuite.client.MutualTLSClientTest,org.keycloak.testsuite.hok.HoKTest "-Dauth.server.ssl.required -Dbrowser=phantomjs"
fi