go 50 lines · 7 steps

Building a JWT auth middleware in Gin

A Gin middleware that validates a Bearer JWT and hands its claims to downstream handlers.

Explained by highlit
1package auth
2 
3import (
4 "net/http"
5 "strings"
6 
7 "github.com/gin-gonic/gin"
8 "github.com/golang-jwt/jwt/v5"
9)
10 
11type Claims struct {
12 UserID string `json:"uid"`
13 Email string `json:"email"`
14 Role string `json:"role"`
15 jwt.RegisteredClaims
16}
17 
18func JWTAuth(secret []byte) gin.HandlerFunc {
19 return func(c *gin.Context) {
20 header := c.GetHeader("Authorization")
21 if header == "" {
22 c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "missing authorization header"})
23 return
24 }
25 
26 raw, ok := strings.CutPrefix(header, "Bearer ")
27 if !ok {
28 c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "invalid authorization scheme"})
29 return
30 }
31 
32 claims := &Claims{}
33 token, err := jwt.ParseWithClaims(raw, claims, func(t *jwt.Token) (interface{}, error) {
34 if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
35 return nil, jwt.ErrSignatureInvalid
36 }
37 return secret, nil
38 })
39 if err != nil || !token.Valid {
40 c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "invalid or expired token"})
41 return
42 }
43 
44 c.Set("userID", claims.UserID)
45 c.Set("email", claims.Email)
46 c.Set("role", claims.Role)
47 c.Set("claims", claims)
48 c.Next()
49 }
50}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Middleware that closes over config (like a secret) keeps handlers free of setup boilerplate.
  2. 2Always verify the token's signing method to defend against algorithm-confusion attacks.
  3. 3Storing parsed claims in the request context lets downstream handlers read the user without re-parsing.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Building a JWT auth middleware in Gin — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code