Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable custom client_authenticator_type #627

Merged
5 changes: 5 additions & 0 deletions docs/resources/openid_client.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ resource "keycloak_openid_client" "openid_client" {
URIs for security. This client should be used for applications using the Implicit grant flow.
- `BEARER-ONLY` - Used for services that never initiate a login. This client will only allow bearer token requests.
- `client_secret` - (Optional) The secret for clients with an `access_type` of `CONFIDENTIAL` or `BEARER-ONLY`. This value is sensitive and should be treated with the same care as a password. If omitted, this will be generated by Keycloak.
- `client_authenticator_type` - (Optional) Defaults to `client-secret` The authenticator type for clients with an `access_type` of `CONFIDENTIAL` or `BEARER-ONLY`. Can be one of the following:
- `client-secret` (Default) Use client id and client secret to authenticate client.
- `client-jwt` Use signed JWT to authenticate client. Set signing algorithm in `extra_config` with `attributes.token.endpoint.auth.signing.alg = <alg>`
- `client-x509` Use x509 certificate to authenticate client. Set Subject DN in `extra_config` with `attributes.x509.subjectdn = <subjectDn>`
- `client-secret-jwt` Use signed JWT with client secret to authenticate client. Set signing algorithm in `extra_config` with `attributes.token.endpoint.auth.signing.alg = <alg>`
- `standard_flow_enabled` - (Optional) When `true`, the OAuth2 Authorization Code Grant will be enabled for this client. Defaults to `false`.
- `implicit_flow_enabled` - (Optional) When `true`, the OAuth2 Implicit Grant will be enabled for this client. Defaults to `false`.
- `direct_access_grants_enabled` - (Optional) When `true`, the OAuth2 Resource Owner Password Grant will be enabled for this client. Defaults to `false`.
Expand Down
6 changes: 2 additions & 4 deletions keycloak/openid_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ type OpenidClient struct {
ClientId string `json:"clientId"`
RealmId string `json:"-"`
Name string `json:"name"`
Protocol string `json:"protocol"` // always openid-connect for this resource
ClientAuthenticatorType string `json:"clientAuthenticatorType"` // always client-secret for now, don't have a need for JWT here
Protocol string `json:"protocol"` // always openid-connect for this resource
ClientAuthenticatorType string `json:"clientAuthenticatorType"`
ClientSecret string `json:"secret,omitempty"`
Enabled bool `json:"enabled"`
Description string `json:"description"`
Expand Down Expand Up @@ -119,7 +119,6 @@ func (keycloakClient *KeycloakClient) ValidateOpenidClient(client *OpenidClient)

func (keycloakClient *KeycloakClient) NewOpenidClient(client *OpenidClient) error {
client.Protocol = "openid-connect"
client.ClientAuthenticatorType = "client-secret"

_, location, err := keycloakClient.post(fmt.Sprintf("/realms/%s/clients", client.RealmId), client)
if err != nil {
Expand Down Expand Up @@ -222,7 +221,6 @@ func (keycloakClient *KeycloakClient) GetOpenidClientByClientId(realmId, clientI

func (keycloakClient *KeycloakClient) UpdateOpenidClient(client *OpenidClient) error {
client.Protocol = "openid-connect"
client.ClientAuthenticatorType = "client-secret"

return keycloakClient.put(fmt.Sprintf("/realms/%s/clients/%s", client.RealmId, client.Id), client)
}
Expand Down
4 changes: 4 additions & 0 deletions provider/data_source_keycloak_openid_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ func dataSourceKeycloakOpenidClient() *schema.Resource {
Computed: true,
Sensitive: true,
},
"client_authenticator_type": {
Type: schema.TypeString,
Computed: true,
},
"standard_flow_enabled": {
Type: schema.TypeBool,
Computed: true,
Expand Down
9 changes: 9 additions & 0 deletions provider/resource_keycloak_openid_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ var (
keycloakOpenidClientAuthorizationPolicyEnforcementMode = []string{"ENFORCING", "PERMISSIVE", "DISABLED"}
keycloakOpenidClientResourcePermissionDecisionStrategies = []string{"UNANIMOUS", "AFFIRMATIVE", "CONSENSUS"}
keycloakOpenidClientPkceCodeChallengeMethod = []string{"", "plain", "S256"}
keycloakOpenidClientAuthenticatorTypes = []string{"client-secret", "client-jwt", "client-x509", "client-secret-jwt"}
)

func resourceKeycloakOpenidClient() *schema.Resource {
Expand Down Expand Up @@ -64,6 +65,12 @@ func resourceKeycloakOpenidClient() *schema.Resource {
Computed: true,
Sensitive: true,
},
"client_authenticator_type": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.StringInSlice(keycloakOpenidClientAuthenticatorTypes, false),
Default: "client-secret",
},
"standard_flow_enabled": {
Type: schema.TypeBool,
Optional: true,
Expand Down Expand Up @@ -293,6 +300,7 @@ func getOpenidClientFromData(data *schema.ResourceData) (*keycloak.OpenidClient,
Enabled: data.Get("enabled").(bool),
Description: data.Get("description").(string),
ClientSecret: data.Get("client_secret").(string),
ClientAuthenticatorType: data.Get("client_authenticator_type").(string),
StandardFlowEnabled: data.Get("standard_flow_enabled").(bool),
ImplicitFlowEnabled: data.Get("implicit_flow_enabled").(bool),
DirectAccessGrantsEnabled: data.Get("direct_access_grants_enabled").(bool),
Expand Down Expand Up @@ -388,6 +396,7 @@ func setOpenidClientData(keycloakClient *keycloak.KeycloakClient, data *schema.R
data.Set("enabled", client.Enabled)
data.Set("description", client.Description)
data.Set("client_secret", client.ClientSecret)
data.Set("client_authenticator_type", client.ClientAuthenticatorType)
data.Set("standard_flow_enabled", client.StandardFlowEnabled)
data.Set("implicit_flow_enabled", client.ImplicitFlowEnabled)
data.Set("direct_access_grants_enabled", client.DirectAccessGrantsEnabled)
Expand Down
58 changes: 58 additions & 0 deletions provider/resource_keycloak_openid_client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,34 @@ func TestAccKeycloakOpenidClient_accessType(t *testing.T) {
},
})
}
func TestAccKeycloakOpenidClient_clientAuthenticatorType(t *testing.T) {
t.Parallel()
clientId := acctest.RandomWithPrefix("tf-acc")

resource.Test(t, resource.TestCase{
ProviderFactories: testAccProviderFactories,
PreCheck: func() { testAccPreCheck(t) },
CheckDestroy: testAccCheckKeycloakOpenidClientDestroy(),
Steps: []resource.TestStep{
{
Config: testKeycloakOpenidClient_clientAuthenticatorType(clientId, "client-secret"),
Check: testAccCheckKeycloakOpenidClientAuthenticatorType("keycloak_openid_client.client", "client-secret"),
},
{
Config: testKeycloakOpenidClient_clientAuthenticatorType(clientId, "client-jwt"),
Check: testAccCheckKeycloakOpenidClientAuthenticatorType("keycloak_openid_client.client", "client-jwt"),
},
{
Config: testKeycloakOpenidClient_clientAuthenticatorType(clientId, "client-secret-jwt"),
Check: testAccCheckKeycloakOpenidClientAuthenticatorType("keycloak_openid_client.client", "client-secret-jwt"),
},
{
Config: testKeycloakOpenidClient_clientAuthenticatorType(clientId, "client-x509"),
Check: testAccCheckKeycloakOpenidClientAuthenticatorType("keycloak_openid_client.client", "client-x509"),
},
},
})
}

func TestAccKeycloakOpenidClient_updateInPlace(t *testing.T) {
t.Parallel()
Expand Down Expand Up @@ -796,6 +824,21 @@ func testAccCheckKeycloakOpenidClientAccessType(resourceName string, public, bea
}
}

func testAccCheckKeycloakOpenidClientAuthenticatorType(resourceName string, authType string) resource.TestCheckFunc {
return func(s *terraform.State) error {
client, err := getOpenidClientFromState(s, resourceName)
if err != nil {
return err
}

if client.ClientAuthenticatorType != authType {
return fmt.Errorf("expected openid client to have client_authenticator_type set to %s, but got %s", authType, client.ClientAuthenticatorType)
}

return nil
}
}

func testAccCheckKeycloakOpenidClientBelongsToRealm(resourceName, realm string) resource.TestCheckFunc {
return func(s *terraform.State) error {
client, err := getOpenidClientFromState(s, resourceName)
Expand Down Expand Up @@ -1092,6 +1135,21 @@ resource "keycloak_openid_client" "client" {
`, testAccRealm.Realm, clientId, accessType)
}

func testKeycloakOpenidClient_clientAuthenticatorType(clientId, authType string) string {
return fmt.Sprintf(`
data "keycloak_realm" "realm" {
realm = "%s"
}

resource "keycloak_openid_client" "client" {
realm_id = data.keycloak_realm.realm.id
client_id = "%s"
access_type = "CONFIDENTIAL"
client_authenticator_type = "%s"
}
`, testAccRealm.Realm, clientId, authType)
}

func testKeycloakOpenidClient_pkceChallengeMethod(clientId, pkceChallengeMethod string) string {

return fmt.Sprintf(`
Expand Down