You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
OneAuth/oa/api/token/token.go

258 lines
6.4 KiB
Go

package token
import (
2 months ago
"encoding/hex"
3 weeks ago
"encoding/json"
"net/http"
"oa/cfg"
2 months ago
"oa/errs"
"oa/libs/auth"
M "oa/models"
2 months ago
"time"
2 months ago
"github.com/golang-jwt/jwt/v5"
"github.com/veypi/OneBD/rest"
2 months ago
"github.com/veypi/utils"
"github.com/veypi/utils/logv"
)
func useToken(r rest.Router) {
2 months ago
r.Post("/salt", tokenSalt)
r.Post("/", tokenPost)
r.Get("/:token_id", tokenGet)
r.Patch("/:token_id", tokenPatch)
r.Delete("/:token_id", tokenDelete)
r.Get("/", tokenList)
}
func tokenSalt(x *rest.X) (any, error) {
opts := &M.TokenSalt{}
err := x.Parse(opts)
if err != nil {
return nil, err
}
data := &M.User{}
2 months ago
query := "username = ?"
if opts.Typ == nil {
} else if *opts.Typ == "email" {
query = "email = ?"
} else if *opts.Typ == "phone" {
query = "phone = ?"
}
2 months ago
err = cfg.DB().Where(query, opts.Username).First(data).Error
return map[string]string{"salt": data.Salt, "id": data.ID}, err
}
2 months ago
// for user login app
func tokenPost(x *rest.X) (any, error) {
opts := &M.TokenPost{}
err := x.Parse(opts)
if err != nil {
return nil, err
}
aid := cfg.Config.ID
3 weeks ago
if opts.AppID != nil && *opts.AppID != "" {
2 months ago
aid = *opts.AppID
}
data := &M.Token{}
claim := &auth.Claims{}
claim.IssuedAt = jwt.NewNumericDate(time.Now())
3 weeks ago
claim.Issuer = cfg.Config.ID
if opts.Refresh != nil {
typ := "app"
if opts.Typ != nil {
typ = *opts.Typ
}
2 months ago
// for other app redirect
refresh, err := auth.ParseJwt(*opts.Refresh)
3 weeks ago
if err != nil {
2 months ago
return nil, err
}
3 weeks ago
if refresh.ID == "" {
return nil, errs.AuthInvalid
}
err = cfg.DB().Where("id = ?", refresh.ID).First(data).Error
2 months ago
if err != nil {
return nil, err
}
3 weeks ago
claim.AID = aid
claim.UID = refresh.UID
claim.Name = refresh.Name
claim.Icon = refresh.Icon
claim.ExpiresAt = jwt.NewNumericDate(time.Now().Add(time.Minute * 10))
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").
3 weeks ago
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
3 weeks ago
if aid == cfg.Config.ID {
return auth.GenJwt(claim)
}
app := &M.App{}
err = cfg.DB().Where("id = ?", aid).First(app).Error
if err != nil {
return nil, err
}
return auth.GenJwtWithKey(claim, app.Key)
} else if aid != cfg.Config.ID {
// 只能生成其他应用的refresh token
newToken := &M.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
3 weeks ago
claim.ID = newToken.ID
claim.ExpiresAt = jwt.NewNumericDate(newToken.ExpiredAt)
return auth.GenJwt(claim)
} else {
// 其他应用获取访问oa的token
claim.Access = make(auth.Access, 0, 10)
if data.OverPerm != "" {
err = json.Unmarshal([]byte(data.OverPerm), &claim.Access)
if err != nil {
return nil, err
}
}
return auth.GenJwt(claim)
}
} else if typ == "ufs" {
claim.AID = refresh.AID
claim.UID = refresh.UID
claim.Name = refresh.Name
claim.Icon = refresh.Icon
2 months ago
claim.ExpiresAt = jwt.NewNumericDate(time.Now().Add(time.Minute * 10))
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
3 weeks ago
} else {
return nil, errs.ArgsInvalid
2 months ago
}
} else if opts.Code != nil && aid == cfg.Config.ID && opts.Salt != nil && opts.UserID != nil {
2 months ago
// for oa login
user := &M.User{}
err = cfg.DB().Where("id = ?", opts.UserID).Find(user).Error
if err != nil {
return nil, err
}
logv.Info().Str("user", user.ID).Msg("login")
code := *opts.Code
salt := logv.AssertFuncErr(hex.DecodeString(*opts.Salt))
key := logv.AssertFuncErr(hex.DecodeString(user.Code))
de, err := utils.AesDecrypt([]byte(code), key, salt)
if err != nil || de != user.ID {
2 months ago
return nil, errs.AuthFailed
2 months ago
}
data.UserID = *opts.UserID
2 months ago
data.AppID = aid
3 weeks ago
data.ExpiredAt = time.Now().Add(time.Hour * 72)
2 months ago
if opts.OverPerm != nil {
data.OverPerm = *opts.OverPerm
}
2 months ago
if opts.Device != nil {
data.Device = *opts.Device
}
2 months ago
data.Ip = x.GetRemoteIp()
2 months ago
logv.AssertError(cfg.DB().Create(data).Error)
claim.ID = data.ID
2 months ago
claim.AID = aid
claim.UID = user.ID
claim.Name = user.Username
claim.Icon = user.Icon
2 months ago
claim.ExpiresAt = jwt.NewNumericDate(data.ExpiredAt)
2 months ago
if user.Nickname != "" {
claim.Name = user.Nickname
}
3 weeks ago
return auth.GenJwt(claim)
2 months ago
} else {
return nil, errs.ArgsInvalid
}
}
func tokenGet(x *rest.X) (any, error) {
opts := &M.TokenGet{}
err := x.Parse(opts)
if err != nil {
return nil, err
}
data := &M.Token{}
err = cfg.DB().Where("id = ?", opts.ID).First(data).Error
return data, err
}
func tokenPatch(x *rest.X) (any, error) {
opts := &M.TokenPatch{}
err := x.Parse(opts)
if err != nil {
return nil, err
}
data := &M.Token{}
err = cfg.DB().Where("id = ?", opts.ID).First(data).Error
if err != nil {
return nil, err
}
optsMap := make(map[string]interface{})
if opts.ExpiredAt != nil {
optsMap["expired_at"] = opts.ExpiredAt
}
if opts.OverPerm != nil {
optsMap["over_perm"] = opts.OverPerm
}
err = cfg.DB().Model(data).Updates(optsMap).Error
return data, err
}
func tokenDelete(x *rest.X) (any, error) {
opts := &M.TokenDelete{}
err := x.Parse(opts)
if err != nil {
return nil, err
}
data := &M.Token{}
err = cfg.DB().Where("id = ?", opts.ID).Delete(data).Error
return data, err
}
func tokenList(x *rest.X) (any, error) {
opts := &M.TokenList{}
err := x.Parse(opts)
if err != nil {
return nil, err
}
data := make([]*M.Token, 0, 10)
query := cfg.DB()
query = query.Where("user_id = ?", opts.UserID)
query = query.Where("app_id = ?", opts.AppID)
err = query.Limit(opts.Limit).Find(&data).Error
return data, err
}