// // login.go // Copyright (C) 2025 veypi // 2025-05-12 17:35 // Distributed under terms of the MIT license. // package user import ( "encoding/base64" "time" "github.com/golang-jwt/jwt/v5" "github.com/veypi/OneAuth/api/sms" "github.com/veypi/OneAuth/cfg" "github.com/veypi/OneAuth/libs/auth" "github.com/veypi/OneAuth/libs/utils" "github.com/veypi/OneAuth/models" "github.com/vyes-ai/vigo" "github.com/vyes-ai/vigo/contrib/limiter" "github.com/vyes-ai/vigo/logv" ) var publicLimits = limiter.NewAdvancedRequestLimiter(time.Minute*5, 20, time.Second*3, nil).Limit var _ = Router.Post("/login", vigo.SkipBefore, publicLimits, userLogin) type loginOpts struct { UserName string `json:"username" parse:"json"` Code string `json:"code" parse:"json"` VerifyCode string `json:"verify_code" parse:"json"` Region string `json:"region" parse:"json"` Phone string `json:"phone" parse:"json"` Email string `json:"email" parse:"json"` Type *string `json:"type" parse:"json"` AppID *string `json:"app_id" parse:"json"` Device *string `json:"device" parse:"json"` } func userLogin(x *vigo.X) (any, error) { // Implement login logic here // For example, validate user credentials and return a token opts := &loginOpts{} err := x.Parse(opts) if err != nil { return nil, err } user := &models.User{} query := cfg.DB() typ := "" if opts.Type != nil { typ = *opts.Type } switch typ { case "phone": query = query.Where("phone = ?", opts.Region+opts.Phone) case "email": query = query.Where("email = ?", opts.Email) default: query = query.Where("username = ?", opts.UserName) } err = query.First(user).Error if err != nil { return nil, vigo.ErrNotFound } logv.Info().Str("user", user.ID).Msg("login") if opts.VerifyCode != "" { err = sms.VerifyCode(opts.Phone, opts.VerifyCode, opts.Region, "signin") if err != nil { logv.Warn().Msgf("verify code: %v", err) return nil, vigo.ErrNotAuthorized.WithError(err) } } else { code, err := base64.URLEncoding.DecodeString(opts.Code) if err != nil { return nil, vigo.ErrArgInvalid.WithArgs("code") } ncode, err := utils.AesDecrypt([]byte(user.Code), utils.PKCS7Padding(code, 32), []byte(user.Salt)) if err != nil || string(ncode) != user.ID { return nil, vigo.ErrNotAuthorized } } aid := cfg.Config.ID if opts.AppID != nil && *opts.AppID != "" { aid = *opts.AppID } data := &models.Token{} data.UserID = user.ID data.AppID = aid data.ExpiredAt = time.Now().Add(time.Hour * 72) if opts.Device != nil { data.Device = *opts.Device } data.Ip = x.GetRemoteIP() logv.AssertError(cfg.DB().Create(data).Error) claim := &auth.Claims{} claim.IssuedAt = jwt.NewNumericDate(time.Now()) claim.Issuer = cfg.Config.ID claim.ID = data.ID claim.AID = aid claim.UID = user.ID claim.Name = user.Username claim.Icon = user.Icon claim.ExpiresAt = jwt.NewNumericDate(data.ExpiredAt) if user.Nickname != "" { claim.Name = user.Nickname } return auth.GenJwt(claim) }