An entire complete sign in user flow

This commit is contained in:
2024-04-21 01:33:42 +08:00
parent e79441dbc5
commit ee6e7324b2
21 changed files with 467 additions and 52 deletions

View File

@ -25,7 +25,7 @@ Thank you for your cooperation in helping us maintain the security of your accou
Best regards,
%s`
func GetPasswordFactor(userId uint) (models.AuthFactor, error) {
func GetPasswordTypeFactor(userId uint) (models.AuthFactor, error) {
var factor models.AuthFactor
err := database.C.Where(models.AuthFactor{
Type: models.PasswordAuthFactor,
@ -53,6 +53,15 @@ func ListUserFactor(userId uint) ([]models.AuthFactor, error) {
return factors, err
}
func CountUserFactor(userId uint) int64 {
var count int64
database.C.Where(models.AuthFactor{
AccountID: userId,
}).Model(&models.AuthFactor{}).Count(&count)
return count
}
func GetFactorCode(factor models.AuthFactor) (bool, error) {
switch factor.Type {
case models.EmailPasswordFactor:

18
pkg/services/mfa.go Normal file
View File

@ -0,0 +1,18 @@
package services
import (
"git.solsynth.dev/hydrogen/passport/pkg/models"
"github.com/nicksnyder/go-i18n/v2/i18n"
)
func GetFactorName(w models.AuthFactorType, localizer *i18n.Localizer) string {
unknown, _ := localizer.LocalizeMessage(&i18n.Message{ID: "unknown"})
mfaEmail, _ := localizer.LocalizeMessage(&i18n.Message{ID: "mfaFactorEmail"})
switch w {
case models.EmailPasswordFactor:
return mfaEmail
default:
return unknown
}
}

View File

@ -27,18 +27,23 @@ func DetectRisk(user models.Account, ip, ua string) bool {
func NewTicket(user models.Account, ip, ua string) (models.AuthTicket, error) {
var ticket models.AuthTicket
if err := database.C.Where(models.AuthTicket{
AccountID: user.ID,
}).First(&ticket).Error; err == nil {
if err := database.C.
Where("account_id = ? AND expired_at < ? AND available_at IS NULL", time.Now(), user.ID).
First(&ticket).Error; err == nil {
return ticket, nil
}
requireMFA := DetectRisk(user, ip, ua)
if count := CountUserFactor(user.ID); count <= 1 {
requireMFA = false
}
ticket = models.AuthTicket{
Claims: []string{"*"},
Audiences: []string{"passport"},
IpAddress: ip,
UserAgent: ua,
RequireMFA: DetectRisk(user, ip, ua),
RequireMFA: requireMFA,
RequireAuthenticate: true,
ExpiredAt: lo.ToPtr(time.Now().Add(2 * time.Hour)),
AvailableAt: nil,
@ -85,16 +90,19 @@ func ActiveTicketWithPassword(ticket models.AuthTicket, password string) (models
return ticket, nil
}
if factor, err := GetPasswordFactor(ticket.AccountID); err != nil {
if factor, err := GetPasswordTypeFactor(ticket.AccountID); err != nil {
return ticket, fmt.Errorf("unable to active ticket: %v", err)
} else if err = CheckFactor(factor, password); err != nil {
return ticket, err
}
ticket.AvailableAt = lo.ToPtr(time.Now())
ticket.RequireAuthenticate = false
if !ticket.RequireAuthenticate && !ticket.RequireMFA {
ticket.AvailableAt = lo.ToPtr(time.Now())
ticket.GrantToken = lo.ToPtr(uuid.NewString())
ticket.AccessToken = lo.ToPtr(uuid.NewString())
ticket.RefreshToken = lo.ToPtr(uuid.NewString())
}
if err := database.C.Save(&ticket).Error; err != nil {
@ -119,6 +127,9 @@ func ActiveTicketWithMFA(ticket models.AuthTicket, factor models.AuthFactor, cod
if !ticket.RequireAuthenticate && !ticket.RequireMFA {
ticket.AvailableAt = lo.ToPtr(time.Now())
ticket.GrantToken = lo.ToPtr(uuid.NewString())
ticket.AccessToken = lo.ToPtr(uuid.NewString())
ticket.RefreshToken = lo.ToPtr(uuid.NewString())
}
if err := database.C.Save(&ticket).Error; err != nil {
@ -128,10 +139,10 @@ func ActiveTicketWithMFA(ticket models.AuthTicket, factor models.AuthFactor, cod
return ticket, nil
}
func RegenSession(session models.AuthTicket) (models.AuthTicket, error) {
session.GrantToken = lo.ToPtr(uuid.NewString())
session.AccessToken = lo.ToPtr(uuid.NewString())
session.RefreshToken = lo.ToPtr(uuid.NewString())
err := database.C.Save(&session).Error
return session, err
func RegenSession(ticket models.AuthTicket) (models.AuthTicket, error) {
ticket.GrantToken = lo.ToPtr(uuid.NewString())
ticket.AccessToken = lo.ToPtr(uuid.NewString())
ticket.RefreshToken = lo.ToPtr(uuid.NewString())
err := database.C.Save(&ticket).Error
return ticket, err
}

View File

@ -19,8 +19,8 @@ func GetToken(ticket models.AuthTicket) (string, string, error) {
return refresh, access, fmt.Errorf("unable to encode token, access or refresh token id missing")
}
accessDuration := time.Duration(viper.GetInt64("access_token_duration")) * time.Second
refreshDuration := time.Duration(viper.GetInt64("refresh_token_duration")) * time.Second
accessDuration := time.Duration(viper.GetInt64("security.access_token_duration")) * time.Second
refreshDuration := time.Duration(viper.GetInt64("security.refresh_token_duration")) * time.Second
var err error
sub := strconv.Itoa(int(ticket.AccountID))