Gin JWT Claims Middleware
Extract and verify JWT claims in Gin middleware, checking issuer and role before handlers, with claims injected into context.
Code
package main
import (
"errors"
"net/http"
"strings"
"time"
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v5"
)
var errInvalidToken = errors.New("invalid token")
type AppClaims struct {
UserID string `json:"uid"`
Role string `json:"role"`
jwt.RegisteredClaims
}
func authMiddleware(secret string, allowedRoles ...string) gin.HandlerFunc {
return func(c *gin.Context) {
header := c.GetHeader("Authorization")
if !strings.HasPrefix(header, "Bearer ") {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "missing bearer token"})
return
}
token, err := jwt.ParseWithClaims(
strings.TrimPrefix(header, "Bearer "),
&AppClaims{},
func(t *jwt.Token) (interface{}, error) {
if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, errors.New("unexpected signing method")
}
return []byte(secret), nil
},
jwt.WithIssuer("auth.example.com"),
jwt.WithExpirationRequired(),
)
if err != nil || !token.Valid {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": errInvalidToken.Error()})
return
}
claims := token.Claims.(*AppClaims)
for _, role := range allowedRoles {
if claims.Role == role {
c.Set("claims", claims)
c.Next()
return
}
}
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "role not permitted"})
}
}
func main() {
r := gin.New()
r.GET("/admin/stats", authMiddleware("secret", "admin"), func(c *gin.Context) {
claims := c.MustGet("claims").(*AppClaims)
c.JSON(http.StatusOK, gin.H{"for": claims.UserID, "generated": time.Now().Unix()})
})
r.Run(":8080")
}Report a problem with this exampleOpens a pre-filled issue on GitHub.