diff --git a/internal/outpost/flow/solvers_mfa.go b/internal/outpost/flow/solvers_mfa.go index f2952eb3d..928b251e3 100644 --- a/internal/outpost/flow/solvers_mfa.go +++ b/internal/outpost/flow/solvers_mfa.go @@ -10,39 +10,44 @@ const CodePasswordSeparator = ";" var alphaNum = regexp.MustCompile(`^[a-zA-Z0-9]*$`) -// CheckPasswordInlineMFA For protocols that only support username/password, check if the password -// contains the TOTP code -func (fe *FlowExecutor) CheckPasswordInlineMFA() { - password := fe.Answers[StagePassword] - // We already have an authenticator answer - if fe.Answers[StageAuthenticatorValidate] != "" { +// SetSecrets sets the secret answers for the flow executor for protocols that only support username/password +// acccording to used options +func (fe *FlowExecutor) SetSecrets(password string, mfacodebased bool) { + if fe.Answers[StageAuthenticatorValidate] != "" || fe.Answers[StagePassword] != "" { return } - // password doesn't contain the separator - if !strings.Contains(password, CodePasswordSeparator) { - return - } - // password ends with the separator, so it won't contain an answer - if strings.HasSuffix(password, CodePasswordSeparator) { - return - } - idx := strings.LastIndex(password, CodePasswordSeparator) - authenticator := password[idx+1:] - // Authenticator is either 6 chars (totp code) or 8 chars (long totp or static) - if len(authenticator) == 6 { - // authenticator answer isn't purely numerical, so won't be value - if _, err := strconv.Atoi(authenticator); err != nil { + fe.Answers[StagePassword] = password + if mfacodebased { + // password doesn't contain the separator + if !strings.Contains(password, CodePasswordSeparator) { return } - } else if len(authenticator) == 8 { - // 8 chars can be a long totp or static token, so it needs to be alphanumerical - if !alphaNum.MatchString(authenticator) { + // password ends with the separator, so it won't contain an answer + if strings.HasSuffix(password, CodePasswordSeparator) { return } + idx := strings.LastIndex(password, CodePasswordSeparator) + authenticator := password[idx+1:] + // Authenticator is either 6 chars (totp code) or 8 chars (long totp or static) + if len(authenticator) == 6 { + // authenticator answer isn't purely numerical, so won't be value + if _, err := strconv.Atoi(authenticator); err != nil { + return + } + } else if len(authenticator) == 8 { + // 8 chars can be a long totp or static token, so it needs to be alphanumerical + if !alphaNum.MatchString(authenticator) { + return + } + } else { + // Any other length, doesn't contain an answer + return + } + fe.Answers[StagePassword] = password[:idx] + fe.Answers[StageAuthenticatorValidate] = authenticator } else { - // Any other length, doesn't contain an answer - return + // If code-based MFA is disabled StageAuthenticatorValidate answer is set to password. + // This allows flows with a mfa stage only. + fe.Answers[StageAuthenticatorValidate] = password } - fe.Answers[StagePassword] = password[:idx] - fe.Answers[StageAuthenticatorValidate] = authenticator } diff --git a/internal/outpost/ldap/bind/direct/bind.go b/internal/outpost/ldap/bind/direct/bind.go index cffa0cf36..b7850e853 100644 --- a/internal/outpost/ldap/bind/direct/bind.go +++ b/internal/outpost/ldap/bind/direct/bind.go @@ -23,10 +23,7 @@ func (db *DirectBinder) Bind(username string, req *bind.Request) (ldap.LDAPResul fe.Params.Add("goauthentik.io/outpost/ldap", "true") fe.Answers[flow.StageIdentification] = username - fe.Answers[flow.StagePassword] = req.BindPW - if db.si.GetMFASupport() { - fe.CheckPasswordInlineMFA() - } + fe.SetSecrets(req.BindPW, db.si.GetMFASupport()) passed, err := fe.Execute() flags := flags.UserFlags{ diff --git a/internal/outpost/radius/handle_access_request.go b/internal/outpost/radius/handle_access_request.go index 55b88ff54..49f177596 100644 --- a/internal/outpost/radius/handle_access_request.go +++ b/internal/outpost/radius/handle_access_request.go @@ -21,14 +21,7 @@ func (rs *RadiusServer) Handle_AccessRequest(w radius.ResponseWriter, r *RadiusR fe.Params.Add("goauthentik.io/outpost/radius", "true") fe.Answers[flow.StageIdentification] = username - fe.Answers[flow.StagePassword] = rfc2865.UserPassword_GetString(r.Packet) - if r.pi.MFASupport { - fe.CheckPasswordInlineMFA() - } else { - // If code-based MFA is disabled StageAuthenticatorValidate answer is set to StagePassword answer. - // This allows flows with only a mfa stage - fe.Answers[flow.StageAuthenticatorValidate] = fe.Answers[flow.StagePassword] - } + fe.SetSecrets(rfc2865.UserPassword_GetString(r.Packet), r.pi.MFASupport) passed, err := fe.Execute() if err != nil {