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

# Add JWT Authentication to Your GoFr API

Secure your GoFr API using JWT authentication with JWKS.

## How Zuplo Handles It

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

## GoFr Backend Code

```go
package main

import (
	"context"
	"errors"
	"fmt"
	"net/http"
	"strings"

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

const issuer = "https://my-api-a32f34.zuplo.api/__zuplo/issuer"

func getKeyFromJWKS(token *jwt.Token) (interface{}, error) {
	jwksURL := fmt.Sprintf("%s/.well-known/jwks.json", issuer)
	set, err := jwk.Fetch(context.Background(), jwksURL)
	if err != nil {
		return nil, err
	}

	keyID, ok := token.Header["kid"].(string)
	if !ok {
		return nil, errors.New("kid not found in token header")
	}

	if key, found := set.LookupKeyID(keyID); found {
		var publicKey interface{}
		if err := key.Raw(&publicKey); err != nil {
			return nil, err
		}
		return publicKey, nil
	}

	return nil, errors.New("unable to find appropriate key")
}

func validateJwtMiddleware(next gofr.HandlerFunc) gofr.HandlerFunc {
	return func(c *gofr.Context) error {
		authHeader := c.Request().Header.Get("Authorization")
		if authHeader == "" {
			return c.JSON(http.StatusUnauthorized, map[string]string{"error": "No token provided"})
		}

		tokenString := strings.Replace(authHeader, "Bearer ", "", 1)
		claims := jwt.StandardClaims{}

		token, err := jwt.ParseWithClaims(tokenString, &claims, getKeyFromJWKS)
		if err != nil || !token.Valid || claims.Issuer != issuer {
			return c.JSON(http.StatusUnauthorized, map[string]string{"error": "Invalid token"})
		}

		c.Set("user", claims)
		return next(c)
	}
}

func protectedRoute(c *gofr.Context) error {
	user, ok := c.Get("user").(jwt.Claims)
	if !ok {
		return c.JSON(http.StatusInternalServerError, map[string]string{"error": "User not found"})
	}

	return c.JSON(http.StatusOK, map[string]interface{}{
		"message": "Access granted",
		"user":    user,
	})
}

func main() {
	app := gofr.New()

	app.GET("/protected", validateJwtMiddleware(protectedRoute))

	if err := app.Listen(":8080"); err != nil {
		fmt.Println("Failed to start server:", err)
	}
}
```

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