AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified Apache-2.0 Self-run

Go Jwt Middleware

skill-auth0-agent-skills-go-jwt-middleware · by auth0

>

No reviews yet
0 installs
16 views
0.0% view→install

Install

$ agentstack add skill-auth0-agent-skills-go-jwt-middleware

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access Used
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets Used
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-auth0-agent-skills-go-jwt-middleware)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
1mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Go Jwt Middleware? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Go JWT Middleware Integration

Protect Go HTTP API endpoints with JWT access token validation using github.com/auth0/go-jwt-middleware/v3.

> Agent instruction: Before providing SDK setup instructions, fetch the latest release version by running: > ``bash > gh api repos/auth0/go-jwt-middleware/releases/latest --jq '.tag_name' > `` > Use the returned version in all dependency lines instead of any hardcoded version below.


Prerequisites

  • Go 1.21 or higher
  • Auth0 API configured (not Application - must be API resource)
  • If you don't have Auth0 set up yet, use the auth0-quickstart skill first

When NOT to Use

  • Go server-rendered web applications - Use go-auth0 for session-based web apps
  • Single Page Applications - Use auth0-react, auth0-vue, or auth0-angular for client-side auth
  • Mobile applications - Use auth0-swift, auth0-android, or auth0-react-native
  • Non-Go backends - Use auth0-aspnetcore-api for .NET, express-jwt for Node.js

Quick Start Workflow

1. Install SDK

go get github.com/auth0/go-jwt-middleware/v3
go get github.com/joho/godotenv

2. Create Auth0 API

You need an API (not Application) in Auth0.

> Agent instruction: If the user's prompt already provides Auth0 credentials (domain and audience), use them directly — skip the setup choice question below and proceed to Step 3 to write the .env file. > > STOP — ask the user before proceeding. > > Ask exactly this question and wait for their answer before doing anything else: > > > "How would you like to create the Auth0 API resource? > > 1. Automated — I'll use the Auth0 CLI to create the API resource and write the exact values to your .env file automatically. > > 2. Manual — You create the API yourself in the Auth0 Dashboard (or via auth0 apis create) and provide me the Domain and Audience. > > > > Which do you prefer? (1 = Automated / 2 = Manual)" > > Do NOT proceed to any setup steps until the user has answered. Do NOT default to manual.

If the user chose Automated, follow the [Setup Guide](references/setup.md) for the "Initial Setup" section (steps 1–6). The automated path writes .env for you — skip Step 3 below and proceed directly to Step 4.

> Agent instruction (Automated path checkpoints): > > When following the automated path, you MUST complete these checkpoints in order. Do NOT skip any: > > 1. Check Auth0 CLI — verify auth0 is installed. > 2. Check Auth0 login — run auth0 tenants list to verify authentication. > 3. Confirm active tenant — show the user which tenant is active and ask: "Your active Auth0 tenant is `. Is this the correct tenant?"_ Wait for confirmation. If they say no, ask them to run auth0 tenants use in their terminal. > 4. **Ask about API name and identifier** — use AskUserQuestion: _"What would you like to name your Auth0 API, and what identifier (audience) should it use? For example: Name: 'My Go API', Identifier: 'https://my-api.example.com'. The identifier is a logical URI that doesn't need to resolve — it just uniquely identifies your API."_ Wait for answer. If the user is unsure, suggest deriving the identifier from the project's module name in go.mod (e.g., https://). > 5. **Ask about scopes** — use AskUserQuestion: _"What scopes (permissions) does your API need? For example: read:users, write:users, read:products. If you're not sure yet, I can start with common defaults and you can add more later."_ Wait for answer. > 6. **Check for existing API** — run auth0 apis list and check if an API with the intended identifier already exists. If it does, ask the user whether to reuse it or create a new one with a different identifier. > 7. **Create the API resource** — using the name, identifier, and scopes from steps 4–5. > 8. **Handle .env** — if a .env file already exists, ask before modifying it. Never read existing .env contents (may contain secrets). If no .env exists, write one with AUTH0DOMAIN and AUTH0_AUDIENCE. > 9. **Add .env to .gitignore` — if not already present. > 10. Proceed to code integration** — skip Step 3 (already done) and go directly to Step 4 to write the middleware code.

If the user chose Manual, follow the [Setup Guide](references/setup.md) (Manual Setup section) for full instructions. Then continue with Step 3 below.

Quick reference for manual API creation:

# Using Auth0 CLI
auth0 apis create \
  --name "My Go API" \
  --identifier https://my-api.example.com

Or create manually in Auth0 Dashboard → Applications → APIs

3. Configure .env

AUTH0_DOMAIN=your-tenant.auth0.com
AUTH0_AUDIENCE=https://my-api.example.com

Important: Domain must NOT include https://. The middleware constructs the issuer URL automatically.

4. Configure main.go

> Agent instruction (integrating with existing code): > > Before writing code, determine whether you are: > - A) Adding auth to an existing project — the user already has a main.go with routes defined. In this case, do NOT replace their file with the template below. Instead: > 1. Add the necessary imports (jwtmiddleware, jwks, validator, godotenv, net/url, os, context, strings). > 2. Add the CustomClaims struct and methods. > 3. Add the middleware setup code (issuer URL, JWKS provider, validator, middleware) near the top of main(). > 4. Ask which endpoints to protect (see below). > 5. Wrap the specified handlers with middleware.CheckJWT(). > > - B) Creating a new project from scratch — use the full template below as a starting point. > > STOP — ask which endpoints to protect: > > If the user's request does NOT explicitly specify which endpoints to protect, ask: > > > "Which endpoints should require authentication? For example: > > - All except health/public — protect everything, leave only specific public routes open > > - Specific routes — tell me which routes need auth > > > > Also, do any endpoints need specific scope/permission checks (e.g., write:users for POST/DELETE), or is a valid JWT sufficient for all?" > > Wait for the answer. If the user says "all" or "everything except health", protect all routes except /health (or whatever they specify as public). If they specify scope requirements per endpoint, implement per-route scope checks using customClaims.HasScope().

package main

import (
	"context"
	"encoding/json"
	"log"
	"net/http"
	"net/url"
	"os"
	"strings"

	jwtmiddleware "github.com/auth0/go-jwt-middleware/v3"
	"github.com/auth0/go-jwt-middleware/v3/jwks"
	"github.com/auth0/go-jwt-middleware/v3/validator"
	"github.com/joho/godotenv"
)

// CustomClaims contains custom data we want from the token.
type CustomClaims struct {
	Scope       string   `json:"scope"`
	Permissions []string `json:"permissions"`
}

func (c CustomClaims) Validate(ctx context.Context) error {
	return nil
}

func (c CustomClaims) HasScope(expectedScope string) bool {
	for _, scope := range strings.Split(c.Scope, " ") {
		if scope == expectedScope {
			return true
		}
	}
	return false
}

func main() {
	if err := godotenv.Load(); err != nil {
		log.Fatalf("Error loading .env file: %v", err)
	}

	issuerURL, err := url.Parse("https://" + os.Getenv("AUTH0_DOMAIN") + "/")
	if err != nil {
		log.Fatalf("Failed to parse issuer URL: %v", err)
	}

	provider, err := jwks.NewCachingProvider(
		jwks.WithIssuerURL(issuerURL),
	)
	if err != nil {
		log.Fatalf("Failed to set up JWKS provider: %v", err)
	}

	jwtValidator, err := validator.New(
		validator.WithKeyFunc(provider.KeyFunc),
		validator.WithAlgorithm(validator.RS256),
		validator.WithIssuer(issuerURL.String()),
		validator.WithAudience(os.Getenv("AUTH0_AUDIENCE")),
		validator.WithCustomClaims(func() validator.CustomClaims {
			return &CustomClaims{}
		}),
	)
	if err != nil {
		log.Fatalf("Failed to set up JWT validator: %v", err)
	}

	middleware, err := jwtmiddleware.New(
		jwtmiddleware.WithValidator(jwtValidator),
	)
	if err != nil {
		log.Fatalf("Failed to set up JWT middleware: %v", err)
	}

	mux := http.NewServeMux()

	// Public endpoint - no authentication
	mux.HandleFunc("/api/public", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		json.NewEncoder(w).Encode(map[string]string{"message": "Hello from a public endpoint!"})
	})

	// Protected endpoint - requires valid JWT
	mux.Handle("/api/private", middleware.CheckJWT(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		claims, err := jwtmiddleware.GetClaims[*validator.ValidatedClaims](r.Context())
		if err != nil {
			http.Error(w, `{"message":"Failed to get token claims."}`, http.StatusInternalServerError)
			return
		}
		w.Header().Set("Content-Type", "application/json")
		json.NewEncoder(w).Encode(map[string]string{
			"message": "Hello from a private endpoint!",
			"userId":  claims.RegisteredClaims.Subject,
		})
	})))

	// Protected + scoped endpoint - requires JWT with specific scope
	mux.Handle("/api/private-scoped", middleware.CheckJWT(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		claims, err := jwtmiddleware.GetClaims[*validator.ValidatedClaims](r.Context())
		if err != nil {
			http.Error(w, `{"message":"Failed to get token claims."}`, http.StatusInternalServerError)
			return
		}
		customClaims := claims.CustomClaims.(*CustomClaims)
		if !customClaims.HasScope("read:messages") {
			w.Header().Set("Content-Type", "application/json")
			w.WriteHeader(http.StatusForbidden)
			json.NewEncoder(w).Encode(map[string]string{"message": "Insufficient scope."})
			return
		}
		w.Header().Set("Content-Type", "application/json")
		json.NewEncoder(w).Encode(map[string]string{"message": "Hello from a scoped endpoint!"})
	})))

	log.Println("Server listening on :8080")
	log.Fatal(http.ListenAndServe(":8080", mux))
}

5. Protect Endpoints

Use middleware.CheckJWT() to wrap handlers that require authentication:

// Public endpoint - no authentication
mux.HandleFunc("/api/public", publicHandler)

// Protected endpoint - requires valid JWT
mux.Handle("/api/private", middleware.CheckJWT(http.HandlerFunc(privateHandler)))

// Protected + scoped - requires JWT with specific permission
mux.Handle("/api/private-scoped", middleware.CheckJWT(http.HandlerFunc(privateScopedHandler)))

6. Test API

> Agent instruction: After writing the code, verify the build compiles: > ``bash > go build ./... > ` > If compilation fails, diagnose the error and fix it. Repeat up to 5-6 times. > > **Failcheck:** If the build still fails after 5-6 fix attempts, stop and ask the user using AskUserQuestion`: > "The build is still failing after several fix attempts. How would you like to proceed?" > - Let me continue fixing iteratively > - Fix it manually — I'll show the remaining errors > - Skip build verification — proceed without a successful build > > Repeat this check after every 5-6 iterations if errors persist.

> Agent instruction: STOP — ask after build succeeds. > > Once the build compiles successfully, ask the user: > > > "Your API is set up and compiles successfully. Would you like me to help you set up testing? > > 1. Yes — I'll help you configure an M2M application to get test tokens. > > 2. No — I'm done for now. > > > > Which do you prefer?" > > Do NOT proceed with testing setup unless the user says yes. If they say no, summarize what was done and stop.

> Agent instruction (M2M app setup — only if user wants testing): > > If the user chose to set up testing, ask: > > > "To test your protected endpoints, you need a Machine-to-Machine (M2M) application authorized to request tokens for this API. > > 1. Create new — I'll create a new M2M application and authorize it for this API. > > 2. Use existing — You already have an M2M application. Provide the Client ID and I'll authorize it for this API. > > > > Which do you prefer? (1 = Create new / 2 = Use existing)" > > Do NOT proceed until the user answers. Do NOT silently pick an existing application from the tenant. > > If the user chose "Create new": > ``bash > auth0 apps create \ > --name " (Test App)" \ > --type m2m \ > --no-input --json > ` > Parse the JSON to extract client_id. Do NOT use --reveal-secrets — never expose client secrets in agent context. > Then create a client grant: > `bash > auth0 api post "client-grants" --data '{ > "client_id": "", > "audience": "", > "scope": [""] > }' > ` > > **If the user chose "Use existing":** > Ask for the Client ID. Then create a client grant to authorize it for this API: > `bash > auth0 api post "client-grants" --data '{ > "client_id": "", > "audience": "", > "scope": [""] > }' > `` > If the grant already exists (409 conflict), that's fine — the app is already authorized.

> Agent instruction (TOKEN ISOLATION — CRITICAL): > > The agent MUST NEVER directly see or display access token values. Token security rules: > - Do NOT run auth0 test token on its own — it outputs the token to stdout > - Do NOT run curl commands to the /oauth/token endpoint on their own > - Do NOT ask the user to paste their token into the conversation > - Do NOT echo, print, or log the token value > - Do NOT store the token in a file > > Secure testing approach (single-command chain): > > If the user explicitly asks to test the protected endpoints, the agent MAY use a single-command chain that captures the token into a shell variable and immediately uses it — the token value is never printed or visible to the agent: > > ``bash > TEST_TOKEN=$(auth0 test token --audience --scopes 2>/dev/null | grep -o 'ey[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*') && \ > [ -n "$TEST_TOKEN" ] && echo "Token acquired (${#TEST_TOKEN} chars)" && \ > curl -s http://localhost:8080/ -H "Authorization: Bearer $TEST_TOKEN" > ` > > **Security guarantees of this approach:** > - $(...) captures stdout — the token is consumed into the variable, not displayed > - grep -o extracts only the JWT pattern (ey...) — no surrounding output leaks > - echo "Token acquired (${#TESTTOKEN} chars)" confirms success by printing LENGTH only, never the value > - The shell variable $TESTTOKEN exists only for the duration of that single command chain — it dies immediately after > - Agent sees only: "Token acquired (834 chars)" + the API response body (JSON) > - No file is written, no env is exported, nothing persists > > **Rules for using this pattern:** > 1. ONLY use when the user explicitly asks to test (e.g., "test it", "run the tests", "verify endpoints work") > 2. Always chain token acquisition + curl in a SINGLE && command — never separate them into two Bash calls > 3. To test multiple endpoints, chain multiple curls in the same command: > `bash > TEST_TOKEN=$(auth0 test token --audience --scopes 2>/dev/null | grep -o 'ey[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*') && \ > [ -n "$TEST_TOKEN" ] && echo "Token acquired (${#TEST_TOKEN} chars)" && \ > echo "=== GET /users ===" && \ > curl -s http://localhost:8080/users -H "Authorization: Bearer $TEST_TOKEN" && \ > echo "" && echo "=== POST /users ===" && \ > curl -s -X POST http://localhost:8080/users -H "Authorization: Bearer $TEST_TOKEN" -d '{"id":"99","name":"Test","email":"test@example.com"}' && \ > echo "" && echo "=== GET /products ===" && \ > curl -s http://localhost:8080/products -H "Authorization: Bearer $TEST_TOKEN" > ` > 4. NEVER add echo $TESTTOKEN, printf $TESTTOKEN, or any command that would print the raw token value > 5. If the token acquisition fails (empty variable), the [ -n "$TESTTOKEN" ] check will halt the chain — report to the user that the M2M app may not be authorized > 6. **Client ID is REQUIRED** — the auth0 test token command requires a Client ID to be passed as the first argument. This MUST be the clientid` obtained from the M2M app setup

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.