feat: improve MFA by using user's own Email and Phone (#2002)

* refactor: mfa

* fix: clean code

* fix: clean code

* fix: fix crash and improve robot
This commit is contained in:
Yaodong Yu
2023-06-21 18:56:37 +08:00
committed by GitHub
parent 6ebca6dbe7
commit c391af4552
28 changed files with 528 additions and 362 deletions

View File

@ -34,6 +34,21 @@ type SmsMfa struct {
Config *MfaProps
}
func (mfa *SmsMfa) Initiate(ctx *context.Context, name string, secret string) (*MfaProps, error) {
recoveryCode := uuid.NewString()
err := ctx.Input.CruSession.Set(MfaSmsRecoveryCodesSession, []string{recoveryCode})
if err != nil {
return nil, err
}
mfaProps := MfaProps{
MfaType: mfa.Config.MfaType,
RecoveryCodes: []string{recoveryCode},
}
return &mfaProps, nil
}
func (mfa *SmsMfa) SetupVerify(ctx *context.Context, passCode string) error {
dest := ctx.Input.CruSession.Get(MfaSmsDestSession).(string)
countryCode := ctx.Input.CruSession.Get(MfaSmsCountryCodeSession).(string)
@ -47,6 +62,45 @@ func (mfa *SmsMfa) SetupVerify(ctx *context.Context, passCode string) error {
return nil
}
func (mfa *SmsMfa) Enable(ctx *context.Context, user *User) error {
recoveryCodes := ctx.Input.CruSession.Get(MfaSmsRecoveryCodesSession).([]string)
if len(recoveryCodes) == 0 {
return fmt.Errorf("recovery codes is empty")
}
columns := []string{"recovery_codes", "preferred_mfa_type"}
user.RecoveryCodes = append(user.RecoveryCodes, recoveryCodes...)
if user.PreferredMfaType == "" {
user.PreferredMfaType = mfa.Config.MfaType
}
if mfa.Config.MfaType == SmsType {
user.MfaPhoneEnabled = true
columns = append(columns, "mfa_phone_enabled")
if user.Phone == "" {
user.Phone = ctx.Input.CruSession.Get(MfaSmsDestSession).(string)
user.CountryCode = ctx.Input.CruSession.Get(MfaSmsCountryCodeSession).(string)
columns = append(columns, "phone", "country_code")
}
} else if mfa.Config.MfaType == EmailType {
user.MfaEmailEnabled = true
columns = append(columns, "mfa_email_enabled")
if user.Email == "" {
user.Email = ctx.Input.CruSession.Get(MfaSmsDestSession).(string)
columns = append(columns, "email")
}
}
_, err := UpdateUser(user.GetId(), user, columns, false)
if err != nil {
return err
}
return nil
}
func (mfa *SmsMfa) Verify(passCode string) error {
if !util.IsEmailValid(mfa.Config.Secret) {
mfa.Config.Secret, _ = util.GetE164Number(mfa.Config.Secret, mfa.Config.CountryCode)
@ -57,65 +111,21 @@ func (mfa *SmsMfa) Verify(passCode string) error {
return nil
}
func (mfa *SmsMfa) Initiate(ctx *context.Context, name string, secret string) (*MfaProps, error) {
recoveryCode, err := uuid.NewRandom()
if err != nil {
return nil, err
}
err = ctx.Input.CruSession.Set(MfaSmsRecoveryCodesSession, []string{recoveryCode.String()})
if err != nil {
return nil, err
}
mfaProps := MfaProps{
AuthType: SmsType,
RecoveryCodes: []string{recoveryCode.String()},
}
return &mfaProps, nil
}
func (mfa *SmsMfa) Enable(ctx *context.Context, user *User) error {
dest := ctx.Input.CruSession.Get(MfaSmsDestSession).(string)
recoveryCodes := ctx.Input.CruSession.Get(MfaSmsRecoveryCodesSession).([]string)
countryCode := ctx.Input.CruSession.Get(MfaSmsCountryCodeSession).(string)
if dest == "" || len(recoveryCodes) == 0 {
return fmt.Errorf("MFA dest or recovery codes is empty")
}
if !util.IsEmailValid(dest) {
mfa.Config.CountryCode = countryCode
}
mfa.Config.AuthType = SmsType
mfa.Config.Id = uuid.NewString()
mfa.Config.Secret = dest
mfa.Config.RecoveryCodes = recoveryCodes
for i, mfaProp := range user.MultiFactorAuths {
if mfaProp.Secret == mfa.Config.Secret {
user.MultiFactorAuths = append(user.MultiFactorAuths[:i], user.MultiFactorAuths[i+1:]...)
}
}
user.MultiFactorAuths = append(user.MultiFactorAuths, mfa.Config)
affected, err := UpdateUser(user.GetId(), user, []string{"multi_factor_auths"}, user.IsAdminUser())
if err != nil {
return err
}
if !affected {
return fmt.Errorf("failed to enable two factor authentication")
}
return nil
}
func NewSmsTwoFactor(config *MfaProps) *SmsMfa {
if config == nil {
config = &MfaProps{
AuthType: SmsType,
MfaType: SmsType,
}
}
return &SmsMfa{
Config: config,
}
}
func NewEmailTwoFactor(config *MfaProps) *SmsMfa {
if config == nil {
config = &MfaProps{
MfaType: EmailType,
}
}
return &SmsMfa{