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

# Add JWT Authentication to Your Chi API

Secure your Chi API using JWT authentication with JWKS.

## How Zuplo Handles It

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

## Chi Backend Code

```go
package main

import (
    "encoding/json"
    "fmt"
    "net/http"
    "strings"

    "github.com/go-chi/chi/v5"
    "github.com/go-chi/chi/v5/middleware"
    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"
)

var jwks *keyfunc.JWKS

func init() {
    var err error
    jwks, err = keyfunc.Get(jwksURL, keyfunc.Options{})
    if err != nil {
        panic(fmt.Sprintf("Failed to create JWKS from URL: %s", err))
    }
}

func JWTAuth(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        authHeader := r.Header.Get("Authorization")
        if authHeader == "" || !strings.HasPrefix(authHeader, "Bearer ") {
            http.Error(w, `{"error": "No token provided"}`, http.StatusUnauthorized)
            return
        }

        tokenStr := strings.TrimPrefix(authHeader, "Bearer ")
        token, err := jwt.Parse(tokenStr, jwks.Keyfunc)

        if err != nil || !token.Valid {
            http.Error(w, `{"error": "Invalid token"}`, http.StatusUnauthorized)
            return
        }

        if claims, ok := token.Claims.(jwt.MapClaims); ok {
            if claims["iss"] != issuer {
                http.Error(w, `{"error": "Invalid issuer"}`, http.StatusUnauthorized)
                return
            }
            r = r.WithContext(r.Context())
        }

        next.ServeHTTP(w, r)
    })
}

func main() {
    r := chi.NewRouter()
    r.Use(middleware.Logger)

    r.With(JWTAuth).Get("/protected", func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-Type", "application/json")
        json.NewEncoder(w).Encode(map[string]string{
            "message": "Access granted",
        })
    })

    http.ListenAndServe(":8080", r)
}
```

## 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)
