diff --git a/terraform/keycloak/main.tf b/terraform/keycloak/main.tf index c931aab..ce7b9ad 100644 --- a/terraform/keycloak/main.tf +++ b/terraform/keycloak/main.tf @@ -41,6 +41,98 @@ resource "keycloak_required_action" "webauthn_register_passwordless" { default_action = false } +# ============================================================================= +# Browser flow with Passkey support +# ============================================================================= + +resource "keycloak_authentication_flow" "browser_with_passkey" { + realm_id = keycloak_realm.hexor.id + alias = "browser-with-passkey" + description = "Browser flow with passkey/password alternatives" + provider_id = "basic-flow" +} + +# --- Cookie (re-use existing session) --- + +resource "keycloak_authentication_execution" "cookie" { + realm_id = keycloak_realm.hexor.id + parent_flow_alias = keycloak_authentication_flow.browser_with_passkey.alias + authenticator = "auth-cookie" + requirement = "ALTERNATIVE" + priority = 10 +} + +# --- Identity Provider Redirector --- + +resource "keycloak_authentication_execution" "idp_redirector" { + realm_id = keycloak_realm.hexor.id + parent_flow_alias = keycloak_authentication_flow.browser_with_passkey.alias + authenticator = "identity-provider-redirector" + requirement = "ALTERNATIVE" + priority = 20 +} + +# --- Passkey (WebAuthn Passwordless) --- + +resource "keycloak_authentication_execution" "passkey" { + realm_id = keycloak_realm.hexor.id + parent_flow_alias = keycloak_authentication_flow.browser_with_passkey.alias + authenticator = "webauthn-authenticator-passwordless" + requirement = "ALTERNATIVE" + priority = 30 +} + +# --- Username/Password + optional 2FA subflow --- + +resource "keycloak_authentication_subflow" "forms" { + realm_id = keycloak_realm.hexor.id + parent_flow_alias = keycloak_authentication_flow.browser_with_passkey.alias + alias = "browser-with-passkey-forms" + provider_id = "basic-flow" + requirement = "ALTERNATIVE" + priority = 40 +} + +resource "keycloak_authentication_execution" "username_password" { + realm_id = keycloak_realm.hexor.id + parent_flow_alias = keycloak_authentication_subflow.forms.alias + authenticator = "auth-username-password-form" + requirement = "REQUIRED" + priority = 10 +} + +resource "keycloak_authentication_subflow" "conditional_2fa" { + realm_id = keycloak_realm.hexor.id + parent_flow_alias = keycloak_authentication_subflow.forms.alias + alias = "browser-with-passkey-conditional-2fa" + provider_id = "basic-flow" + requirement = "CONDITIONAL" + priority = 20 +} + +resource "keycloak_authentication_execution" "condition_user_configured" { + realm_id = keycloak_realm.hexor.id + parent_flow_alias = keycloak_authentication_subflow.conditional_2fa.alias + authenticator = "conditional-user-configured" + requirement = "REQUIRED" + priority = 10 +} + +resource "keycloak_authentication_execution" "otp_form" { + realm_id = keycloak_realm.hexor.id + parent_flow_alias = keycloak_authentication_subflow.conditional_2fa.alias + authenticator = "auth-otp-form" + requirement = "ALTERNATIVE" + priority = 20 +} + +# --- Bind the new flow as the browser flow --- + +resource "keycloak_authentication_bindings" "browser" { + realm_id = keycloak_realm.hexor.id + browser_flow = keycloak_authentication_flow.browser_with_passkey.alias +} + # ============================================================================= # Google Identity Provider # =============================================================================