diff --git a/docs/resources/openid_client.md b/docs/resources/openid_client.md index f2fb508a1..44d9cbd20 100644 --- a/docs/resources/openid_client.md +++ b/docs/resources/openid_client.md @@ -72,6 +72,7 @@ is set to `true`. - `direct_grant_id` - (Optional) Direct grant flow id (flow needs to exist) - `login_theme` - (Optional) The client login theme. This will override the default theme for the realm. - `exclude_session_state_from_auth_response` - (Optional) When `true`, the parameter `session_state` will not be included in OpenID Connect Authentication Response. +- `use_refresh_tokens` - (Optional) If this is `true`, a refresh_token will be created and added to the token response. If this is `false` then no refresh_token will be generated. Defaults to `true`. - `authorization` - (Optional) When this block is present, fine-grained authorization will be enabled for this client. The client's `access_type` must be `CONFIDENTIAL`, and `service_accounts_enabled` must be `true`. This block has the following arguments: - `policy_enforcement_mode` - (Required) Dictates how policies are enforced when evaluating authorization requests. Can be one of `ENFORCING`, `PERMISSIVE`, or `DISABLED`. - `decision_strategy` - (Optional) Dictates how the policies associated with a given permission are evaluated and how a final decision is obtained. Could be one of `AFFIRMATIVE`, `CONSENSUS`, or `UNANIMOUS`. Applies to permissions. diff --git a/keycloak/openid_client.go b/keycloak/openid_client.go index ee68012f9..6efb1f945 100644 --- a/keycloak/openid_client.go +++ b/keycloak/openid_client.go @@ -63,6 +63,7 @@ type OpenidClientAttributes struct { ClientOfflineSessionMaxLifespan string `json:"client.offline.session.max.lifespan,omitempty"` ClientSessionIdleTimeout string `json:"client.session.idle.timeout,omitempty"` ClientSessionMaxLifespan string `json:"client.session.max.lifespan,omitempty"` + UseRefreshTokens KeycloakBoolQuoted `json:"use.refresh.tokens"` } type OpenidAuthenticationFlowBindingOverrides struct { diff --git a/provider/data_source_keycloak_openid_client.go b/provider/data_source_keycloak_openid_client.go index c1e18e796..80306aa2c 100644 --- a/provider/data_source_keycloak_openid_client.go +++ b/provider/data_source_keycloak_openid_client.go @@ -167,6 +167,11 @@ func dataSourceKeycloakOpenidClient() *schema.Resource { Type: schema.TypeString, Computed: true, }, + "use_refresh_tokens": { + Type: schema.TypeBool, + Optional: true, + Default: true, + }, }, } } diff --git a/provider/resource_keycloak_openid_client.go b/provider/resource_keycloak_openid_client.go index 3ee896426..ddee8dd1f 100644 --- a/provider/resource_keycloak_openid_client.go +++ b/provider/resource_keycloak_openid_client.go @@ -206,6 +206,11 @@ func resourceKeycloakOpenidClient() *schema.Resource { Type: schema.TypeString, Optional: true, }, + "use_refresh_tokens": { + Type: schema.TypeBool, + Optional: true, + Default: true, + }, }, CustomizeDiff: customdiff.ComputedIf("service_account_user_id", func(ctx context.Context, d *schema.ResourceDiff, meta interface{}) bool { return d.HasChange("service_accounts_enabled") @@ -270,6 +275,7 @@ func getOpenidClientFromData(data *schema.ResourceData) (*keycloak.OpenidClient, ClientOfflineSessionMaxLifespan: data.Get("client_offline_session_max_lifespan").(string), ClientSessionIdleTimeout: data.Get("client_session_idle_timeout").(string), ClientSessionMaxLifespan: data.Get("client_session_max_lifespan").(string), + UseRefreshTokens: keycloak.KeycloakBoolQuoted(data.Get("use_refresh_tokens").(bool)), }, ValidRedirectUris: validRedirectUris, WebOrigins: webOrigins, @@ -357,6 +363,7 @@ func setOpenidClientData(keycloakClient *keycloak.KeycloakClient, data *schema.R data.Set("access_token_lifespan", client.Attributes.AccessTokenLifespan) data.Set("login_theme", client.Attributes.LoginTheme) + data.Set("use_refresh_tokens", client.Attributes.UseRefreshTokens) data.Set("client_offline_session_idle_timeout", client.Attributes.ClientOfflineSessionIdleTimeout) data.Set("client_offline_session_max_lifespan", client.Attributes.ClientOfflineSessionMaxLifespan) data.Set("client_session_idle_timeout", client.Attributes.ClientSessionIdleTimeout) diff --git a/provider/resource_keycloak_openid_client_test.go b/provider/resource_keycloak_openid_client_test.go index 7e6d54920..45c720ac6 100644 --- a/provider/resource_keycloak_openid_client_test.go +++ b/provider/resource_keycloak_openid_client_test.go @@ -491,6 +491,27 @@ func TestAccKeycloakOpenidClient_loginTheme(t *testing.T) { }) } +func TestAccKeycloakOpenidClient_useRefreshTokens(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_useRefreshTokens(clientId, true), + Check: testAccCheckKeycloakOpenidClientUseRefreshTokens("keycloak_openid_client.client", true), + }, + { + Config: testKeycloakOpenidClient_useRefreshTokens(clientId, false), + Check: testAccCheckKeycloakOpenidClientUseRefreshTokens("keycloak_openid_client.client", false), + }, + }, + }) +} + func testAccCheckKeycloakOpenidClientExistsWithCorrectProtocol(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { client, err := getOpenidClientFromState(s, resourceName) @@ -728,6 +749,21 @@ func testAccCheckKeycloakOpenidClientLoginTheme(resourceName string, loginTheme } } +func testAccCheckKeycloakOpenidClientUseRefreshTokens(resourceName string, useRefreshTokens bool) resource.TestCheckFunc { + return func(s *terraform.State) error { + client, err := getOpenidClientFromState(s, resourceName) + if err != nil { + return err + } + + if client.Attributes.UseRefreshTokens != keycloak.KeycloakBoolQuoted(useRefreshTokens) { + return fmt.Errorf("expected openid client to have use refresh tokens set to %t, but got %v", useRefreshTokens, client.Attributes.UseRefreshTokens) + } + + return nil + } +} + func getOpenidClientFromState(s *terraform.State, resourceName string) (*keycloak.OpenidClient, error) { rs, ok := s.RootModule().Resources[resourceName] if !ok { @@ -1063,3 +1099,19 @@ resource "keycloak_openid_client" "client" { } `, testAccRealm.Realm, clientId, loginTheme) } + +func testKeycloakOpenidClient_useRefreshTokens(clientId string, useRefreshTokens bool) string { + + return fmt.Sprintf(` +data "keycloak_realm" "realm" { + realm = "%s" +} + +resource "keycloak_openid_client" "client" { + client_id = "%s" + realm_id = data.keycloak_realm.realm.id + access_type = "CONFIDENTIAL" + use_refresh_tokens = %t +} + `, testAccRealm.Realm, clientId, useRefreshTokens) +}