package handlers import ( "fmt" "net/http" "strings" "github.com/gin-gonic/gin" "github.com/golang-jwt/jwt/v5" ) type jwtClaims struct { UserID string `json:"user_id"` jwt.RegisteredClaims } func generateToken(userID, secret string) (string, error) { claims := jwtClaims{ UserID: userID, RegisteredClaims: jwt.RegisteredClaims{ Subject: userID, }, } return jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString([]byte(secret)) } func parseToken(tokenStr, secret string) (*jwtClaims, error) { token, err := jwt.ParseWithClaims(tokenStr, &jwtClaims{}, func(t *jwt.Token) (interface{}, error) { if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok { return nil, fmt.Errorf("unexpected signing method") } return []byte(secret), nil }) if err != nil { return nil, err } claims, ok := token.Claims.(*jwtClaims) if !ok || !token.Valid { return nil, fmt.Errorf("invalid token") } return claims, nil } func AuthRequired(jwtSecret string) gin.HandlerFunc { return func(c *gin.Context) { // Cookie-first, Bearer header as fallback for backwards compat tokenStr, err := c.Cookie(cookieName) if err != nil || tokenStr == "" { header := c.GetHeader("Authorization") tokenStr = strings.TrimPrefix(header, "Bearer ") } if tokenStr == "" { c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"}) return } claims, err := parseToken(tokenStr, jwtSecret) if err != nil { c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "invalid token"}) return } c.Set("userID", claims.UserID) c.Next() } } func currentUserID(c *gin.Context) string { id, _ := c.Get("userID") s, _ := id.(string) return s }