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

# Add JWT Authentication to Your Traefik API

Secure your Traefik API using JWT authentication with JWKS.

## How Zuplo Handles It

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

## Traefik Backend Code

```go
// Traefik middleware plugin for JWT authentication with JWKS
package jwt_auth

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

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

// Config holds the plugin configuration
type Config struct {
    JwksURL string `json:"jwksUrl"`
    Issuer  string `json:"issuer"`
}

// CreateConfig creates the default plugin configuration
func CreateConfig() *Config {
    return &Config{
        JwksURL: "https://my-api-a32f34.zuplo.api/__zuplo/issuer/.well-known/jwks.json",
        Issuer:  "https://my-api-a32f34.zuplo.api/__zuplo/issuer",
    }
}

// JWTAuth is the middleware handler
type JWTAuth struct {
    next   http.Handler
    jwks   *keyfunc.JWKS
    issuer string
    name   string
}

// New creates a new JWTAuth middleware
func New(ctx context.Context, next http.Handler, config *Config, name string) (http.Handler, error) {
    jwks, err := keyfunc.Get(config.JwksURL, keyfunc.Options{})
    if err != nil {
        return nil, fmt.Errorf("failed to create JWKS: %w", err)
    }

    return &JWTAuth{
        next:   next,
        jwks:   jwks,
        issuer: config.Issuer,
        name:   name,
    }, nil
}

func (j *JWTAuth) ServeHTTP(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, j.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"] != j.issuer {
            http.Error(w, `{"error": "Invalid issuer"}`, http.StatusUnauthorized)
            return
        }
        // Pass claims to downstream services via headers
        if sub, ok := claims["sub"].(string); ok {
            r.Header.Set("X-User-Sub", sub)
        }
    }

    j.next.ServeHTTP(w, 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)
