diff --git a/docs/resources/realm.md b/docs/resources/realm.md index 8fbaad1dd..3a5255a55 100644 --- a/docs/resources/realm.md +++ b/docs/resources/realm.md @@ -255,6 +255,7 @@ Each of these attributes are blocks with the following attributes: - `avoid_same_authenticator_register` - (Optional) When `true`, Keycloak will avoid registering the authenticator for WebAuthn if it has already been registered. Defaults to `false`. - `acceptable_aaguids` - (Optional) A set of AAGUIDs for which an authenticator can be registered. - `extra_origins` - (Optional) A set of extra origins for non-web applications. +- `passwordless_passkeys_enabled` - (Optional) When `true`, Keycloak will enable passwordless passkey support. Defaults to `false`. ## Default Client Scopes diff --git a/keycloak/realm.go b/keycloak/realm.go index bef5c1091..60c426170 100644 --- a/keycloak/realm.go +++ b/keycloak/realm.go @@ -141,6 +141,7 @@ type Realm struct { WebAuthnPolicyPasswordlessRpId string `json:"webAuthnPolicyPasswordlessRpId"` WebAuthnPolicyPasswordlessSignatureAlgorithms []string `json:"webAuthnPolicyPasswordlessSignatureAlgorithms"` WebAuthnPolicyPasswordlessUserVerificationRequirement string `json:"webAuthnPolicyPasswordlessUserVerificationRequirement"` + WebAuthnPolicyPasswordlessPasskeysEnabled bool `json:"webAuthnPolicyPasswordlessPasskeysEnabled"` // Roles DefaultRole *Role `json:"defaultRole,omitempty"` diff --git a/provider/data_source_keycloak_realm.go b/provider/data_source_keycloak_realm.go index 1dd2a18ad..1cae5302e 100644 --- a/provider/data_source_keycloak_realm.go +++ b/provider/data_source_keycloak_realm.go @@ -95,6 +95,10 @@ func dataSourceKeycloakRealm() *schema.Resource { Description: "Either required, preferred or discouraged", Computed: true, }, + "passwordless_passkeys_enabled": { + Type: schema.TypeBool, + Computed: true, + }, } return &schema.Resource{ ReadContext: dataSourceKeycloakRealmRead, diff --git a/provider/resource_keycloak_realm.go b/provider/resource_keycloak_realm.go index 226e75b02..d7a5596a9 100644 --- a/provider/resource_keycloak_realm.go +++ b/provider/resource_keycloak_realm.go @@ -1220,6 +1220,10 @@ func getRealmFromData(data *schema.ResourceData, keycloakVersion *version.Versio if webAuthnPolicyPasswordlessUserVerificationRequirement, ok := webAuthnPasswordlessPolicy["user_verification_requirement"]; ok { realm.WebAuthnPolicyPasswordlessUserVerificationRequirement = webAuthnPolicyPasswordlessUserVerificationRequirement.(string) } + + if webAuthnPolicyPasswordlessPasskeysEnabled, ok := webAuthnPasswordlessPolicy["passwordless_passkeys_enabled"]; ok { + realm.WebAuthnPolicyPasswordlessPasskeysEnabled = webAuthnPolicyPasswordlessPasskeysEnabled.(bool) + } } return realm, nil @@ -1423,6 +1427,7 @@ func setRealmData(data *schema.ResourceData, realm *keycloak.Realm, keycloakVers webAuthnPasswordlessPolicy["relying_party_id"] = realm.WebAuthnPolicyPasswordlessRpId webAuthnPasswordlessPolicy["signature_algorithms"] = realm.WebAuthnPolicyPasswordlessSignatureAlgorithms webAuthnPasswordlessPolicy["user_verification_requirement"] = realm.WebAuthnPolicyPasswordlessUserVerificationRequirement + webAuthnPasswordlessPolicy["passwordless_passkeys_enabled"] = realm.WebAuthnPolicyPasswordlessPasskeysEnabled data.Set("web_authn_passwordless_policy", []interface{}{webAuthnPasswordlessPolicy}) attributes := map[string]interface{}{} diff --git a/provider/resource_keycloak_realm_test.go b/provider/resource_keycloak_realm_test.go index 6b274a3b1..e72b950aa 100644 --- a/provider/resource_keycloak_realm_test.go +++ b/provider/resource_keycloak_realm_test.go @@ -989,6 +989,7 @@ func TestAccKeycloakRealm_webauthn(t *testing.T) { userVerificationRequirement := randomStringInSlice([]string{"not specified", "required", "preferred", "discouraged"}) signatureAlgorithms := randomStringSliceSubset([]string{"ES256", "ES384", "ES512", "RS256", "ES384", "ES512"}) avoidSameAuthenticatorRegister := randomBool() + passwordlessPasskeysEnabled := randomBool() resource.Test(t, resource.TestCase{ ProviderFactories: testAccProviderFactories, @@ -1000,7 +1001,7 @@ func TestAccKeycloakRealm_webauthn(t *testing.T) { Check: testAccCheckKeycloakRealmExists("keycloak_realm.realm"), }, { - Config: testKeycloakRealm_webauthn_passwordless_policy(realmName, realmDisplayName, realmDisplayNameHtml, rpName, rpId, attestationConveyancePreference, authenticatorAttachment, requireResidentKey, userVerificationRequirement, signatureAlgorithms, avoidSameAuthenticatorRegister), + Config: testKeycloakRealm_webauthn_passwordless_policy(realmName, realmDisplayName, realmDisplayNameHtml, rpName, rpId, attestationConveyancePreference, authenticatorAttachment, requireResidentKey, userVerificationRequirement, signatureAlgorithms, avoidSameAuthenticatorRegister, passwordlessPasskeysEnabled), Check: testAccCheckKeycloakRealmExists("keycloak_realm.realm"), }, { @@ -1781,7 +1782,7 @@ resource "keycloak_realm" "realm" { `, realm, realmDisplayName, realmDisplayNameHtml, rpName, rpId, arrayOfStringsForTerraformResource(signatureAlgorithms), attestationConveyancePreference, authenticatorAttachment, avoidSameAuthenticatorRegister, requireResidentKey, userVerificationRequirement) } -func testKeycloakRealm_webauthn_passwordless_policy(realm, realmDisplayName, realmDisplayNameHtml, rpName, rpId, attestationConveyancePreference, authenticatorAttachment, requireResidentKey, userVerificationRequirement string, signatureAlgorithms []string, avoidSameAuthenticatorRegister bool) string { +func testKeycloakRealm_webauthn_passwordless_policy(realm, realmDisplayName, realmDisplayNameHtml, rpName, rpId, attestationConveyancePreference, authenticatorAttachment, requireResidentKey, userVerificationRequirement string, signatureAlgorithms []string, avoidSameAuthenticatorRegister bool, passwordlessPasskeysEnabled bool) string { return fmt.Sprintf(` resource "keycloak_realm" "realm" { realm = "%s" @@ -1799,9 +1800,10 @@ resource "keycloak_realm" "realm" { avoid_same_authenticator_register = %t require_resident_key = "%s" user_verification_requirement = "%s" + passwordless_passkeys_enabled = %t } } - `, realm, realmDisplayName, realmDisplayNameHtml, rpName, rpId, arrayOfStringsForTerraformResource(signatureAlgorithms), attestationConveyancePreference, authenticatorAttachment, avoidSameAuthenticatorRegister, requireResidentKey, userVerificationRequirement) + `, realm, realmDisplayName, realmDisplayNameHtml, rpName, rpId, arrayOfStringsForTerraformResource(signatureAlgorithms), attestationConveyancePreference, authenticatorAttachment, avoidSameAuthenticatorRegister, requireResidentKey, userVerificationRequirement, passwordlessPasskeysEnabled) } func testKeycloakRealm_basicInternalId(realm, internalId string) string {