From c86aaa97fc307b9196ce0c2c11a9f4b5ceab7514 Mon Sep 17 00:00:00 2001 From: Lennart Fleischmann <67686424+lfleischmann@users.noreply.github.com> Date: Thu, 1 Aug 2024 10:10:43 +0200 Subject: [PATCH] fix: onboarding states generation This commit fixes the following issues: - Generation of onboarding states on registration does not consider all possible values for the acquire_on_registration configuration option. - Generation of onboarding states on login does not consider WebAuthn availability/capability. --- .../login/hook_schedule_onboarding_states.go | 109 ++++++++++-------- .../action_register_login_identifier.go | 56 +++++---- 2 files changed, 92 insertions(+), 73 deletions(-) diff --git a/backend/flow_api/flow/login/hook_schedule_onboarding_states.go b/backend/flow_api/flow/login/hook_schedule_onboarding_states.go index ad61ff407..55c410673 100644 --- a/backend/flow_api/flow/login/hook_schedule_onboarding_states.go +++ b/backend/flow_api/flow/login/hook_schedule_onboarding_states.go @@ -47,57 +47,68 @@ func (h ScheduleOnboardingStates) determineCredentialOnboardingStates(c flowpilo cfg := deps.Cfg result := make([]flowpilot.StateName, 0) - alwaysAcquirePasskey := cfg.Passkey.Enabled && cfg.Passkey.AcquireOnLogin == "always" - alwaysAcquirePassword := cfg.Password.Enabled && cfg.Password.AcquireOnLogin == "always" - conditionalAcquirePasskey := cfg.Passkey.Enabled && cfg.Passkey.AcquireOnLogin == "conditional" - conditionalAcquirePassword := cfg.Password.Enabled && cfg.Password.AcquireOnLogin == "conditional" - neverAcquirePasskey := !cfg.Passkey.Enabled || cfg.Passkey.AcquireOnLogin == "never" - neverAcquirePassword := !cfg.Password.Enabled || cfg.Password.AcquireOnLogin == "never" - - if alwaysAcquirePasskey && alwaysAcquirePassword { - if !hasPasskey && !hasPassword { - if !cfg.Password.Optional && cfg.Passkey.Optional { - result = append(result, shared.StatePasswordCreation, shared.StateOnboardingCreatePasskey) - } else { - result = append(result, shared.StateOnboardingCreatePasskey, shared.StatePasswordCreation) + webauthnAvailable := c.Stash().Get(shared.StashPathWebauthnAvailable).Bool() + passkeyEnabled := webauthnAvailable && deps.Cfg.Passkey.Enabled + passwordEnabled := deps.Cfg.Password.Enabled + passwordAndPasskeyEnabled := passkeyEnabled && passwordEnabled + + alwaysAcquirePasskey := cfg.Passkey.AcquireOnLogin == "always" + alwaysAcquirePassword := cfg.Password.AcquireOnLogin == "always" + conditionalAcquirePasskey := cfg.Passkey.AcquireOnLogin == "conditional" + conditionalAcquirePassword := cfg.Password.AcquireOnLogin == "conditional" + neverAcquirePasskey := cfg.Passkey.AcquireOnLogin == "never" + neverAcquirePassword := cfg.Password.AcquireOnLogin == "never" + + if passwordAndPasskeyEnabled { + if alwaysAcquirePasskey && alwaysAcquirePassword { + if !hasPasskey && !hasPassword { + if !cfg.Password.Optional && cfg.Passkey.Optional { + result = append(result, shared.StatePasswordCreation, shared.StateOnboardingCreatePasskey) + } else { + result = append(result, shared.StateOnboardingCreatePasskey, shared.StatePasswordCreation) + } + } else if hasPasskey && !hasPassword { + result = append(result, shared.StatePasswordCreation) + } else if !hasPasskey && hasPassword { + result = append(result, shared.StateOnboardingCreatePasskey) + } + } else if alwaysAcquirePasskey && conditionalAcquirePassword { + if !hasPasskey && !hasPassword { + result = append(result, shared.StateOnboardingCreatePasskey) // skip should lead to password onboarding + } else if !hasPasskey && hasPassword { + result = append(result, shared.StateOnboardingCreatePasskey) + } + } else if conditionalAcquirePasskey && alwaysAcquirePassword { + if !hasPasskey && !hasPassword { + result = append(result, shared.StatePasswordCreation) // skip should lead to passkey onboarding + } else if hasPasskey && !hasPassword { + result = append(result, shared.StatePasswordCreation) + } + } else if conditionalAcquirePasskey && conditionalAcquirePassword { + if !hasPasskey && !hasPassword { + result = append(result, shared.StateCredentialOnboardingChooser) // credential_onboarding_chooser can be skipped + } + } else if conditionalAcquirePasskey && neverAcquirePassword { + if !hasPasskey && !hasPassword { + result = append(result, shared.StateOnboardingCreatePasskey) + } + } else if neverAcquirePasskey && conditionalAcquirePassword { + if !hasPasskey && !hasPassword { + result = append(result, shared.StatePasswordCreation) + } + } else if neverAcquirePasskey && alwaysAcquirePassword { + if !hasPassword { + result = append(result, shared.StatePasswordCreation) + } + } else if alwaysAcquirePasskey && neverAcquirePassword { + if !hasPasskey { + result = append(result, shared.StateOnboardingCreatePasskey) } - } else if hasPasskey && !hasPassword { - result = append(result, shared.StatePasswordCreation) - } else if !hasPasskey && hasPassword { - result = append(result, shared.StateOnboardingCreatePasskey) - } - } else if alwaysAcquirePasskey && conditionalAcquirePassword { - if !hasPasskey && !hasPassword { - result = append(result, shared.StateOnboardingCreatePasskey) // skip should lead to password onboarding - } else if !hasPasskey && hasPassword { - result = append(result, shared.StateOnboardingCreatePasskey) - } - } else if conditionalAcquirePasskey && alwaysAcquirePassword { - if !hasPasskey && !hasPassword { - result = append(result, shared.StatePasswordCreation) // skip should lead to passkey onboarding - } else if hasPasskey && !hasPassword { - result = append(result, shared.StatePasswordCreation) - } - } else if conditionalAcquirePasskey && conditionalAcquirePassword { - if !hasPasskey && !hasPassword { - result = append(result, shared.StateCredentialOnboardingChooser) // credential_onboarding_chooser can be skipped - } - } else if conditionalAcquirePasskey && neverAcquirePassword { - if !hasPasskey && !hasPassword { - result = append(result, shared.StateOnboardingCreatePasskey) - } - } else if neverAcquirePasskey && conditionalAcquirePassword { - if !hasPasskey && !hasPassword { - result = append(result, shared.StatePasswordCreation) - } - } else if neverAcquirePasskey && alwaysAcquirePassword { - if !hasPassword { - result = append(result, shared.StatePasswordCreation) - } - } else if alwaysAcquirePasskey && neverAcquirePassword { - if !hasPasskey { - result = append(result, shared.StateOnboardingCreatePasskey) } + } else if passkeyEnabled && (alwaysAcquirePasskey || conditionalAcquirePasskey) { + result = append(result, shared.StateOnboardingCreatePasskey) + } else if passwordEnabled && (alwaysAcquirePassword || conditionalAcquirePassword) { + result = append(result, shared.StatePasswordCreation) } return result diff --git a/backend/flow_api/flow/registration/action_register_login_identifier.go b/backend/flow_api/flow/registration/action_register_login_identifier.go index 2c65b344c..27bb41cd4 100644 --- a/backend/flow_api/flow/registration/action_register_login_identifier.go +++ b/backend/flow_api/flow/registration/action_register_login_identifier.go @@ -166,42 +166,50 @@ func (a RegisterLoginIdentifier) Execute(c flowpilot.ExecutionContext) error { func (a RegisterLoginIdentifier) generateRegistrationStates(c flowpilot.ExecutionContext) []flowpilot.StateName { deps := a.GetDeps(c) - stateNames := make([]flowpilot.StateName, 0) + result := make([]flowpilot.StateName, 0) emailExists := len(c.Input().Get("email").String()) > 0 if emailExists && deps.Cfg.Email.RequireVerification { - stateNames = append(stateNames, shared.StatePasscodeConfirmation) + result = append(result, shared.StatePasscodeConfirmation) } webauthnAvailable := c.Stash().Get(shared.StashPathWebauthnAvailable).Bool() passkeyEnabled := webauthnAvailable && deps.Cfg.Passkey.Enabled passwordEnabled := deps.Cfg.Password.Enabled - bothEnabled := passkeyEnabled && passwordEnabled - - alwaysPasskey := deps.Cfg.Passkey.AcquireOnRegistration == "always" - conditionalPasskey := deps.Cfg.Passkey.AcquireOnRegistration == "conditional" - alwaysPassword := deps.Cfg.Password.AcquireOnRegistration == "always" - conditionalPassword := deps.Cfg.Password.AcquireOnRegistration == "conditional" - - if bothEnabled { - if conditionalPasskey && conditionalPassword { - stateNames = append(stateNames, shared.StateCredentialOnboardingChooser) - } else if alwaysPasskey && !alwaysPassword { - stateNames = append(stateNames, shared.StateOnboardingCreatePasskey) - } else if !alwaysPasskey && alwaysPassword { - stateNames = append(stateNames, shared.StatePasswordCreation) - } else if alwaysPassword && alwaysPasskey { + passwordAndPasskeyEnabled := passkeyEnabled && passwordEnabled + + alwaysAcquirePasskey := deps.Cfg.Passkey.AcquireOnRegistration == "always" + conditionalAcquirePasskey := deps.Cfg.Passkey.AcquireOnRegistration == "conditional" + alwaysAcquirePassword := deps.Cfg.Password.AcquireOnRegistration == "always" + conditionalAcquirePassword := deps.Cfg.Password.AcquireOnRegistration == "conditional" + neverAcquirePasskey := deps.Cfg.Passkey.AcquireOnLogin == "never" + neverAcquirePassword := deps.Cfg.Password.AcquireOnLogin == "never" + + if passwordAndPasskeyEnabled { + if alwaysAcquirePasskey && alwaysAcquirePassword { if !deps.Cfg.Password.Optional && deps.Cfg.Passkey.Optional { - stateNames = append(stateNames, shared.StatePasswordCreation, shared.StateOnboardingCreatePasskey) + result = append(result, shared.StatePasswordCreation, shared.StateOnboardingCreatePasskey) } else { - stateNames = append(stateNames, shared.StateOnboardingCreatePasskey, shared.StatePasswordCreation) + result = append(result, shared.StateOnboardingCreatePasskey, shared.StatePasswordCreation) } + } else if alwaysAcquirePasskey && conditionalAcquirePassword { + result = append(result, shared.StateOnboardingCreatePasskey) + } else if conditionalAcquirePasskey && alwaysAcquirePassword { + result = append(result, shared.StatePasswordCreation) + } else if conditionalAcquirePasskey && conditionalAcquirePassword { + result = append(result, shared.StateCredentialOnboardingChooser) + } else if conditionalAcquirePasskey && neverAcquirePassword { + result = append(result, shared.StateOnboardingCreatePasskey) + } else if neverAcquirePasskey && (alwaysAcquirePassword || conditionalAcquirePassword) { + result = append(result, shared.StatePasswordCreation) + } else if (alwaysAcquirePasskey || conditionalAcquirePasskey) && neverAcquirePassword { + result = append(result, shared.StateOnboardingCreatePasskey) } - } else if passkeyEnabled && (alwaysPasskey || conditionalPasskey) { - stateNames = append(stateNames, shared.StateOnboardingCreatePasskey) - } else if passwordEnabled && (alwaysPassword || conditionalPassword) { - stateNames = append(stateNames, shared.StatePasswordCreation) + } else if passkeyEnabled && (alwaysAcquirePasskey || conditionalAcquirePasskey) { + result = append(result, shared.StateOnboardingCreatePasskey) + } else if passwordEnabled && (alwaysAcquirePassword || conditionalAcquirePassword) { + result = append(result, shared.StatePasswordCreation) } - return stateNames + return result }