Skip to main content
By Kunal Dawar
This document will help you troubleshoot your configuration if you get unexpected responses from your API. We recommend that you log in to follow this quickstart with examples configured for your account.

go-jwt-middleware Troubleshooting

If you configured JWT validation correctly, you will be able to get proper responses from your API when you make requests. However, if you get a 401 (Unauthorized) response from your API, it is because the configuration of your JWT middleware does not match the JWT that was passed.

How Does a Token Get Validated?

Token Validation Steps

The middleware validates tokens through these steps:
1

Structure Validation

The token must conform to the JWT structure. Learn more about JWT structure.
2

Signature Verification

The signature verifies that the token was signed by the sender and not altered. The middleware uses a JWKS caching provider to fetch and validate against Auth0’s public keys.
3

Expiration Check

Tokens are only valid for a specified time (expressed in the exp claim). You can configure clock skew via validator.WithAllowedClockSkew().
4

Issuer Validation

Signature Verification: The JWKS provider automatically fetches and caches the correct keysIssuer Value: The iss claim must match exactly (including trailing slash)
5

Audience Validation

The aud claim must match your API identifier exactly.

Inspecting a Token

Use JWT.io to quickly inspect and debug JWTs.
Debugging a JWT on JWT.io
In the screenshot above, the token uses RS256 algorithm. The Issuer is https://jerrie.auth0.com/, and the Audience is https://rs256.test.api.

Configuration Must Match Exactly

Your validator configuration must match these values precisely:
issuerURL, _ := url.Parse("https://jerrie.auth0.com/")

provider, _ := jwks.NewCachingProvider(
    jwks.WithIssuerURL(issuerURL),
    jwks.WithCacheTTL(5*time.Minute),
)

jwtValidator, err := validator.New(
    validator.WithKeyFunc(provider.KeyFunc),
    validator.WithAlgorithm(validator.RS256),
    validator.WithIssuer(issuerURL.String()),              // Must match token's 'iss' claim exactly
    validator.WithAudience("https://rs256.test.api"),     // Must match token's 'aud' claim exactly
    validator.WithAllowedClockSkew(30*time.Second),
)
Important: The audience should NOT have a trailing slash.

Common Troubleshooting Scenarios

1. Invalid Audience Error

Error Message:
{"error": "invalid_token", "description": "aud claim mismatch"}
Cause: The audience in your configuration doesn’t match the aud claim in the token. Solution:
  • Verify AUTH0_AUDIENCE exactly matches your API Identifier in the Auth0 Dashboard
  • Audience should NOT have a trailing slash
  • Check that the token was requested with the correct audience parameter
Code Check:
jwtValidator, err := validator.New(
    validator.WithKeyFunc(provider.KeyFunc),
    validator.WithAlgorithm(validator.RS256),
    validator.WithIssuer(issuerURL.String()),
    validator.WithAudience(cfg.Audience), // Must match API Identifier exactly, no trailing slash
)

2. JWKS Endpoint Unreachable

Error Message:
error fetching keys: connection refused
Cause: The JWKS caching provider cannot reach Auth0’s public key endpoint. Solution:
  • Verify network connectivity to Auth0 (check firewall/proxy settings)
  • Test JWKS endpoint manually:
    curl https://your-domain.auth0.com/.well-known/jwks.json
    
  • Ensure correct Auth0 region (us/eu/au)
  • Check if corporate proxy requires configuration
Code Check:
provider, err := jwks.NewCachingProvider(
    jwks.WithIssuerURL(issuerURL),
    jwks.WithCacheTTL(5*time.Minute),
)
// Ensure issuerURL is correct and accessible

3. Wrong Import Path

Error Message:
cannot find package "github.com/auth0/go-jwt-middleware/v3/..."
Cause: Import statements are incorrect. Solution: Ensure all imports use the correct path:
// Correct
import (
    jwtmiddleware "github.com/auth0/go-jwt-middleware/v3"
    "github.com/auth0/go-jwt-middleware/v3/jwks"
    "github.com/auth0/go-jwt-middleware/v3/validator"
)

4. Claims Extraction Fails

Error Message:
Failed to retrieve claims
Cause: Incorrect generic type used with GetClaims[T](). Solution: Ensure you’re using the correct type parameter:
// Correct - Extract validated claims with custom claims
claims, err := jwtmiddleware.GetClaims[*validator.ValidatedClaims](r.Context())
if err != nil {
    // Handle error
}

// Access custom claims
customClaims, ok := claims.CustomClaims.(*auth.CustomClaims)
if !ok || !customClaims.HasScope("read:messages") {
    // Insufficient permissions
}

5. Algorithm Mismatch

Error Message:
{"error": "invalid_token", "description": "unexpected signing method"}
Cause: Token algorithm doesn’t match validator configuration. Solution:
  • Auth0 uses RS256 by default (asymmetric)
  • Ensure validator specifies validator.RS256
  • Never use validator.HS256 for Auth0 tokens (unless specifically configured)
jwtValidator, err := validator.New(
    validator.WithKeyFunc(provider.KeyFunc),
    validator.WithAlgorithm(validator.RS256), // Must match Auth0's signing algorithm
    validator.WithIssuer(issuerURL.String()),
    validator.WithAudience(cfg.Audience),
)

6. Clock Skew Issues

Error Message:
{"error": "invalid_token", "description": "token is expired"}
Cause: Server clock is out of sync, causing valid tokens to appear expired. Solution: Configure clock skew tolerance:
jwtValidator, err := validator.New(
    validator.WithKeyFunc(provider.KeyFunc),
    validator.WithAlgorithm(validator.RS256),
    validator.WithIssuer(issuerURL.String()),
    validator.WithAudience(cfg.Audience),
    validator.WithAllowedClockSkew(30*time.Second), // Allow 30s tolerance
)

7. Go Version Incompatibility

Error Message:
type parameter requires go1.24 or later (required by go-jwt-middleware v3)
Cause: Go version is too old for v3’s generics usage. Solution:
  • Upgrade to Go 1.24 or later
  • Verify version: go version
  • Update go.mod: go 1.24

Error Responses

The middleware returns RFC 6750 compliant error responses with structured JSON:
{
  "error": "invalid_token",
  "description": "token expired"
}
The WWW-Authenticate header also provides standard error information:
WWW-Authenticate: Bearer error="invalid_token", error_description="token expired"

Debugging Tips

The middleware includes built-in logging support:
import "log/slog"

middleware := jwtmiddleware.New(
    jwtmiddleware.WithValidator(jwtValidator),
    jwtmiddleware.WithLogger(slog.Default()),
)
This will log validation events, making debugging easier.
Add logging to verify your configuration on startup:
log.Printf("Validator configured:")
log.Printf("  Issuer: %s", issuerURL.String())
log.Printf("  Audience: %v", cfg.Audience)
log.Printf("  Algorithm: RS256")
Use the Test tab in your API settings to generate a test token, then verify it works with your API.
Decode your token at JWT.io and verify:
  • iss matches your issuer (with trailing slash)
  • aud contains your API identifier
  • exp is in the future
  • alg is RS256

Getting Help

If you’re still experiencing issues after following this guide: Edit on GitHub