Skip to content

Commit b656057

Browse files
authored
Add X509 TLS client authentication (#10)
Co-authored-by: lucdew <[email protected]>
1 parent 02f6350 commit b656057

File tree

5 files changed

+22
-6
lines changed

5 files changed

+22
-6
lines changed

docs/index.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,8 @@ The following arguments are supported:
8787
- `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.
8888
- `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.
8989
- `root_ca_certificate` - (Optional) Allows x509 calls using an unknown CA certificate (for development purposes)
90-
- `tls_client_certificate` - (Optional) The TLS client certificate in PEM format when the keycloak server is configured with TLS mutual authentication.
90+
- `tls_client_certificate` - (Optional) The TLS client certificate in PEM format when the Keycloak server is configured with TLS mutual authentication.
91+
- `tls_client_auth` - (Optional) When true, also uses the TLS client certificate for Keycloak X509 authentication.
9192
- `tls_client_private_key` - (Optional) The TLS client pkcs1 private key in PEM format when the keycloak server is configured with TLS mutual authentication.
9293
- `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`.
9394
- `additional_headers` - (Optional) A map of custom HTTP headers to add to each request to the Keycloak API.

keycloak/keycloak_client.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ type KeycloakClient struct {
3636
additionalHeaders map[string]string
3737
debug bool
3838
redHatSSO bool
39+
tlsClientAuth bool
3940
}
4041

4142
type ClientCredentials struct {
@@ -61,7 +62,7 @@ var redHatSSO7VersionMap = map[int]string{
6162
4: "9.0.17",
6263
}
6364

64-
func NewKeycloakClient(ctx context.Context, url, basePath, clientId, clientSecret, realm, username, password string, initialLogin bool, clientTimeout int, caCert string, tlsClientCert string, tlsClientPrivateKey string, tlsInsecureSkipVerify bool, userAgent string, redHatSSO bool, additionalHeaders map[string]string) (*KeycloakClient, error) {
65+
func NewKeycloakClient(ctx context.Context, url, basePath, clientId, clientSecret, realm, username, password string, initialLogin bool, clientTimeout int, caCert string, tlsClientCert string, tlsClientAuth bool, tlsClientPrivateKey string, tlsInsecureSkipVerify bool, userAgent string, redHatSSO bool, additionalHeaders map[string]string) (*KeycloakClient, error) {
6566
clientCredentials := &ClientCredentials{
6667
ClientId: clientId,
6768
ClientSecret: clientSecret,
@@ -72,6 +73,8 @@ func NewKeycloakClient(ctx context.Context, url, basePath, clientId, clientSecre
7273
clientCredentials.GrantType = "password"
7374
} else if clientSecret != "" {
7475
clientCredentials.GrantType = "client_credentials"
76+
} else if tlsClientAuth {
77+
clientCredentials.GrantType = "client_credentials"
7578
} else {
7679
if initialLogin {
7780
return nil, fmt.Errorf("must specify client id, username and password for password grant, or client id and secret for client credentials grant")
@@ -94,6 +97,7 @@ func NewKeycloakClient(ctx context.Context, url, basePath, clientId, clientSecre
9497
userAgent: userAgent,
9598
redHatSSO: redHatSSO,
9699
additionalHeaders: additionalHeaders,
100+
tlsClientAuth: tlsClientAuth,
97101
}
98102

99103
if keycloakClient.initialLogin {
@@ -271,10 +275,14 @@ func (keycloakClient *KeycloakClient) getAuthenticationFormData() url.Values {
271275
authenticationFormData.Set("client_secret", keycloakClient.clientCredentials.ClientSecret)
272276
}
273277

274-
} else if keycloakClient.clientCredentials.GrantType == "client_credentials" {
278+
} else if keycloakClient.clientCredentials.GrantType == "client_credentials" && keycloakClient.clientCredentials.ClientSecret != "" {
275279
authenticationFormData.Set("client_secret", keycloakClient.clientCredentials.ClientSecret)
276280
}
277281

282+
if keycloakClient.tlsClientAuth {
283+
authenticationFormData.Set("client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:tls_client_auth")
284+
}
285+
278286
return authenticationFormData
279287
}
280288

keycloak/keycloak_client_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ func TestAccKeycloakApiClientRefresh(t *testing.T) {
4949
t.Fatal("KEYCLOAK_CLIENT_TIMEOUT must be an integer")
5050
}
5151

52-
keycloakClient, err := NewKeycloakClient(ctx, os.Getenv("KEYCLOAK_URL"), "", os.Getenv("KEYCLOAK_CLIENT_ID"), os.Getenv("KEYCLOAK_CLIENT_SECRET"), os.Getenv("KEYCLOAK_REALM"), os.Getenv("KEYCLOAK_USER"), os.Getenv("KEYCLOAK_PASSWORD"), true, clientTimeout, "", "", "", false, "", false, map[string]string{
52+
keycloakClient, err := NewKeycloakClient(ctx, os.Getenv("KEYCLOAK_URL"), "", os.Getenv("KEYCLOAK_CLIENT_ID"), os.Getenv("KEYCLOAK_CLIENT_SECRET"), os.Getenv("KEYCLOAK_REALM"), os.Getenv("KEYCLOAK_USER"), os.Getenv("KEYCLOAK_PASSWORD"), true, clientTimeout, "", "", false, "", false, "", false, map[string]string{
5353
"foo": "bar",
5454
})
5555
if err != nil {

provider/provider.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,12 @@ func KeycloakProvider(client *keycloak.KeycloakClient) *schema.Provider {
180180
Description: "Allows ignoring insecure certificates when set to true. Defaults to false. Disabling security check is dangerous and should be avoided.",
181181
Default: false,
182182
},
183+
"tls_client_auth": {
184+
Optional: true,
185+
Type: schema.TypeString,
186+
Description: "When true, uses also the TLS client certificate for authentication in Keycloak",
187+
Default: "",
188+
},
183189
"tls_client_certificate": {
184190
Optional: true,
185191
Type: schema.TypeString,
@@ -229,6 +235,7 @@ func KeycloakProvider(client *keycloak.KeycloakClient) *schema.Provider {
229235
clientTimeout := data.Get("client_timeout").(int)
230236
tlsInsecureSkipVerify := data.Get("tls_insecure_skip_verify").(bool)
231237
tlsClientCertificate := data.Get("tls_client_certificate").(string)
238+
tlsClientAuth := data.Get("tls_client_auth").(bool)
232239
tlsClientPrivateKey := data.Get("tls_client_private_key").(string)
233240
rootCaCertificate := data.Get("root_ca_certificate").(string)
234241
redHatSSO := data.Get("red_hat_sso").(bool)
@@ -241,7 +248,7 @@ func KeycloakProvider(client *keycloak.KeycloakClient) *schema.Provider {
241248

242249
userAgent := fmt.Sprintf("HashiCorp Terraform/%s (+https://www.terraform.io) Terraform Plugin SDK/%s", provider.TerraformVersion, meta.SDKVersionString())
243250

244-
keycloakClient, err := keycloak.NewKeycloakClient(ctx, url, basePath, clientId, clientSecret, realm, username, password, initialLogin, clientTimeout, rootCaCertificate, tlsClientCertificate, tlsClientPrivateKey, tlsInsecureSkipVerify, userAgent, redHatSSO, additionalHeaders)
251+
keycloakClient, err := keycloak.NewKeycloakClient(ctx, url, basePath, clientId, clientSecret, realm, username, password, initialLogin, clientTimeout, rootCaCertificate, tlsClientCertificate, tlsClientAuth, tlsClientPrivateKey, tlsInsecureSkipVerify, userAgent, redHatSSO, additionalHeaders)
245252
if err != nil {
246253
diags = append(diags, diag.Diagnostic{
247254
Severity: diag.Error,

provider/provider_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ func init() {
6060
}
6161
}
6262

63-
keycloakClient, err = keycloak.NewKeycloakClient(testCtx, os.Getenv("KEYCLOAK_URL"), "", os.Getenv("KEYCLOAK_CLIENT_ID"), os.Getenv("KEYCLOAK_CLIENT_SECRET"), os.Getenv("KEYCLOAK_REALM"), "", "", true, 5, "", "", "", false, userAgent, false, map[string]string{
63+
keycloakClient, err = keycloak.NewKeycloakClient(testCtx, os.Getenv("KEYCLOAK_URL"), "", os.Getenv("KEYCLOAK_CLIENT_ID"), os.Getenv("KEYCLOAK_CLIENT_SECRET"), os.Getenv("KEYCLOAK_REALM"), "", "", true, 5, "", "", false, "", false, userAgent, false, map[string]string{
6464
"foo": "bar",
6565
})
6666
if err != nil {

0 commit comments

Comments
 (0)