✨ Impl for totp code, and in app notify factor
This commit is contained in:
@ -8,7 +8,10 @@ import (
|
||||
"git.solsynth.dev/hypernet/passport/pkg/internal/http/exts"
|
||||
"git.solsynth.dev/hypernet/passport/pkg/internal/services"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/pquerna/otp/totp"
|
||||
"github.com/samber/lo"
|
||||
"github.com/spf13/viper"
|
||||
"gorm.io/datatypes"
|
||||
)
|
||||
|
||||
func getAvailableFactors(c *fiber.Ctx) error {
|
||||
@ -100,10 +103,45 @@ func createFactor(c *fiber.Ctx) error {
|
||||
Account: user,
|
||||
AccountID: user.ID,
|
||||
}
|
||||
|
||||
additionalOnceConfig := map[string]any{}
|
||||
|
||||
switch data.Type {
|
||||
case models.TimeOtpFactor:
|
||||
cfg := totp.GenerateOpts{
|
||||
Issuer: viper.GetString("name"),
|
||||
AccountName: user.Name,
|
||||
Period: 30,
|
||||
SecretSize: 20,
|
||||
Digits: 6,
|
||||
}
|
||||
key, err := totp.Generate(cfg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to generate totp key: %v", err)
|
||||
}
|
||||
factor.Secret = key.Secret()
|
||||
factor.Config = datatypes.NewJSONType(map[string]any{
|
||||
"issuer": cfg.Issuer,
|
||||
"account_name": cfg.AccountName,
|
||||
"period": cfg.Period,
|
||||
"secret_size": cfg.SecretSize,
|
||||
"digits": cfg.Digits,
|
||||
})
|
||||
additionalOnceConfig["url"] = key.URL()
|
||||
}
|
||||
|
||||
if err := database.C.Create(&factor).Error; err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
if len(additionalOnceConfig) > 0 {
|
||||
data := factor.Config.Data()
|
||||
for k, v := range additionalOnceConfig {
|
||||
data[k] = v
|
||||
}
|
||||
factor.Config = datatypes.NewJSONType(data)
|
||||
}
|
||||
|
||||
return c.JSON(factor)
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
"git.solsynth.dev/hypernet/pusher/pkg/pushkit"
|
||||
"github.com/google/uuid"
|
||||
"github.com/nats-io/nats.go"
|
||||
"github.com/pquerna/otp/totp"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/samber/lo"
|
||||
"github.com/spf13/viper"
|
||||
@ -70,6 +71,36 @@ func CountUserFactor(userId uint) int64 {
|
||||
|
||||
func GetFactorCode(factor models.AuthFactor) (bool, error) {
|
||||
switch factor.Type {
|
||||
case models.InAppNotifyFactor:
|
||||
var user models.Account
|
||||
if err := database.C.Where(&models.Account{
|
||||
BaseModel: models.BaseModel{ID: factor.AccountID},
|
||||
}).First(&user).Error; err != nil {
|
||||
return true, err
|
||||
}
|
||||
|
||||
secret := uuid.NewString()[:6]
|
||||
|
||||
identifier := fmt.Sprintf("%s%d", gap.FactorOtpPrefix, factor.ID)
|
||||
_, err := gap.Jt.Publish(identifier, []byte(secret))
|
||||
if err != nil {
|
||||
return true, fmt.Errorf("error during publish message: %v", err)
|
||||
} else {
|
||||
log.Info().Uint("factor", factor.ID).Str("secret", secret).Msg("Published one-time-password to JetStream...")
|
||||
}
|
||||
|
||||
err = PushNotification(models.Notification{
|
||||
Topic: "passport.security.otp",
|
||||
Title: "Your login one-time-password",
|
||||
Body: fmt.Sprintf("`%s` is your login verification code. It will expires in 30 minutes.", secret),
|
||||
Account: user,
|
||||
AccountID: user.ID,
|
||||
}, true)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Uint("factor", factor.ID).Msg("Failed to delivery one-time-password via notify...")
|
||||
return true, nil
|
||||
}
|
||||
return true, nil
|
||||
case models.EmailPasswordFactor:
|
||||
var user models.Account
|
||||
if err := database.C.Where(&models.Account{
|
||||
@ -103,7 +134,6 @@ func GetFactorCode(factor models.AuthFactor) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
return true, nil
|
||||
|
||||
default:
|
||||
return false, nil
|
||||
}
|
||||
@ -117,6 +147,13 @@ func CheckFactor(factor models.AuthFactor, code string) error {
|
||||
nil,
|
||||
fmt.Errorf("invalid password"),
|
||||
)
|
||||
case models.TimeOtpFactor:
|
||||
lo.Ternary(
|
||||
totp.Validate(code, factor.Secret),
|
||||
nil,
|
||||
fmt.Errorf("invalid verification code"),
|
||||
)
|
||||
case models.InAppNotifyFactor:
|
||||
case models.EmailPasswordFactor:
|
||||
identifier := fmt.Sprintf("%s%d", gap.FactorOtpPrefix, factor.ID)
|
||||
sub, err := gap.Jt.PullSubscribe(identifier, "otp_validator", nats.BindStream("OTPs"))
|
||||
|
Reference in New Issue
Block a user