2024-09-04 18:15:49 +00:00

135 lines
3.8 KiB
Go

package middleware
import (
"errors"
"net/http"
"strings"
"github.com/Timothylock/go-signin-with-apple/apple"
"github.com/golang-jwt/jwt/v5"
"github.com/nose7en/ToyBoomServer/common"
"github.com/nose7en/ToyBoomServer/config"
"github.com/nose7en/ToyBoomServer/models"
"github.com/nose7en/ToyBoomServer/rpc"
"github.com/nose7en/ToyBoomServer/utils"
"github.com/spf13/cast"
"github.com/gin-gonic/gin"
)
func ValidateAppleAppLoginCode() func(c *gin.Context) {
return func(c *gin.Context) {
code := c.GetHeader(common.TokenKey)
resp, err := rpc.GetManager().AppleCli().VerifyAppToken(c, code)
if err != nil || len(resp.Error) > 0 {
common.Logger(c).WithError(err).Errorf("failed to verify apple token, response error: %s", resp.Error)
c.AbortWithStatusJSON(http.StatusOK, common.UnAuth("failed to verify apple token"))
return
}
// Get the unique user ID
unique, err := apple.GetUniqueID(resp.IDToken)
if err != nil {
common.Logger(c).WithError(err).Error("failed to get apple unique id")
c.AbortWithStatusJSON(http.StatusOK, common.UnAuth("failed to verify apple token"))
return
}
// Get detail user info
claim, err := apple.GetClaims(resp.IDToken)
if err != nil || claim == nil {
common.Logger(c).WithError(err).Error("failed to get apple user info or claim is nil")
c.AbortWithStatusJSON(http.StatusOK, common.UnAuth("failed to verify apple token"))
return
}
if config.IsDebug() {
common.Logger(c).Debugf("apple auth success, user info: %+v", claim)
}
email := cast.ToString((*claim)["email"])
emailVerified := cast.ToBool((*claim)["email_verified"])
isPrivateEmail := cast.ToBool((*claim)["is_private_email"])
userInfo := &models.User{
AppleUserID: unique,
Email: email,
IsPrivateEmail: isPrivateEmail,
EmailVerified: emailVerified,
}
common.Logger(c).Infof("apple auth success, user info: %+v", userInfo)
c.Set(common.UserInfoKey, userInfo)
}
}
func ValidateToken() func(c *gin.Context) {
return func(c *gin.Context) {
tokens, err := GetAuthTokensFromAny(c)
if err != nil {
common.Logger(c).WithError(err).Error("failed to get auth token")
c.AbortWithStatusJSON(http.StatusOK, common.UnAuth("failed to get auth token"))
return
}
var clms jwt.MapClaims
for tokenType, tokenStr := range tokens {
clms, err = validateToken(c, tokenStr)
if err == nil {
common.Logger(c).Infof("jwt middleware parse token success, token type: %s", tokenType)
break
}
}
if err != nil {
common.Logger(c).WithError(err).Errorf("jwt middleware parse token error")
c.JSON(http.StatusOK, common.UnAuth("invalid authorization"))
c.Abort()
return
}
userInfo := &models.User{}
userInfo.FromJWTClaims(clms)
if userInfo.GetUserID() <= 0 {
common.Logger(c).Errorf("failed to build user info from token")
c.AbortWithStatusJSON(http.StatusOK, common.UnAuth("invalid authorization"))
return
}
common.Logger(c).Infof("token auth success, user info: %+v", userInfo)
c.Set(common.UserInfoKey, userInfo)
}
}
func validateToken(ctx *gin.Context, tokenStr string) (u jwt.MapClaims, err error) {
if tokenStr == "" {
return nil, errors.New("token_str is empty")
}
if t, err := utils.ParseToken(config.GetSettings().JWTConfig.Secret, tokenStr); err == nil {
for k, v := range t {
ctx.Set(k, v)
}
ctx.Set(common.TokenKey, tokenStr)
return t, nil
}
return nil, err
}
func GetAuthTokensFromAny(c *gin.Context) (map[string]string, error) {
ans := map[string]string{}
if headerTokenStr := c.Request.Header.Get(common.AuthorizationKey); len(headerTokenStr) > 0 {
headerToken := strings.Split(headerTokenStr, " ")
if len(headerToken) == 2 {
ans[common.TokenKey] = headerToken[1]
} else {
ans[common.TokenKey] = headerTokenStr
}
}
if len(ans) == 0 {
return nil, errors.New("auth token is empty")
}
return ans, nil
}