// // post.go // Copyright (C) 2025 veypi // 2025-05-09 17:26 // Distributed under terms of the MIT license. // package token import ( "net/http" "time" "github.com/golang-jwt/jwt/v5" "github.com/veypi/OneAuth/cfg" "github.com/veypi/OneAuth/libs/auth" "github.com/veypi/OneAuth/models" "github.com/vyes/vigo" "github.com/vyes/vigo/logv" ) type postOpts struct { // 两种获取token方式,一种用refreshtoken换取apptoken(应用登录),一种用密码加密code换refreshtoken (oa登录) Refresh *string `json:"refresh" parse:"json"` Typ *string `json:"typ" parse:"json"` AppID *string `json:"app_id" gorm:"index;type:varchar(32)" parse:"json"` ExpiredAt *time.Time `json:"expired_at" parse:"json"` OverPerm *string `json:"over_perm" parse:"json"` Device *string `json:"device" parse:"json"` } var _ = Router.Post("/", tokenPost) // for user login app func tokenPost(x *vigo.X) (any, error) { opts := &postOpts{} err := x.Parse(opts) if err != nil { return nil, err } aid := cfg.Config.ID if opts.AppID != nil && *opts.AppID != "" { aid = *opts.AppID } data := &models.Token{} claim := &auth.Claims{} claim.IssuedAt = jwt.NewNumericDate(time.Now()) claim.Issuer = cfg.Config.ID if opts.Refresh != nil { typ := "app" if opts.Typ != nil { typ = *opts.Typ } // for other app redirect refresh, err := auth.ParseJwt(*opts.Refresh) if err != nil { return nil, err } if refresh.ID == "" { return nil, vigo.ErrNotAuthorized } err = cfg.DB().Where("id = ?", refresh.ID).First(data).Error if err != nil { return nil, err } claim.AID = aid claim.UID = refresh.UID claim.Name = refresh.Name claim.Icon = refresh.Icon claim.ExpiresAt = jwt.NewNumericDate(time.Now().Add(cfg.Config.TokenExpire)) if typ == "app" { if refresh.AID == aid { // refresh token acList := make(auth.Access, 0, 10) logv.AssertError(cfg.DB().Table("accesses a"). Select("a.name, a.t_id, a.level"). Joins("INNER JOIN user_roles ur ON ur.role_id = a.role_id AND ur.user_id = ? AND a.app_id = ?", refresh.UID, aid). Scan(&acList).Error) claim.Access = acList if aid == cfg.Config.ID { return auth.GenJwt(claim) } app := &models.App{} err = cfg.DB().Where("id = ?", aid).First(app).Error if err != nil { return nil, err } return auth.GenJwtWithKey(claim, app.Key) } else if refresh.ID == cfg.Config.ID { // oa应用生成其他应用的refresh token newToken := &models.Token{} newToken.UserID = refresh.UID newToken.AppID = aid newToken.ExpiredAt = time.Now().Add(time.Hour * 24) if opts.OverPerm != nil { newToken.OverPerm = *opts.OverPerm } if opts.Device != nil { newToken.Device = *opts.Device } newToken.Ip = x.GetRemoteIp() logv.AssertError(cfg.DB().Create(newToken).Error) // gen other app token claim.ID = newToken.ID claim.ExpiresAt = jwt.NewNumericDate(newToken.ExpiredAt) return auth.GenJwt(claim) } else { return nil, vigo.ErrNotPermitted } } else if typ == "ufs" { claim.AID = refresh.AID claim.UID = refresh.UID claim.Name = refresh.Name claim.Icon = refresh.Icon claim.ExpiresAt = jwt.NewNumericDate(time.Now().Add(cfg.Config.TokenExpire)) claim.Access = auth.Access{ {Name: "fs", TID: "/", Level: auth.Do}, } token := logv.AssertFuncErr(auth.GenJwt(claim)) cookie := &http.Cookie{ Name: "fstoken", // Cookie 的名称 Value: token, // Cookie 的值 Path: "/fs/u/", // Cookie 的路径,通常是根路径 MaxAge: 600, // Cookie 的最大年龄,单位是秒 HttpOnly: true, // 是否仅限 HTTP(S) 访问 Secure: false, // 是否通过安全连接传输 Cookie } http.SetCookie(x, cookie) return token, nil } else { return nil, vigo.ErrArgInvalid } } else { return nil, vigo.ErrArgInvalid } }