Skip to content

Commit b1f9974

Browse files
Add support for mtls client cert auth (#1317)
* feat: Add support for mtls client cert auth (#1095) - Add dummy keymaterial for mtls-tests - Add default mtls-certs valid for 825 days, see 1) - Add makefile rules for generating / clean mtls-certs Based on initial PR #1037 1) https://knowledge.digicert.com/quovadis/ssl-certificates/ssl-general-topics/maximum-validity-changes-for-tls-ssl-to-drop-to-825-days-in-q1-2018 Fixes #1095 Co-authored-by: lucdew <[email protected]> Signed-off-by: Thomas Darimont <[email protected]> * Update .gitignore - custom-user-federation-example/bin - Ignore test_env_*.json files 1) https://knowledge.digicert.com/quovadis/ssl-certificates/ssl-general-topics/maximum-validity-changes-for-tls-ssl-to-drop-to-825-days-in-q1-2018 Signed-off-by: Thomas Darimont <[email protected]> * Add support for testing mtls configs - Inspect KEYCLOAK_TLS_CA_CERT, KEYCLOAK_TLS_CLIENT_CERT and KEYCLOAK_TLS_CLIENT_KEY - Extract TEST_ENV_FILE processing - Make UpdateEnvFromTestEnvIfPresent available for provider_test and keycloak_client_test - Add certificates to Keycloak docker compose config - Update readme Signed-off-by: Thomas Darimont <[email protected]> * Add keycloak https mtls client auth connect test Signed-off-by: Thomas Darimont <[email protected]> * Allow testing of https mtls client auth in github CI tests Signed-off-by: Thomas Darimont <[email protected]> * Don't use mtls certs for http tests Signed-off-by: Thomas Darimont <[email protected]> * Enable https/mtls support for github CI tests Signed-off-by: Thomas Darimont <[email protected]> * Skip https/mtls client auth tests for Keycloak <= 26.0 Signed-off-by: Thomas Darimont <[email protected]> * Use KEYCLOAK_URL_HTTP to derive http:// URL of Keycloak We need this to be always able to obtain the Keycloak version from the server to decide if we can perform the test or not. Signed-off-by: Thomas Darimont <[email protected]> * Split up github actions in http and https(mtls) tests Signed-off-by: Thomas Darimont <[email protected]> * Expose https port for Keycloak in github CI env Signed-off-by: Thomas Darimont <[email protected]> * Pass KEYCLOAK_TLS_CA_CERT from env if present in provider_test Signed-off-by: Thomas Darimont <[email protected]> * Only apply EXTRA_HTTP_CLIENT_AUTH if we test mtls client auth Signed-off-by: Thomas Darimont <[email protected]> * Use http.Client provided by keycloakClient in tests to benefit from the TLS/mTLS configuration. Signed-off-by: Thomas Darimont <[email protected]> * Add support for dedicated local mTLS test environment - Add local-mtls makefile rule - Move mTLS client authentication config into docker-compose-mtls.yml - Skip HttpsMtlsAuth test if client certificate config is not set - Add https/mtls testing sections to readme Signed-off-by: Thomas Darimont <[email protected]> * Update .gitignore - Simplify pattern for excluding local test_env overrides - Exclude custom provider_installation mappings - Exclude kcadm config folder - Exclude kcdata Signed-off-by: Thomas Darimont <[email protected]> * Add update keycloak version filter in test.yml Signed-off-by: Thomas Darimont <[email protected]> * Moved provider/misc folder into testdata Signed-off-by: Thomas Darimont <[email protected]> * Add dedicated shell script to generate mtls-certs Signed-off-by: Thomas Darimont <[email protected]> * Revert "Moved provider/misc folder into testdata" This reverts commit 5a51df7. Signed-off-by: Thomas Darimont <[email protected]> * Use testdata folder to avoid adding test artifacts to binaries - Unify testdata folder - Rename provider/misc to provider/testdata - Move testdata/tls folder to provider/testdata/tls Fixes #1319 Signed-off-by: Thomas Darimont <[email protected]> --------- Signed-off-by: Thomas Darimont <[email protected]> Co-authored-by: lucdew <[email protected]>
1 parent 33b305b commit b1f9974

28 files changed

+665
-64
lines changed

.github/workflows/test.yml

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,18 +94,30 @@ jobs:
9494
fi
9595
if [[ "${{ matrix.keycloak-version }}" == "26.3.4" || "${{ matrix.keycloak-version }}" == "26.2.5" ]]; then
9696
EXTRA_FEATURES=",admin-fine-grained-authz:v1"
97+
98+
EXTRA_HTTP_CLIENT_AUTH="-e KC_HTTPS_CLIENT_AUTH=required"
99+
EXTRA_HTTPS_CERT="-e KC_HTTPS_CERTIFICATE_FILE=/opt/keycloak/testdata/tls/server-cert.pem"
100+
EXTRA_HTTPS_KEY="-e KC_HTTPS_CERTIFICATE_KEY_FILE=/opt/keycloak/testdata/tls/server-key.pem"
101+
EXTRA_MTLS_CERTS="-e KC_TRUSTSTORE_PATHS=/opt/keycloak/testdata/tls/ca-cert.pem,/opt/keycloak/testdata/tls/client-cert.pem"
97102
fi
98103
99104
docker run -d --name keycloak \
100105
-p 8080:8080 \
106+
-p 8443:8443 \
101107
-e KC_DB=dev-mem \
102108
-e KC_LOG_LEVEL=INFO,org.keycloak:debug \
103109
-e KEYCLOAK_ADMIN=keycloak \
104110
-e KEYCLOAK_ADMIN_PASSWORD=password \
111+
-e KC_BOOTSTRAP_ADMIN_USERNAME=keycloak \
112+
-e KC_BOOTSTRAP_ADMIN_PASSWORD=password \
113+
${EXTRA_HTTP_CLIENT_AUTH} \
114+
${EXTRA_HTTPS_CERT} \
115+
${EXTRA_HTTPS_KEY} \
116+
${EXTRA_MTLS_CERTS} \
105117
-e KC_FEATURES=preview${EXTRA_FEATURES} \
106118
-e QUARKUS_HTTP_ACCESS_LOG_ENABLED=true \
107119
-e QUARKUS_HTTP_RECORD_REQUEST_START_TIME=true \
108-
-v $PWD/provider/misc:/opt/keycloak/misc:z \
120+
-v $PWD/provider/testdata:/opt/keycloak/testdata:z \
109121
$MOUNT_FEDERATION_EXAMPLE_VOLUME \
110122
quay.io/keycloak/keycloak:${{ matrix.keycloak-version }} --verbose start-dev
111123
@@ -130,9 +142,33 @@ jobs:
130142
KEYCLOAK_CLIENT_SECRET: 884e0f95-0f42-4a63-9b1f-94274655669e
131143
KEYCLOAK_CLIENT_TIMEOUT: 120
132144
KEYCLOAK_REALM: master
145+
# for mtls client auth
133146
KEYCLOAK_URL: "http://localhost:8080"
134147
KEYCLOAK_TEST_PASSWORD_GRANT: "true"
135148
KEYCLOAK_VERSION: ${{ steps.keycloak-version.outputs.result }}
149+
150+
timeout-minutes: 60
151+
# Only run mtls test for the later versions
152+
- name: Test (mtls client auth)
153+
if: matrix.keycloak-version == '26.3.4' || matrix.keycloak-version == '26.2.5'
154+
run: |
155+
terraform version
156+
go mod download
157+
make testacc
158+
env:
159+
KEYCLOAK_CLIENT_ID: terraform
160+
KEYCLOAK_CLIENT_SECRET: 884e0f95-0f42-4a63-9b1f-94274655669e
161+
KEYCLOAK_CLIENT_TIMEOUT: 120
162+
KEYCLOAK_REALM: master
163+
# for mtls client auth
164+
KEYCLOAK_URL: "https://localhost:8443"
165+
KEYCLOAK_URL_HTTP: "http://localhost:8080"
166+
KEYCLOAK_TLS_CLIENT_CERT: "-----BEGIN CERTIFICATE-----\nMIIFAjCCAuqgAwIBAgIUHeZgtpvLa35tBbH5DT92iPzan64wDQYJKoZIhvcNAQEL\nBQAwbTELMAkGA1UEBhMCVVMxEDAOBgNVBAgMB1Vua25vd24xEDAOBgNVBAcMB1Vu\na25vd24xEDAOBgNVBAoMB1Vua25vd24xEDAOBgNVBAsMB1Vua25vd24xFjAUBgNV\nBAMMDURldiBUZXN0IFJvb3QwHhcNMjUwOTIwMTkwMjU3WhcNMjcxMjI0MTkwMjU3\nWjBzMQswCQYDVQQGEwJVUzEQMA4GA1UECAwHVW5rbm93bjEQMA4GA1UEBwwHVW5r\nbm93bjEQMA4GA1UECgwHVW5rbm93bjEQMA4GA1UECwwHVW5rbm93bjEcMBoGA1UE\nAwwTdHJ1c3RlZC1jbGllbnQtbXRsczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC\nAQoCggEBAKfvc1qWAfE39s4RuS81RfdwXT9buwr5RLASNfPW4vZKt/iy/L+nS+SG\nXYQQeMSreZQwunFtQJF5JhxXMC4tlgAyIn2r+59c+5+9C9cbKUypV4NxtUqSjLew\nvTEKs2bu2t2cax97RtUJzPoCeD8qVi+SkyJBU0mNR7tRS2zrh2NdPMg9sBMc2HmV\nOSZ86zLvn6vSmmP9AefXvA78S3Bkj3L+fhRfqWqxYI08j2TdtLpvrvzsnJ2rqYHO\nPjgSE7GE4tbPGtSLNQU4ziEmC8bt3mdqgMUG1lBG6JrBoVMVaqH3Z86ZQr94xz9W\nAmJk646sXRa+vQmx62HOicFrA/v/Z8UCAwEAAaOBkzCBkDAJBgNVHRMEAjAAMA4G\nA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAjAeBgNVHREEFzAVghN0\ncnVzdGVkLWNsaWVudC1tdGxzMB0GA1UdDgQWBBSFE0oLAkwJBwyMCCoQJXvF6Jvg\nyTAfBgNVHSMEGDAWgBT459iaWRGzTBIdUHLFon1GSQUyoTANBgkqhkiG9w0BAQsF\nAAOCAgEAZKnvqPT3lnDuuG1lJKUiDr/3qkC5TZpDLsrLaglbSwiCPVNHLgE4oq0q\n5ktzNUNx6HTLfn3dAuyd+K63/Tc3hXHDGNHQnRPRhPHGxceCIGUC7Qiqwdi6BNpr\nXJPHqMbEYWq4YHNj9aA6UYr2opp1P3KikACurN4llssx/FgHAXNPs5lD7nCxPuA+\nu2yWE+Y7kzd9PasrgFThX5Blz18H9+O0ri3T5VnYyDZ1kdALx/BzZ6BaQQEkcuh5\nVz+ZXCTNe9mtG8cFdnJUaCL6u9J6D4DfhdW40J+ZX1VJ1223CZquDXjcUUyPZPMo\n5WlTlCYodmcXCk6wtaUZ6kgUvqV61hFrcgs7byHYAtjaweulqy51QNfJT5Qhm8y+\n6b+PkWX+Gb8HKH8ceGjpJ2BA73Rb1keew77zr1/XMVWhwO524DRrXqQ4YFpK4Q3i\n9ZGhuVJCZIXhG4K+S48x/Q9AXPQ87Yk7SGxk7+/keXIpxZZiwB1TMfdpOKPH7wT1\n4wNrhiKrK4t+fSMbMvbPtFRAWGKz+dS1KRZVcGqv5qt05NDesA3pzrR9Rbyl9G4A\n2uxAeH/RjzDI/9UHfYZSOoAvsLrul7ZzIpRWpSSaK0W8Pw2iNUArYTlTpzIUxeLP\nDH309xDpOXvRgKhri6zUQYfnGv5lA2m3LEH3cVqjhACRWMg7dkM=\n-----END CERTIFICATE-----\n"
167+
KEYCLOAK_TLS_CLIENT_KEY: "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCn73NalgHxN/bO\nEbkvNUX3cF0/W7sK+USwEjXz1uL2Srf4svy/p0vkhl2EEHjEq3mUMLpxbUCReSYc\nVzAuLZYAMiJ9q/ufXPufvQvXGylMqVeDcbVKkoy3sL0xCrNm7trdnGsfe0bVCcz6\nAng/KlYvkpMiQVNJjUe7UUts64djXTzIPbATHNh5lTkmfOsy75+r0ppj/QHn17wO\n/EtwZI9y/n4UX6lqsWCNPI9k3bS6b6787Jydq6mBzj44EhOxhOLWzxrUizUFOM4h\nJgvG7d5naoDFBtZQRuiawaFTFWqh92fOmUK/eMc/VgJiZOuOrF0Wvr0JsethzonB\nawP7/2fFAgMBAAECggEAA0SaSWWokq8fcxHOjr1J/USx1oJ3I1bdH/1au2yvwfyL\nk/ViYcBkWQVxsG45oL94KuAVNhEwM88tugN1q+W13jnGM2KIulMu5QQ4GhmB4Odd\nYptwhwukXWFnwm/jidnqvGvyJwyua4WN+EIwC4VMDrpFeWHYDb2ywFHBVqnxWoef\n1UhhL2w+vVDC+IVW4dd50Z4i8PU9xFUeTaKfr7tWujXGlujn57wWvr5r25WURha6\nWNBVZaoj/WSUbdD1c6a150q0GEF3Fd1ofQ1/PJRUgL5+lhdjUgBv1S2Z9/6DK4Fq\ngA8Saeh1tl85PrAbNPkz3lqoXg0HpOBd4pRYXrA4CQKBgQDo8gZocAMJzoX+6Fym\naBJWB97hcMl0YkGDl8tUaZlO0bCxh5BOGh4ZoP5e7avEXu8FbdmxNdIO5ENO80Bk\ntl2eG1S7ajdzgEoNREgUplChza6bEGAltnaloY9kzY2c/FRdqZFRPwpBB68V1n/E\nFusMJlQ09fN8SGj0GD98nCadpwKBgQC4jk+s2HnbvLCxOE852YNLS18Rlm030/ZP\ndyOVzQuHPpOghOHLVA5L10Q5bjVQGzN+bTbgB/403wAyop3oZtjOCE2qbimZxmfs\nqeJSx5OEpfqo95Eg/9WDjXMtWN8WtbYsxqOdzO+aqK1KX3aBUA/VgthBAnfWbZF1\nfNQ4euT0swKBgQDVv39xxZaEISWDSeP6LfTlTEOPydaRHLfQ8DB7PIqYcIEZ5bLc\nd8q26at/n8bFYfchnDLtEN23HG1GvJ6Ry2UL9zhA4K4RJd7NXaJmkFXcosddMiGH\neW5VfXH+pT8UldU0PKxDSP03vr1B5JlIbV8wvtr13dmWaTslADsBNKeacQKBgCpz\nucoVhXpRHge13yt8aCIStUyTYI4d+KNw0UOtBcDXWRfsWQ/vRtaVLsFTI3pIt4CW\nWLARxpycyyvakh4aQjaqXEseyfzwUYlzznaiJ8G0eEMTp1OC5bc7+0lsDuznYX9N\nNeefc2IM+MeJy/WU1/+R+HKDwdMWIwZ2b06Knk3XAoGAOCedCxVJMIR6xGw6NDDI\njWI39WpIzq7FNJGBJbjXgE0EazFClQrEsKkt4Qvi9mIkHFwLo+LbriWs5oe1V4dC\nNSgNPEtPR70LwRhp1Xr8ChMM5ZP75zYcu09O1IKrbiWGN6jJwnJxg3q4WmuB8g3o\nOValBgrKUp3ueYbmlRqLfcs=\n-----END PRIVATE KEY-----\n"
168+
KEYCLOAK_TLS_CA_CERT: "-----BEGIN CERTIFICATE-----\n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\n-----END CERTIFICATE-----\n"
169+
KEYCLOAK_TEST_PASSWORD_GRANT: "true"
170+
KEYCLOAK_VERSION: ${{ steps.keycloak-version.outputs.result }}
171+
136172
timeout-minutes: 60
137173

138174
- name: Print container logs

.gitignore

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ scratch/
2626
.gradle/
2727

2828
# custom user federation example
29+
custom-user-federation-example/bin
2930
custom-user-federation-example/build
3031
!custom-user-federation-example/build/libs
3132

@@ -37,4 +38,14 @@ site/
3738

3839
.DS_Store
3940

40-
test_env.json
41+
# Custom test_env overrides
42+
test_env*.json
43+
44+
# Custom provider_installation mappings
45+
dev.tfrc
46+
47+
# KCADM config folder
48+
.keycloak/
49+
50+
# Locally started Keycloak data
51+
kcdata/

README.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ You can spin up a local developer environment via [Docker Compose](https://docs.
106106
This will spin up a few containers for Keycloak, PostgreSQL, and OpenLDAP, which can be used for testing the provider.
107107
This environment and its setup via `make local` is not intended for production use.
108108

109+
You can also use `make local-mtls` to start Keycloak with required client authentication via mTLS certificate.
110+
109111
To stop the environment you can use the `make local-stop`. To remove the local environment use `make local-down`.
110112

111113
Note: The setup scripts require the [jq](https://stedolan.github.io/jq/) command line utility.
@@ -128,6 +130,39 @@ KEYCLOAK_URL="http://localhost:8080" \
128130
make testacc
129131
```
130132

133+
#### Test with HTTPS
134+
You can also run the same tests on Keycloak's https port.
135+
For this start the env with `make local`. After that run the following command:
136+
137+
```
138+
KEYCLOAK_CLIENT_ID=terraform \
139+
KEYCLOAK_CLIENT_SECRET=884e0f95-0f42-4a63-9b1f-94274655669e \
140+
KEYCLOAK_CLIENT_TIMEOUT=5 \
141+
KEYCLOAK_REALM=master \
142+
KEYCLOAK_TEST_PASSWORD_GRANT=true \
143+
KEYCLOAK_URL="https://localhost:8443" \
144+
KEYCLOAK_TLS_CA_CERT="$(cat provider/testdata/tls/server-cert.pem)" \
145+
make testacc
146+
```
147+
148+
#### Test with HTTPS + mTLS
149+
You can also run the same tests on Keycloak's https port with the Keycloak Terraform provider authenticating to the server with a mTLS client certificate.
150+
For this start the env with `make local-mtls`. After that run the following command:
151+
152+
```
153+
KEYCLOAK_CLIENT_ID=terraform \
154+
KEYCLOAK_CLIENT_SECRET=884e0f95-0f42-4a63-9b1f-94274655669e \
155+
KEYCLOAK_CLIENT_TIMEOUT=5 \
156+
KEYCLOAK_REALM=master \
157+
KEYCLOAK_TEST_PASSWORD_GRANT=true \
158+
KEYCLOAK_URL_HTTP="http://localhost:8080" \
159+
KEYCLOAK_URL="https://localhost:8443" \
160+
KEYCLOAK_TLS_CLIENT_CERT="$(cat provider/testdata/tls/client-cert.pem)" \
161+
KEYCLOAK_TLS_CLIENT_KEY="$(cat provider/testdata/tls/client-key.pem)" \
162+
KEYCLOAK_TLS_CA_CERT="$(cat provider/testdata/tls/server-cert.pem)" \
163+
make testacc
164+
```
165+
131166
### Run examples
132167

133168
You can run examples against a Keycloak instance.

docker-compose-mtls.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
services:
2+
keycloak:
3+
environment:
4+
- KC_HTTPS_CLIENT_AUTH=required
5+
- KC_TRUSTSTORE_PATHS=/opt/keycloak/testdata/tls/ca-cert.pem,/opt/keycloak/testdata/tls/client-cert.pem

docker-compose.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,18 @@ services:
3333
- KC_FEATURES=preview,admin-fine-grained-authz:v1
3434
- QUARKUS_HTTP_ACCESS_LOG_ENABLED=true
3535
- QUARKUS_HTTP_RECORD_REQUEST_START_TIME=true
36+
- KC_HTTPS_CERTIFICATE_FILE=/opt/keycloak/testdata/tls/server-cert.pem
37+
- KC_HTTPS_CERTIFICATE_KEY_FILE=/opt/keycloak/testdata/tls/server-key.pem
3638
# Enable for remote java debugging
3739
# - DEBUG=true
3840
# - DEBUG_PORT=*:8787
41+
# - DEBUG_SUSPEND=y
3942
ports:
4043
- "8080:8080"
44+
- "8443:8443"
4145
# Enable for remote java debugging
4246
# - "8787:8787"
4347
volumes:
4448
# Make the custom-user-federation-example extension available to Keycloak. The :z option is required and tells Docker that the volume content will be shared between containers.
4549
- ./custom-user-federation-example/build/libs/custom-user-federation-example-all.jar:/opt/keycloak/providers/custom-user-federation-example-all.jar:z
46-
- ./provider/misc:/opt/keycloak/misc:z
50+
- ./provider/testdata:/opt/keycloak/testdata:z

docs/index.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,5 +111,7 @@ The following arguments are supported:
111111
- `client_timeout` - (Optional) Sets the timeout of the client when addressing Keycloak, in seconds. Defaults to the environment variable `KEYCLOAK_CLIENT_TIMEOUT`, or `15` if the environment variable is not specified.
112112
- `tls_insecure_skip_verify` - (Optional) Allows ignoring insecure certificates when set to `true`. Defaults to `false`. Disabling this security check is dangerous and should only be done in local or test environments.
113113
- `root_ca_certificate` - (Optional) Allows x509 calls using an unknown CA certificate (for development purposes)
114+
- `tls_client_certificate` - (Optional) The TLS client certificate in PEM format when the keycloak server is configured with TLS mutual authentication.
115+
- `tls_client_private_key` - (Optional) The TLS client pkcs1 private key in PEM format when the keycloak server is configured with TLS mutual authentication.
114116
- `base_path` - (Optional) The base path used for accessing the Keycloak REST API. Defaults to the environment variable `KEYCLOAK_BASE_PATH`, or an empty string if the environment variable is not specified. Note that users of the legacy distribution of Keycloak will need to set this attribute to `/auth`.
115117
- `additional_headers` - (Optional) A map of custom HTTP headers to add to each request to the Keycloak API.

helper/helpers.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package helper
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"log"
7+
"os"
8+
)
9+
10+
func UpdateEnvFromTestEnvIfPresent() {
11+
12+
testEnvFile := os.Getenv("TEST_ENV_FILE")
13+
if testEnvFile == "" {
14+
testEnvFile = "../test_env.json"
15+
}
16+
17+
if _, err := os.Stat(testEnvFile); err == nil {
18+
fmt.Printf("Using %s to load environment variables...", testEnvFile)
19+
file, err := os.Open(testEnvFile)
20+
if err != nil {
21+
log.Fatalf("Unable to open env.json: %s", err)
22+
}
23+
defer file.Close()
24+
25+
var envVars map[string]string
26+
if err := json.NewDecoder(file).Decode(&envVars); err != nil {
27+
log.Fatalf("Unable to decode env.json: %s", err)
28+
}
29+
30+
for key, value := range envVars {
31+
if err := os.Setenv(key, value); err != nil {
32+
log.Fatalf("Unable to set environment variable %s: %s", key, err)
33+
}
34+
}
35+
}
36+
}

keycloak/keycloak_client.go

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ var redHatSSO7VersionMap = map[int]string{
6767
4: "9.0.17",
6868
}
6969

70-
func NewKeycloakClient(ctx context.Context, url, basePath, adminUrl, clientId, clientSecret, realm, username, password, jwtSigningAlg, jwtSigningKey string, initialLogin bool, clientTimeout int, caCert string, tlsInsecureSkipVerify bool, userAgent string, redHatSSO bool, additionalHeaders map[string]string) (*KeycloakClient, error) {
70+
func NewKeycloakClient(ctx context.Context, url, basePath, adminUrl, clientId, clientSecret, realm, username, password, jwtSigningAlg, jwtSigningKey string, initialLogin bool, clientTimeout int, caCert string, tlsInsecureSkipVerify bool, tlsClientCert string, tlsClientPrivateKey string, userAgent string, redHatSSO bool, additionalHeaders map[string]string) (*KeycloakClient, error) {
7171
clientCredentials := &ClientCredentials{
7272
ClientId: clientId,
7373
ClientSecret: clientSecret,
@@ -89,7 +89,7 @@ func NewKeycloakClient(ctx context.Context, url, basePath, adminUrl, clientId, c
8989
}
9090
}
9191

92-
httpClient, err := newHttpClient(tlsInsecureSkipVerify, clientTimeout, caCert)
92+
httpClient, err := newHttpClient(tlsInsecureSkipVerify, clientTimeout, caCert, tlsClientCert, tlsClientPrivateKey)
9393
if err != nil {
9494
return nil, fmt.Errorf("failed to create http client: %v", err)
9595
}
@@ -562,7 +562,7 @@ func RetryPolicy(ctx context.Context, resp *http.Response, err error) (bool, err
562562
return false, nil
563563
}
564564

565-
func newHttpClient(tlsInsecureSkipVerify bool, clientTimeout int, caCert string) (*http.Client, error) {
565+
func newHttpClient(tlsInsecureSkipVerify bool, clientTimeout int, caCert string, tlsClientCert string, tlsClientPrivateKey string) (*http.Client, error) {
566566
cookieJar, err := cookiejar.New(&cookiejar.Options{
567567
PublicSuffixList: publicsuffix.List,
568568
})
@@ -582,6 +582,14 @@ func newHttpClient(tlsInsecureSkipVerify bool, clientTimeout int, caCert string)
582582
transport.TLSClientConfig.RootCAs = caCertPool
583583
}
584584

585+
if tlsClientCert != "" && tlsClientPrivateKey != "" {
586+
clientKeyPairCert, err := tls.X509KeyPair([]byte(tlsClientCert), []byte(tlsClientPrivateKey))
587+
if err != nil {
588+
return nil, err
589+
}
590+
transport.TLSClientConfig.Certificates = []tls.Certificate{clientKeyPairCert}
591+
}
592+
585593
retryClient := retryablehttp.NewClient()
586594
retryClient.CheckRetry = RetryPolicy
587595
retryClient.RetryMax = 5
@@ -647,3 +655,8 @@ func newSignedJWT(ctx context.Context, url, clientId, alg, jwtSigningKey string)
647655

648656
return tokenString, nil
649657
}
658+
659+
// Expose the underlying http client for tests
660+
func (kc *KeycloakClient) GetHttpClient() *http.Client {
661+
return kc.httpClient
662+
}

0 commit comments

Comments
 (0)