diff --git a/authui/src/authflowv2/botprotection/botProtection.ts b/authui/src/authflowv2/botprotection/botProtection.ts index 574805caa2..175ee33cf0 100644 --- a/authui/src/authflowv2/botprotection/botProtection.ts +++ b/authui/src/authflowv2/botprotection/botProtection.ts @@ -1,5 +1,8 @@ import { Controller } from "@hotwired/stimulus"; -import { dispatchBotProtectionDialogOpen } from "./botProtectionDialog"; +import { + dispatchBotProtectionDialogClose, + dispatchBotProtectionDialogOpen, +} from "./botProtectionDialog"; /** * Dispatch a custom event to set captcha verified with success token @@ -77,7 +80,10 @@ export class BotProtectionController extends Controller { this.isVerified = true; // Wait for bot-protection-token-input to process "bot-protection:verify-success" event // so that the form submission will have bot protection token injected. - setTimeout(() => this.formSubmitTarget?.click(), 0); + setTimeout(() => { + this.formSubmitTarget?.click(); + dispatchBotProtectionDialogClose(); + }, 0); }; onVerifyFailed = () => { diff --git a/pkg/auth/handler/webapp/authflow_controller.go b/pkg/auth/handler/webapp/authflow_controller.go index 40153c29df..6428b0815c 100644 --- a/pkg/auth/handler/webapp/authflow_controller.go +++ b/pkg/auth/handler/webapp/authflow_controller.go @@ -747,7 +747,10 @@ func (c *AuthflowController) takeBranchRecursively(s *webapp.Session, screen *we // Take the first branch, and first channel by default. var zeroIndex int var zeroChannel model.AuthenticatorOOBChannel - takeBranchResult := screen.TakeBranch(zeroIndex, zeroChannel, &webapp.TakeBranchOptions{}) + takeBranchResult := screen.TakeBranch(&webapp.TakeBranchInput{ + Index: zeroIndex, + Channel: zeroChannel, + }, &webapp.TakeBranchOptions{}) switch takeBranchResult := takeBranchResult.(type) { // This taken branch does not require an input to select. @@ -935,8 +938,15 @@ func (c *AuthflowController) takeBranch(w http.ResponseWriter, r *http.Request, return err } channel := r.Form.Get("x_channel") - - takeBranchResult := screen.TakeBranch(index, model.AuthenticatorOOBChannel(channel), &webapp.TakeBranchOptions{ + input := &webapp.TakeBranchInput{ + Index: index, + Channel: model.AuthenticatorOOBChannel(channel), + } + if hasBPInput := IsBotProtectionInputValid(r.Form); hasBPInput { + input.BotProtectionProviderType = r.Form.Get("x_bot_protection_provider_type") + input.BotProtectionProviderResponse = r.Form.Get("x_bot_protection_provider_response") + } + takeBranchResult := screen.TakeBranch(input, &webapp.TakeBranchOptions{ DisableFallbackToSMS: true, }) diff --git a/pkg/auth/handler/webapp/bot_protection.go b/pkg/auth/handler/webapp/bot_protection.go index e704f35be9..fc97b05cad 100644 --- a/pkg/auth/handler/webapp/bot_protection.go +++ b/pkg/auth/handler/webapp/bot_protection.go @@ -24,6 +24,11 @@ func ValidateBotProtectionInput(formData url.Values) error { return AuthflowBotProtectionSchema.Validator().ValidateValue(FormToJSON(formData)) } +func IsBotProtectionInputValid(formData url.Values) bool { + err := ValidateBotProtectionInput(formData) + return err == nil +} + func InsertBotProtection(formData url.Values, input map[string]interface{}) { bpType := formData.Get("x_bot_protection_provider_type") bpResp := formData.Get("x_bot_protection_provider_response") diff --git a/pkg/auth/handler/webapp/viewmodels/authflow_branch.go b/pkg/auth/handler/webapp/viewmodels/authflow_branch.go index d418b78069..8a3d202634 100644 --- a/pkg/auth/handler/webapp/viewmodels/authflow_branch.go +++ b/pkg/auth/handler/webapp/viewmodels/authflow_branch.go @@ -18,6 +18,7 @@ type AuthflowBranch struct { MaskedClaimValue string OTPForm otp.Form VerificationSkippable bool + BotProtectionRequired bool } func isAuthflowBranchSame(a AuthflowBranch, b AuthflowBranch) bool { @@ -114,6 +115,8 @@ func newAuthflowBranchViewModelStepAuthenticate(screen *webapp.AuthflowScreenWit Channel: channel, MaskedClaimValue: o.MaskedDisplayName, OTPForm: o.OTPForm, + // only add bot protection for channel branch + BotProtectionRequired: o.BotProtection.IsRequired(), } if !isAuthflowBranchSame(branch, takenBranch) { branches = append(branches, branch) diff --git a/pkg/auth/webapp/session_authflow.go b/pkg/auth/webapp/session_authflow.go index ed27ae724e..8ddfdb3af8 100644 --- a/pkg/auth/webapp/session_authflow.go +++ b/pkg/auth/webapp/session_authflow.go @@ -382,45 +382,59 @@ func (s *AuthflowScreenWithFlowResponse) InheritTakenBranchState(from *AuthflowS } } -func (s *AuthflowScreenWithFlowResponse) TakeBranch(index int, channel model.AuthenticatorOOBChannel, options *TakeBranchOptions) TakeBranchResult { +type TakeBranchInput struct { + Index int + Channel model.AuthenticatorOOBChannel + + // bot protection specific inputs + BotProtectionProviderType string + BotProtectionProviderResponse string +} + +func (i *TakeBranchInput) HasBotProtectionInput() bool { + return i != nil && i.BotProtectionProviderType != "" && i.BotProtectionProviderResponse != "" +} + +func (s *AuthflowScreenWithFlowResponse) TakeBranch(input *TakeBranchInput, options *TakeBranchOptions) TakeBranchResult { switch s.StateTokenFlowResponse.Type { case authflow.FlowTypeSignup: - return s.takeBranchSignup(index, channel, options) + return s.takeBranchSignup(input, options) case authflow.FlowTypePromote: - return s.takeBranchPromote(index, channel, options) + return s.takeBranchPromote(input, options) case authflow.FlowTypeLogin: - return s.takeBranchLogin(index, channel, options) + return s.takeBranchLogin(input, options) case authflow.FlowTypeSignupLogin: - return s.takeBranchSignupLogin(index, options) + return s.takeBranchSignupLogin(input.Index, options) case authflow.FlowTypeReauth: - return s.takeBranchReauth(index, channel, options) + return s.takeBranchReauth(input, options) case authflow.FlowTypeAccountRecovery: - return s.takeBranchAccountRecovery(index, options) + return s.takeBranchAccountRecovery(input.Index, options) default: panic(fmt.Errorf("unexpected flow type: %v", s.StateTokenFlowResponse.Type)) } } -func (s *AuthflowScreenWithFlowResponse) takeBranchSignup(index int, channel model.AuthenticatorOOBChannel, options *TakeBranchOptions) TakeBranchResult { - return s.takeBranchSignupPromote(index, channel, options) +func (s *AuthflowScreenWithFlowResponse) takeBranchSignup(input *TakeBranchInput, options *TakeBranchOptions) TakeBranchResult { + return s.takeBranchSignupPromote(input, options) } -func (s *AuthflowScreenWithFlowResponse) takeBranchPromote(index int, channel model.AuthenticatorOOBChannel, options *TakeBranchOptions) TakeBranchResult { - return s.takeBranchSignupPromote(index, channel, options) +func (s *AuthflowScreenWithFlowResponse) takeBranchPromote(input *TakeBranchInput, options *TakeBranchOptions) TakeBranchResult { + return s.takeBranchSignupPromote(input, options) } -func (s *AuthflowScreenWithFlowResponse) takeBranchSignupPromote(index int, channel model.AuthenticatorOOBChannel, options *TakeBranchOptions) TakeBranchResult { +func (s *AuthflowScreenWithFlowResponse) takeBranchSignupPromote(input *TakeBranchInput, options *TakeBranchOptions) TakeBranchResult { switch config.AuthenticationFlowStepType(s.StateTokenFlowResponse.Action.Type) { case config.AuthenticationFlowStepTypeIdentify: // In identify, the user input actually takes the branch. // The branch taken here is unimportant. - return s.takeBranchResultSimple(index, channel, false) + return s.takeBranchResultSimple(input, false) case config.AuthenticationFlowStepTypeCreateAuthenticator: data := s.StateTokenFlowResponse.Action.Data.(declarative.IntentSignupFlowStepCreateAuthenticatorData) - return s.takeBranchCreateAuthenticator(index, channel, options, data.Options[index]) + return s.takeBranchCreateAuthenticator(input, options, data.Options[input.Index]) case config.AuthenticationFlowStepTypeVerify: // If we ever reach here, this means we have to choose channels. data := s.StateTokenFlowResponse.Action.Data.(declarative.SelectOOBOTPChannelsData) + channel := input.Channel if channel == "" { channel = data.Channels[0] } @@ -429,14 +443,14 @@ func (s *AuthflowScreenWithFlowResponse) takeBranchSignupPromote(index int, chan "channel": c, } } - input := inputFactory(channel) + resultInput := inputFactory(channel) onFailureHandler := s.makeFallbackToSMSFromWhatsappRetryHandler( inputFactory, data.Channels, options.DisableFallbackToSMS, ) return TakeBranchResultInput{ - Input: input, + Input: resultInput, NewAuthflowScreenFull: func(flowResponse *authflow.FlowResponse, retriedForError error) *AuthflowScreenWithFlowResponse { var nilIndex *int isContinuation := func(flowResponse *authflow.FlowResponse) bool { @@ -446,7 +460,7 @@ func (s *AuthflowScreenWithFlowResponse) takeBranchSignupPromote(index int, chan if d, ok := flowResponse.Action.Data.(declarative.VerifyOOBOTPData); ok { takenChannel = d.Channel } - screen := s.makeScreenForTakenBranch(flowResponse, input, nilIndex, takenChannel, isContinuation) + screen := s.makeScreenForTakenBranch(flowResponse, resultInput, nilIndex, takenChannel, isContinuation) return screen }, OnRetry: &onFailureHandler, @@ -456,10 +470,10 @@ func (s *AuthflowScreenWithFlowResponse) takeBranchSignupPromote(index int, chan } } -func (s *AuthflowScreenWithFlowResponse) takeBranchLoginAuthenticate(index int, channel model.AuthenticatorOOBChannel, options *TakeBranchOptions) TakeBranchResult { +func (s *AuthflowScreenWithFlowResponse) takeBranchLoginAuthenticate(input *TakeBranchInput, options *TakeBranchOptions) TakeBranchResult { switch data := s.StateTokenFlowResponse.Action.Data.(type) { case declarative.StepAuthenticateData: - option := data.Options[index] + option := data.Options[input.Index] switch option.Authentication { case config.AuthenticationFlowAuthenticationPrimaryPassword: fallthrough @@ -471,7 +485,7 @@ func (s *AuthflowScreenWithFlowResponse) takeBranchLoginAuthenticate(index int, fallthrough case config.AuthenticationFlowAuthenticationPrimaryPasskey: // All these can take the branch simply by setting index. - return s.takeBranchResultSimple(index, channel, false) + return s.takeBranchResultSimple(input, false) case config.AuthenticationFlowAuthenticationPrimaryOOBOTPEmail: fallthrough case config.AuthenticationFlowAuthenticationPrimaryOOBOTPSMS: @@ -480,21 +494,32 @@ func (s *AuthflowScreenWithFlowResponse) takeBranchLoginAuthenticate(index int, fallthrough case config.AuthenticationFlowAuthenticationSecondaryOOBOTPSMS: // This branch requires input to take. + channel := input.Channel if channel == "" { channel = option.Channels[0] } - if option.BotProtection.IsRequired() { - return s.takeBranchResultSimple(index, channel, true) + // Below clause takes place only when index=0 branch is OOBOTP, and bot protection not in input + // otherwise, the bot protection is fed to auto-taken OOBOTP branch too + if option.BotProtection.IsRequired() && !input.HasBotProtectionInput() { + return s.takeBranchResultSimple(input, true) } inputFactory := func(c model.AuthenticatorOOBChannel) map[string]interface{} { - return map[string]interface{}{ + out := map[string]interface{}{ "authentication": option.Authentication, - "index": index, + "index": input.Index, "channel": c, } + if input.HasBotProtectionInput() { + bp := map[string]interface{}{ + "type": input.BotProtectionProviderType, + "response": input.BotProtectionProviderResponse, + } + out["bot_protection"] = bp + } + return out } - input := inputFactory(channel) + resultInput := inputFactory(channel) onFailureHandler := s.makeFallbackToSMSFromWhatsappRetryHandler( inputFactory, option.Channels, @@ -502,7 +527,7 @@ func (s *AuthflowScreenWithFlowResponse) takeBranchLoginAuthenticate(index int, ) return TakeBranchResultInput{ - Input: input, + Input: resultInput, NewAuthflowScreenFull: func(flowResponse *authflow.FlowResponse, retriedForError error) *AuthflowScreenWithFlowResponse { isContinuation := func(flowResponse *authflow.FlowResponse) bool { return flowResponse.Action.Type == authflow.FlowActionType(config.AuthenticationFlowLoginFlowStepTypeAuthenticate) && flowResponse.Action.Authentication == option.Authentication @@ -515,7 +540,7 @@ func (s *AuthflowScreenWithFlowResponse) takeBranchLoginAuthenticate(index int, // Skip if we do not have the channel. } - screen := s.makeScreenForTakenBranch(flowResponse, input, &index, takenChannel, isContinuation) + screen := s.makeScreenForTakenBranch(flowResponse, resultInput, &input.Index, takenChannel, isContinuation) return screen }, OnRetry: &onFailureHandler, @@ -524,28 +549,28 @@ func (s *AuthflowScreenWithFlowResponse) takeBranchLoginAuthenticate(index int, panic(fmt.Errorf("unexpected authentication: %v", option.Authentication)) } case declarative.VerifyOOBOTPData: - channel = data.Channel - return s.takeBranchResultSimple(index, channel, false) + channel := data.Channel + return s.takeBranchResultSimple(&TakeBranchInput{Index: input.Index, Channel: channel}, false) default: panic(fmt.Errorf("unexpected data type: %T", s.StateTokenFlowResponse.Action.Data)) } } -func (s *AuthflowScreenWithFlowResponse) takeBranchLogin(index int, channel model.AuthenticatorOOBChannel, options *TakeBranchOptions) TakeBranchResult { +func (s *AuthflowScreenWithFlowResponse) takeBranchLogin(input *TakeBranchInput, options *TakeBranchOptions) TakeBranchResult { switch config.AuthenticationFlowStepType(s.StateTokenFlowResponse.Action.Type) { case config.AuthenticationFlowStepTypeIdentify: // In identify, the user input actually takes the branch. // The branch taken here is unimportant. - return s.takeBranchResultSimple(index, channel, false) + return s.takeBranchResultSimple(input, false) case config.AuthenticationFlowStepTypeAuthenticate: switch s.StateTokenFlowResponse.Action.Data.(type) { case declarative.IntentLoginFlowStepCreateAuthenticatorData: data := s.StateTokenFlowResponse.Action.Data.(declarative.IntentLoginFlowStepCreateAuthenticatorData) - return s.takeBranchCreateAuthenticator(index, channel, options, data.Options[index]) + return s.takeBranchCreateAuthenticator(input, options, data.Options[input.Index]) case declarative.IntentCreateAuthenticatorTOTPData: - return s.takeBranchResultSimple(index, channel, false) + return s.takeBranchResultSimple(input, false) case declarative.StepAuthenticateData: - return s.takeBranchLoginAuthenticate(index, channel, options) + return s.takeBranchLoginAuthenticate(input, options) default: panic(fmt.Errorf("unexpected action data: %T", s.StateTokenFlowResponse.Action.Data)) } @@ -554,15 +579,15 @@ func (s *AuthflowScreenWithFlowResponse) takeBranchLogin(index int, channel mode } } -func (s *AuthflowScreenWithFlowResponse) takeBranchReauth(index int, channel model.AuthenticatorOOBChannel, options *TakeBranchOptions) TakeBranchResult { +func (s *AuthflowScreenWithFlowResponse) takeBranchReauth(input *TakeBranchInput, options *TakeBranchOptions) TakeBranchResult { switch config.AuthenticationFlowStepType(s.StateTokenFlowResponse.Action.Type) { case config.AuthenticationFlowStepTypeIdentify: // In identify, id_token is used. // The branch taken here is unimportant. - return s.takeBranchResultSimple(index, channel, false) + return s.takeBranchResultSimple(input, false) case config.AuthenticationFlowStepTypeAuthenticate: data := s.StateTokenFlowResponse.Action.Data.(declarative.StepAuthenticateData) - option := data.Options[index] + option := data.Options[input.Index] switch option.Authentication { case config.AuthenticationFlowAuthenticationPrimaryPassword: fallthrough @@ -572,7 +597,7 @@ func (s *AuthflowScreenWithFlowResponse) takeBranchReauth(index int, channel mod fallthrough case config.AuthenticationFlowAuthenticationPrimaryPasskey: // All these can take the branch simply by setting index. - return s.takeBranchResultSimple(index, channel, false) + return s.takeBranchResultSimple(input, false) case config.AuthenticationFlowAuthenticationPrimaryOOBOTPEmail: fallthrough case config.AuthenticationFlowAuthenticationPrimaryOOBOTPSMS: @@ -581,22 +606,23 @@ func (s *AuthflowScreenWithFlowResponse) takeBranchReauth(index int, channel mod fallthrough case config.AuthenticationFlowAuthenticationSecondaryOOBOTPSMS: // This branch requires input to take. + channel := input.Channel if channel == "" { channel = option.Channels[0] } if option.BotProtection.IsRequired() { - return s.takeBranchResultSimple(index, channel, true) + return s.takeBranchResultSimple(input, true) } inputFactory := func(c model.AuthenticatorOOBChannel) map[string]interface{} { return map[string]interface{}{ "authentication": option.Authentication, - "index": index, + "index": input.Index, "channel": c, } } - input := inputFactory(channel) + resultInput := inputFactory(channel) onFailureHandler := s.makeFallbackToSMSFromWhatsappRetryHandler( inputFactory, option.Channels, @@ -604,7 +630,7 @@ func (s *AuthflowScreenWithFlowResponse) takeBranchReauth(index int, channel mod ) return TakeBranchResultInput{ - Input: input, + Input: resultInput, NewAuthflowScreenFull: func(flowResponse *authflow.FlowResponse, retriedForError error) *AuthflowScreenWithFlowResponse { isContinuation := func(flowResponse *authflow.FlowResponse) bool { return flowResponse.Action.Type == authflow.FlowActionType(config.AuthenticationFlowStepTypeAuthenticate) && flowResponse.Action.Authentication == option.Authentication @@ -617,7 +643,7 @@ func (s *AuthflowScreenWithFlowResponse) takeBranchReauth(index int, channel mod // Skip if we do not have the channel. } - screen := s.makeScreenForTakenBranch(flowResponse, input, &index, takenChannel, isContinuation) + screen := s.makeScreenForTakenBranch(flowResponse, resultInput, &input.Index, takenChannel, isContinuation) return screen }, OnRetry: &onFailureHandler, @@ -635,7 +661,7 @@ func (s *AuthflowScreenWithFlowResponse) takeBranchSignupLogin(index int, option case config.AuthenticationFlowStepTypeIdentify: // In identify, the user input actually takes the branch. // The branch taken here is unimportant. - return s.takeBranchResultSimple(index, "", false) + return s.takeBranchResultSimple(&TakeBranchInput{Index: index, Channel: ""}, false) default: panic(fmt.Errorf("unexpected action type: %v", s.StateTokenFlowResponse.Action.Type)) } @@ -646,19 +672,19 @@ func (s *AuthflowScreenWithFlowResponse) takeBranchAccountRecovery(index int, op case config.AuthenticationFlowStepTypeIdentify: // In identify, the user input actually takes the branch. // The branch taken here is unimportant. - return s.takeBranchResultSimple(index, "", false) + return s.takeBranchResultSimple(&TakeBranchInput{Index: index, Channel: ""}, false) default: panic(fmt.Errorf("unexpected action type: %v", s.StateTokenFlowResponse.Action.Type)) } } -func (s *AuthflowScreenWithFlowResponse) takeBranchResultSimple(index int, channel model.AuthenticatorOOBChannel, botProtectionRequired bool) TakeBranchResultSimple { +func (s *AuthflowScreenWithFlowResponse) takeBranchResultSimple(input *TakeBranchInput, botProtectionRequired bool) TakeBranchResultSimple { xStep := s.Screen.StateToken.XStep screen := NewAuthflowScreenWithFlowResponse(s.StateTokenFlowResponse, xStep, nil) screen.Screen.BranchStateToken = s.Screen.StateToken screen.BranchStateTokenFlowResponse = s.BranchStateTokenFlowResponse - screen.Screen.TakenBranchIndex = &index - screen.Screen.TakenChannel = channel + screen.Screen.TakenBranchIndex = &input.Index + screen.Screen.TakenChannel = input.Channel screen.Screen.IsBotProtectionRequired = botProtectionRequired return TakeBranchResultSimple{ Screen: screen, @@ -752,20 +778,20 @@ func (s *AuthflowScreenWithFlowResponse) makeFallbackToSMSFromWhatsappRetryHandl } } -func (s *AuthflowScreenWithFlowResponse) takeBranchCreateAuthenticator(index int, channel model.AuthenticatorOOBChannel, options *TakeBranchOptions, option declarative.CreateAuthenticatorOptionForOutput) TakeBranchResult { +func (s *AuthflowScreenWithFlowResponse) takeBranchCreateAuthenticator(input *TakeBranchInput, options *TakeBranchOptions, option declarative.CreateAuthenticatorOptionForOutput) TakeBranchResult { switch option.Authentication { case config.AuthenticationFlowAuthenticationPrimaryPassword: fallthrough case config.AuthenticationFlowAuthenticationSecondaryPassword: // Password branches can be taken by setting index. - return s.takeBranchResultSimple(index, channel, false) + return s.takeBranchResultSimple(input, false) case config.AuthenticationFlowAuthenticationSecondaryTOTP: // This branch requires input to take. - input := map[string]interface{}{ + resultInput := map[string]interface{}{ "authentication": "secondary_totp", } return TakeBranchResultInput{ - Input: input, + Input: resultInput, NewAuthflowScreenFull: func(flowResponse *authflow.FlowResponse, retriedForError error) *AuthflowScreenWithFlowResponse { var emptyChannel model.AuthenticatorOOBChannel isContinuation := func(flowResponse *authflow.FlowResponse) bool { @@ -773,12 +799,13 @@ func (s *AuthflowScreenWithFlowResponse) takeBranchCreateAuthenticator(index int flowResponse.Action.Authentication == config.AuthenticationFlowAuthenticationSecondaryTOTP } - return s.makeScreenForTakenBranch(flowResponse, input, &index, emptyChannel, isContinuation) + return s.makeScreenForTakenBranch(flowResponse, resultInput, &input.Index, emptyChannel, isContinuation) }, } case config.AuthenticationFlowAuthenticationPrimaryOOBOTPEmail: fallthrough case config.AuthenticationFlowAuthenticationPrimaryOOBOTPSMS: + channel := input.Channel if channel == "" { channel = option.Channels[0] } @@ -788,7 +815,7 @@ func (s *AuthflowScreenWithFlowResponse) takeBranchCreateAuthenticator(index int "channel": c, } } - input := inputFactory(channel) + resultInput := inputFactory(channel) onFailureHandler := s.makeFallbackToSMSFromWhatsappRetryHandler( inputFactory, option.Channels, @@ -806,7 +833,7 @@ func (s *AuthflowScreenWithFlowResponse) takeBranchCreateAuthenticator(index int takenChannel = d.Channel } - screen := s.makeScreenForTakenBranch(flowResponse, input, &index, takenChannel, isContinuation) + screen := s.makeScreenForTakenBranch(flowResponse, resultInput, &input.Index, takenChannel, isContinuation) return screen }, OnRetry: &onFailureHandler, @@ -815,10 +842,14 @@ func (s *AuthflowScreenWithFlowResponse) takeBranchCreateAuthenticator(index int case config.AuthenticationFlowAuthenticationSecondaryOOBOTPEmail: fallthrough case config.AuthenticationFlowAuthenticationSecondaryOOBOTPSMS: + channel := input.Channel if channel == "" { channel = option.Channels[0] } - return s.takeBranchResultSimple(index, channel, false) + return s.takeBranchResultSimple(&TakeBranchInput{ + Index: input.Index, + Channel: channel, + }, false) default: panic(fmt.Errorf("unexpected authentication: %v", option.Authentication)) } diff --git a/resources/authgear/templates/en/web/authflowv2/__authflow_branch.html b/resources/authgear/templates/en/web/authflowv2/__authflow_branch.html index 4720b3d80b..7358ae1069 100644 --- a/resources/authgear/templates/en/web/authflowv2/__authflow_branch.html +++ b/resources/authgear/templates/en/web/authflowv2/__authflow_branch.html @@ -36,12 +36,15 @@ method="post" novalidate data-controller="turbo-form" - data-action="submit->turbo-form#submitForm" + data-action="{{ if .BotProtectionRequired }}submit->bot-protection#verifyFormSubmit {{end}}submit->turbo-form#submitForm" > {{ $.CSRFField }} + {{ if .BotProtectionRequired }} + {{ template "web/authflowv2/__bot_protection_form_input.html" $ }} + {{ end }}