---
title: "Add JWT Authentication to Your Gin API"
description: "Secure your Gin API using JWT authentication with JWKS."
canonicalUrl: "https://zuplo.com/use-cases/api-key-auth/go/gin/jwt-backend"
framework: "Gin"
language: "Go"
authStrategy: "JWT with JWKS"
pageType: use-case
---

# Add JWT Authentication to Your Gin API

Secure your Gin API using JWT authentication with JWKS.

## How Zuplo Handles It

Let Zuplo issue short-lived JWTs signed with a JWKS your Gin backend can verify — no long-lived API keys touch your origin.

## Gin Backend Code

```go
package main

import (
    "net/http"
    "fmt"

    "github.com/gin-gonic/gin"
    jwt "github.com/golang-jwt/jwt/v4"
    "github.com/MicahParks/keyfunc"
)

const (
    jwksURL = "https://my-api-a32f34.zuplo.api/__zuplo/issuer/.well-known/jwks.json"
    issuer  = "https://my-api-a32f34.zuplo.api/__zuplo/issuer"
)

func validateJWT() gin.HandlerFunc {
    // Fetch the JWKS
    options := keyfunc.Options{
        RefreshErrorHandler: func(err error) {
            fmt.Printf("There was an error with the jwt.Keyfunc\nError: %s\n", err.Error())
        },
    }
    jwks, err := keyfunc.Get(jwksURL, options)
    if err != nil {
        panic(fmt.Sprintf("Failed to create JWKS from URL: %s", err))
    }

    return func(c *gin.Context) {
        authHeader := c.GetHeader("Authorization")
        if authHeader == "" {
            c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "No token provided"})
            return
        }

        tokenStr := authHeader[len("Bearer "):]
        token, err := jwt.Parse(tokenStr, jwks.Keyfunc)

        if err != nil || !token.Valid {
            c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Invalid token"})
            return
        }

        if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
            if claims["iss"] != issuer {
                c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Invalid issuer"})
                return
            }

            c.Set("user", claims)
            c.Next()
        } else {
            c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Invalid token claims"})
        }
    }
}

func main() {
    r := gin.Default()

    r.GET("/protected", validateJWT(), func(c *gin.Context) {
        user, _ := c.Get("user")
        c.JSON(http.StatusOK, gin.H{
            "message": "Access granted",
            "user":    user,
        })
    })

    r.Run(":8080")
}
```

## Example Request

```bash
curl -X GET \
  'https://your-api.zuplo.dev/your-route' \
  -H 'Authorization: Bearer YOUR_API_KEY'
```

## Learn More

- [API Key Authentication on Zuplo](https://zuplo.com/docs/policies/api-key-auth-inbound)
- [JWT Authentication on Zuplo](https://zuplo.com/docs/policies/open-id-jwt-auth-inbound)
- [All use cases](https://zuplo.com/use-cases)
