mirror of
https://github.com/volatiletech/authboss.git
synced 2025-02-15 14:03:17 +02:00
Add recovery code logins to sms
This commit is contained in:
parent
e79638a05e
commit
e4badae1ee
@ -17,10 +17,11 @@ const (
|
||||
FormValuePassword = "password"
|
||||
FormValueUsername = "username"
|
||||
|
||||
FormValueConfirm = "cnf"
|
||||
FormValueToken = "token"
|
||||
FormValueCode = "code"
|
||||
FormValuePhoneNumber = "phone_number"
|
||||
FormValueConfirm = "cnf"
|
||||
FormValueToken = "token"
|
||||
FormValueCode = "code"
|
||||
FormValueRecoveryCode = "recovery_code"
|
||||
FormValuePhoneNumber = "phone_number"
|
||||
)
|
||||
|
||||
// UserValues from the login form
|
||||
@ -98,23 +99,31 @@ func (r RecoverEndValues) GetPassword() string { return r.NewPassword }
|
||||
type TwoFA struct {
|
||||
HTTPFormValidator
|
||||
|
||||
Code string
|
||||
Code string
|
||||
RecoveryCode string
|
||||
}
|
||||
|
||||
// GetCode from authenticator
|
||||
func (t TwoFA) GetCode() string { return t.Code }
|
||||
|
||||
// GetRecoveryCode for authenticator
|
||||
func (t TwoFA) GetRecoveryCode() string { return t.RecoveryCode }
|
||||
|
||||
// SMSTwoFA for sms2fa_validate page
|
||||
type SMSTwoFA struct {
|
||||
HTTPFormValidator
|
||||
|
||||
Code string
|
||||
PhoneNumber string
|
||||
Code string
|
||||
RecoveryCode string
|
||||
PhoneNumber string
|
||||
}
|
||||
|
||||
// GetCode from sms
|
||||
func (s SMSTwoFA) GetCode() string { return s.Code }
|
||||
|
||||
// GetRecoveryCode from sms
|
||||
func (s SMSTwoFA) GetRecoveryCode() string { return s.RecoveryCode }
|
||||
|
||||
// GetPhoneNumber from authenticator
|
||||
func (s SMSTwoFA) GetPhoneNumber() string { return s.PhoneNumber }
|
||||
|
||||
@ -263,12 +272,14 @@ func (h HTTPBodyReader) Read(page string, r *http.Request) (authboss.Validator,
|
||||
return TwoFA{
|
||||
HTTPFormValidator: HTTPFormValidator{Values: values, Ruleset: rules, ConfirmFields: confirms},
|
||||
Code: values[FormValueCode],
|
||||
RecoveryCode: values[FormValueRecoveryCode],
|
||||
}, nil
|
||||
case "sms2fa_validate":
|
||||
return SMSTwoFA{
|
||||
HTTPFormValidator: HTTPFormValidator{Values: values, Ruleset: rules, ConfirmFields: confirms},
|
||||
Code: values[FormValueCode],
|
||||
PhoneNumber: values[FormValuePhoneNumber],
|
||||
RecoveryCode: values[FormValueRecoveryCode],
|
||||
}, nil
|
||||
case "register":
|
||||
arbitrary := make(map[string]string)
|
||||
|
@ -272,15 +272,25 @@ func (s *SMSValidator) Post(w http.ResponseWriter, r *http.Request) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
smsCodeValues := MustHaveSMSValues(validator)
|
||||
inputCode := smsCodeValues.GetCode()
|
||||
|
||||
if len(inputCode) == 0 {
|
||||
var inputCode, recoveryCode string
|
||||
inputCode = smsCodeValues.GetCode()
|
||||
|
||||
// Only allow recovery codes on login/remove operations
|
||||
if s.Action == dataValidate || s.Action == dataValidateRemove {
|
||||
recoveryCode = smsCodeValues.GetRecoveryCode()
|
||||
}
|
||||
|
||||
if len(recoveryCode) == 0 && len(inputCode) == 0 {
|
||||
return s.sendCode(w, r, user)
|
||||
}
|
||||
|
||||
return s.validateCode(w, r, user, inputCode)
|
||||
if len(recoveryCode) != 0 {
|
||||
return s.validateCode(w, r, user, "", recoveryCode)
|
||||
}
|
||||
|
||||
return s.validateCode(w, r, user, inputCode, "")
|
||||
}
|
||||
|
||||
func (s *SMSValidator) sendCode(w http.ResponseWriter, r *http.Request, user User) error {
|
||||
@ -316,17 +326,35 @@ func (s *SMSValidator) sendCode(w http.ResponseWriter, r *http.Request, user Use
|
||||
return s.Core.Responder.Respond(w, r, http.StatusOK, PageSMSValidate, data)
|
||||
}
|
||||
|
||||
func (s *SMSValidator) validateCode(w http.ResponseWriter, r *http.Request, user User, inputCode string) error {
|
||||
code, ok := authboss.GetSession(r, SessionSMSSecret)
|
||||
if !ok || len(code) == 0 {
|
||||
return errors.Errorf("no code in session for user %s", user.GetPID())
|
||||
func (s *SMSValidator) validateCode(w http.ResponseWriter, r *http.Request, user User, inputCode, recoveryCode string) error {
|
||||
logger := s.RequestLogger(r)
|
||||
|
||||
var verified bool
|
||||
if len(recoveryCode) != 0 {
|
||||
var ok bool
|
||||
recoveryCodes := twofactor.DecodeRecoveryCodes(user.GetRecoveryCodes())
|
||||
recoveryCodes, ok = twofactor.UseRecoveryCode(recoveryCodes, recoveryCode)
|
||||
|
||||
verified = ok
|
||||
|
||||
if verified {
|
||||
logger.Infof("user %s used recovery code instead of sms2fa", user.GetPID())
|
||||
user.PutRecoveryCodes(twofactor.EncodeRecoveryCodes(recoveryCodes))
|
||||
if err := s.Authboss.Config.Storage.Server.Save(r.Context(), user); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
code, ok := authboss.GetSession(r, SessionSMSSecret)
|
||||
if !ok || len(code) == 0 {
|
||||
return errors.Errorf("no code in session for user %s", user.GetPID())
|
||||
}
|
||||
|
||||
verified = 1 == subtle.ConstantTimeCompare([]byte(inputCode), []byte(code))
|
||||
}
|
||||
|
||||
verified := 1 == subtle.ConstantTimeCompare([]byte(inputCode), []byte(code))
|
||||
|
||||
logger := s.RequestLogger(r)
|
||||
if !verified {
|
||||
logger.Infof("user %s sms failure (wrong code)", user.GetPID())
|
||||
logger.Infof("user %s sms 2fa failure (wrong code)", user.GetPID())
|
||||
data := authboss.HTMLData{
|
||||
authboss.DataValidation: map[string][]string{FormValueCode: []string{"2fa code was invalid"}},
|
||||
DataValidateMode: s.Action,
|
||||
@ -383,7 +411,7 @@ func (s *SMSValidator) validateCode(w http.ResponseWriter, r *http.Request, user
|
||||
authboss.DelSession(w, SessionSMSPendingPID)
|
||||
authboss.DelSession(w, SessionSMSSecret)
|
||||
|
||||
logger.Infof("user %s sms success", user.GetPID())
|
||||
logger.Infof("user %s sms 2fa success", user.GetPID())
|
||||
|
||||
ro := authboss.RedirectOptions{
|
||||
Code: http.StatusTemporaryRedirect,
|
||||
|
@ -11,6 +11,7 @@ type SMSValuer interface {
|
||||
authboss.Validator
|
||||
|
||||
GetCode() string
|
||||
GetRecoveryCode() string
|
||||
}
|
||||
|
||||
// SMSPhoneNumberValuer returns a phone number from the body
|
||||
|
@ -327,7 +327,7 @@ func (t *TOTP) PostValidate(w http.ResponseWriter, r *http.Request) error {
|
||||
case err != nil:
|
||||
return err
|
||||
case !ok:
|
||||
logger.Infof("user %s totp failure (wrong code)", user.GetPID())
|
||||
logger.Infof("user %s totp 2fa failure (wrong code)", user.GetPID())
|
||||
data := authboss.HTMLData{
|
||||
authboss.DataErr: "totp 2fa code incorrect",
|
||||
DataValidateMode: dataValidate,
|
||||
@ -341,7 +341,7 @@ func (t *TOTP) PostValidate(w http.ResponseWriter, r *http.Request) error {
|
||||
authboss.DelSession(w, SessionTOTPPendingPID)
|
||||
authboss.DelSession(w, SessionTOTPSecret)
|
||||
|
||||
logger.Infof("user %s totp success", user.GetPID())
|
||||
logger.Infof("user %s totp 2fa success", user.GetPID())
|
||||
|
||||
ro := authboss.RedirectOptions{
|
||||
Code: http.StatusTemporaryRedirect,
|
||||
|
Loading…
x
Reference in New Issue
Block a user