|  |  |  |  | //
 | 
					
						
							|  |  |  |  | // post.go
 | 
					
						
							|  |  |  |  | // Copyright (C) 2025 veypi <i@veypi.com>
 | 
					
						
							|  |  |  |  | // 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 | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | } |