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 }