Baike.dev
All toolsOnline toolsAI codingTrendingOpen sourceNewsSubmit
Log in
< Back to tools
G

go-jwt-middleware

> 编程语言
Open source

A Middleware for Go Programming Language to check for JWTs on HTTP requests

1.2K stars0 likes0 views
WebsiteGitHub

About

A Middleware for Go Programming Language to check for JWTs on HTTP requests

Documentation

  • Godoc - explore the go-jwt-middleware documentation.
  • Docs site — explore our docs site and learn more about Auth0.
  • Quickstart - our guide for adding go-jwt-middleware to your app.
  • Migration Guide - upgrading from v2 to v3.

What's New in v3

v3 introduces significant improvements while maintaining the simplicity and flexibility you expect:

Pure Options Pattern

All configuration through functional options for better IDE support and compile-time validation:

go
// v3: Clean, self-documenting API
validator.New(
    validator.WithKeyFunc(keyFunc),
    validator.WithAlgorithm(validator.RS256),
    validator.WithIssuer("https://issuer.example.com/"),
    validator.WithAudience("my-api"),
)

Enhanced JWT Library (lestrrat-go/jwx v3)

  • Better performance and security
  • Support for 14 signature algorithms (including EdDSA, ES256K)
  • Improved JWKS handling with automatic kid matching
  • Active maintenance and modern Go support

️ Core-Adapter Architecture

Framework-agnostic validation logic that can be reused across HTTP, gRPC, and other transports:

HTTP Middleware → Core Engine → Validator
gRPC Interceptor → Core Engine → Validator

gRPC Support

Native gRPC integration with interceptors for unary and streaming methods:

…

The gRPC interceptor also supports Multiple Custom Domains (MCD) with WithIssuers or WithIssuersResolver, and automatic JWKS routing via MultiIssuerProvider. See the gRPC integration docs for details.

Type-Safe Claims with Generics

Use Go 1.24+ generics for compile-time type safety:

go
claims, err := jwtmiddleware.GetClaims[*validator.ValidatedClaims](r.Context())

Built-in Logging Support

Optional structured logging compatible with log/slog:

go
jwtmiddleware.New(
    jwtmiddleware.WithValidator(jwtValidator),
    jwtmiddleware.WithLogger(slog.Default()),
)

️ Enhanced Security

  • RFC 6750 compliant error responses
  • Secure defaults (credentials required, clock skew = 0)
  • DPoP support (RFC 9449) for proof-of-possession tokens

DPoP (Demonstrating Proof-of-Possession)

Prevent token theft with proof-of-possession:

go
jwtmiddleware.New(
    jwtmiddleware.WithValidator(jwtValidator),
    jwtmiddleware.WithDPoPMode(jwtmiddleware.DPoPRequired),
)

Getting Started

Requirements

This library follows the same support policy as Go. The last two major Go releases are actively supported and compatibility issues will be fixed.

  • Go 1.24+

Installation

bash
go get github.com/auth0/go-jwt-middleware/v3

Basic Usage

Simple Example with HMAC

…

Try it out:

bash
curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnby1qd3QtbWlkZGxld2FyZS1leGFtcGxlIiwiYXVkIjoiYXVkaWVuY2UtZXhhbXBsZSIsInN1YiI6IjEyMzQ1Njc4OTAiLCJuYW1lIjoiSm9obiBEb2UiLCJpYXQiOjE1MTYyMzkwMjIsInVzZXJuYW1lIjoidXNlcjEyMyJ9.XFhrzWzntyINkgoRt2mb8dES84dJcuOoORdzKfwUX70" \
  http://localhost:3000

This JWT is signed with secret and contains:

json
{
  "iss": "go-jwt-middleware-example",
  "aud": "audience-example",
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022,
  "username": "user123"
}

Production Example with JWKS and Auth0

…

Testing the Server

After running the server (go run main.go), test with curl:

Valid Token:

bash
$ curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnby1qd3QtbWlkZGxld2FyZS1leGFtcGxlIiwiYXVkIjoiYXVkaWVuY2UtZXhhbXBsZSIsInN1YiI6IjEyMzQ1Njc4OTAiLCJuYW1lIjoiSm9obiBEb2UiLCJpYXQiOjE1MTYyMzkwMjIsInVzZXJuYW1lIjoidXNlcjEyMyJ9.XFhrzWzntyINkgoRt2mb8dES84dJcuOoORdzKfwUX70" localhost:3000

Response:

json
{
  "CustomClaims": null,
  "RegisteredClaims": {
    "iss": "go-jwt-middleware-example",
    "aud": ["audience-example"],
    "sub": "1234567890",
    "name": "John Doe",
    "iat": 1516239022
  }
}

Invalid Token:

bash
$ curl -v -H "Authorization: Bearer invalid.token.here" localhost:3000

Response:

HTTP/1.1 401 Unauthorized
Content-Type: application/json
WWW-Authenticate: Bearer realm="api", error="invalid_token", error_description="The access token is malformed"

{
  "error": "invalid_token",
  "error_description": "The access token is malformed",
  "error_code": "token_malformed"
}

Advanced Usage

Custom Claims

Define and validate custom claims:

…

Optional Credentials

Allow both authenticated and public access:

go
middleware, err := jwtmiddleware.New(
	jwtmiddleware.WithValidator(jwtValidator),
	jwtmiddleware.WithCredentialsOptional(true),
)

func handler(w http.ResponseWriter, r *http.Request) {
	claims, err := jwtmiddleware.GetClaims[*validator.ValidatedClaims](r.Context())
	if err != nil {
		// No JWT - serve public content
		w.Write([]byte("Public content"))
		return
	}
	// JWT present - serve authenticated content
	w.Write([]byte("Hello, " + claims.RegisteredClaims.Subject))
}

Custom Token Extraction

Extract tokens from cookies or query parameters:

…

URL Exclusions

Skip JWT validation for specific URLs:

go
middleware, err := jwtmiddleware.New(
	jwtmiddleware.WithValidator(jwtValidator),
	jwtmiddleware.WithExclusionUrls([]string{
		"/health",
		"/metrics",
		"/public",
	}),
)

Structured Logging

Enable logging with log/slog or compatible loggers:

go
import "log/slog"

logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
	Level: slog.LevelDebug,
}))

middleware, err := jwtmiddleware.New(
	jwtmiddleware.WithValidator(jwtValidator),
	jwtmiddleware.WithLogger(logger),
)

Custom Error Handling

Implement custom error responses:

…

Error Responses

The default error handler returns specific HTTP status codes and structured JSON responses for each type of JWT validation failure:

Failure HTTP Status error error_code
Malformed token 401 invalid_token token_malformed
Invalid algorithm 401 invalid_token invalid_algorithm
Invalid signature 401 invalid_token invalid_signature
Expired token 401 invalid_token token_expired
Not yet valid (nbf/iat) 401 invalid_token token_not_yet_valid
Invalid issuer 401 invalid_token invalid_issuer
Invalid audience 401 invalid_token invalid_audience
Invalid claims 401 invalid_token invalid_claims
JWKS fetch failed 401 invalid_token jwks_fetch_failed
JWKS key not found 401 invalid_token jwks_key_not_found
Missing token 401 invalid_token —

Example response for an expired token:

json
{
  "error": "invalid_token",
  "error_description": "The access token expired",
  "error_code": "token_expired"
}

All error responses include RFC 6750 compliant WWW-Authenticate headers.

Available error code constants (on the jwtmiddleware package):

ErrorCodeTokenMalformed, ErrorCodeTokenExpired, ErrorCodeTokenNotYetValid, ErrorCodeInvalidSignature, ErrorCodeInvalidAlgorithm, ErrorCodeInvalidIssuer, ErrorCodeInvalidAudience, ErrorCodeInvalidClaims, ErrorCodeJWKSFetchFailed, ErrorCodeJWKSKeyNotFound

Clock Skew Tolerance

Allow for time drift between servers:

go
jwtValidator, err := validator.New(
	validator.WithKeyFunc(keyFunc),
	validator.WithAlgorithm(validator.RS256),
	validator.WithIssuer("https://issuer.example.com/"),
	validator.WithAudience("my-api"),
	validator.WithAllowedClockSkew(30*time.Second),
)

DPoP (Demonstrating Proof-of-Possession)

v3 adds support for DPoP (RFC 9449), which provides proof-of-possession for access tokens. This prevents token theft and replay attacks.

DPoP Modes

Mode Description Use Case
DPoPAllowed (default) Accepts both Bearer and DPoP tokens Migration period, backward compatibility
DPoPRequired Only accepts DPoP tokens Maximum security
DPoPDisabled Ignores DPoP proofs, rejects DPoP scheme Legacy systems

Basic DPoP Setup

go
middleware, err := jwtmiddleware.New(
	jwtmiddleware.WithValidator(jwtValidator),
	jwtmiddleware.WithDPoPMode(jwtmiddleware.DPoPAllowed), // Default
)

Require DPoP for Maximum Security

go
middleware, err := jwtmiddleware.New(
	jwtmiddleware.WithValidator(jwtValidator),
	jwtmiddleware.WithDPoPMode(jwtmiddleware.DPoPRequired),
)

Behind a Proxy

When running behind a reverse proxy, configure trusted proxy headers:

go
middleware, err := jwtmiddleware.New(
	jwtmiddleware.WithValidator(jwtValidator),
	jwtmiddleware.WithDPoPMode(jwtmiddleware.DPoPRequired),
	jwtmiddleware.WithStandardProxy(),  // Trust X-Forwarded-* headers
)

See the DPoP examples for complete working code.

On-Behalf-Of / Token Exchange (RFC 8693)

When an access token is issued through On-Behalf-Of Token Exchange (RFC 8693), for example an MCP server exchanging an incoming user token for one scoped to a downstream API, it carries an act (actor) claim describing who is acting on behalf of the user, along with azp and, for organization-bound tokens, org_id/org_name. These claims are parsed automatically during validation, so there is nothing extra to configure. This SDK validates and inspects such tokens; performing the exchange itself is done by your authorization server, not this middleware.

Per RFC 8693 §4.1, only the current actor (the outermost act.sub, the client that performed the most recent exchange) may be used for access-control decisions. The nested actors form a delegation chain that is informational only and intended for audit and logging. ValidatedClaims exposes helpers that keep this distinction clear:

…

Delegation chains are limited to 5 levels by default; a token whose act claim nests more actors than the limit is rejected with an invalid_claims validation error. You can change the limit with validator.WithMaxActorChainDepth.

Multiple Issuers (Multi-Tenant Support)

Accept JWTs from multiple issuers simultaneously - perfect for multi-tenant SaaS applications, domain migrations, or enterprise deployments.

When to Use What

Choose the right issuer validation approach for your use case:

Approach When to Use Example Use Case
WithIssuer (single) You have one Auth0 tenant or identity provider Simple API with single Auth0 tenant
WithIssuers (static list) You have a fixed set of issuers known at startup - Small number of tenants ( Note for Auth0 users: The WithIssuers / WithIssuersResolver configuration for MCD is intended only for multiple custom domains that belong to the same Auth0 tenant. It is not a supported mechanism for connecting multiple Auth0 tenants to a single API. Each domain in your issuer list should resolve to the same underlying Auth0 tenant.

Dynamic Resolver Warning: If your WithIssuersResolver function uses request-derived values (such as headers, query parameters, or path segments), do not trust those values directly. Use them only to map known and expected request values to a fixed allowlist of issuer domains that you control.

In particular:

  • Request headers like Host, X-Forwarded-Host, or Origin may be influenced by clients, proxies, or load balancers depending on your deployment setup
  • The unverified iss claim from the token has not been signature-verified yet and must not be trusted by itself

If yo

Issues· 0 open

View all issuesOpen on GitHub

No open issues yet, or sync has not completed.

> Tags

Godx-sdk

No comments yet. Be the first to share.

> Details

PublishedAug 1, 2026
UpdatedSep 17, 2026
Category编程语言
PricingOpen source

> Related tools

T
TypeScript
JavaScript 的超集,为前端与全栈提供静态类型
P
Python
通用编程语言,广泛用于 Web、数据与 AI
G
Go
Google 推出的简洁高效系统语言