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

# Add JWT Authentication to Your Tyk API

Secure your Tyk API using JWT authentication with JWKS.

## How Zuplo Handles It

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

## Tyk Backend Code

```go
package main

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

	"github.com/dgrijalva/jwt-go"
	"github.com/lestrrat-go/jwx/jwk"
	"github.com/lestrrat-go/jwx/jwt"
)

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

// Struct to hold cached keys
var jwksCache *jwk.Set

// Fetches the JWKS for validating JWTs
func fetchJWKS() (*jwk.Set, error) {
	if jwksCache != nil {
		return jwksCache, nil
	}

	resp, err := http.Get(jwksURL)
	if err != nil {
		return nil, fmt.Errorf("failed to fetch JWKS: %v", err)
	}
	defer resp.Body.Close()

	jwks := jwk.NewSet()
	if err := json.NewDecoder(resp.Body).Decode(&jwks); err != nil {
		return nil, fmt.Errorf("failed to decode JWKS: %v", err)
	}
	jwksCache = jwks
	return jwks, nil
}

// Middleware to validate JWT
func validateJWT(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		tokenString := r.Header.Get("Authorization")
		if tokenString == "" {
			http.Error(w, "No token provided", http.StatusUnauthorized)
			return
		}

		jwks, err := fetchJWKS()
		if err != nil {
			http.Error(w, fmt.Sprintf("Could not fetch JWKS: %v", err), http.StatusInternalServerError)
			return
		}

		token, err := jwt.Parse(
			tokenString,
			func(token *jwt.Token) (interface{}, error) {
				if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
					return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
				}

				keyID, ok := token.Header["kid"].(string)
				if !ok {
					return nil, fmt.Errorf("invalid kid in header")
				}

				key, found := jwks.LookupKeyID(keyID)
				if !found {
					return nil, fmt.Errorf("key not found in JWKS")
				}

				return key.Materialize()
			},
		)

		if err != nil {
			http.Error(w, fmt.Sprintf("Invalid token: %v", err), http.StatusUnauthorized)
			return
		}

		if !token.Valid {
			http.Error(w, "Invalid token", http.StatusUnauthorized)
			return
		}

		claims, ok := token.Claims.(jwt.MapClaims)
		if !ok {
			http.Error(w, "Invalid claims", http.StatusUnauthorized)
			return
		}

		r.Header.Set("X-User", fmt.Sprintf("%v", claims["sub"]))
		next.ServeHTTP(w, r)
	})
}

// Protected route example
func protectedHandler(w http.ResponseWriter, r *http.Request) {
	user := r.Header.Get("X-User")
	w.WriteHeader(http.StatusOK)
	fmt.Fprintf(w, "Access granted, user: %s", user)
}

func main() {
	mux := http.NewServeMux()
	mux.Handle("/protected", validateJWT(http.HandlerFunc(protectedHandler)))

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

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