By Kunal Dawar
This tutorial demonstrates how to add authorization to a Go API using go-jwt-middleware v3. Learn how to validate JWTs, protect endpoints, and enforce permission-based access control.View Sample on GitHub
Complete working example with tests • Go 1.24+ required
New to Auth0? Learn how Auth0 works and read about implementing API authentication and authorization using the OAuth 2.0 framework.
What You’ll Build
Public Endpoints
Routes accessible without authentication
Protected Endpoints
JWT validation for authenticated users
Permission-Based Access
Scope validation for fine-grained control
Production-Ready
Graceful shutdown, timeouts, and error handling
Prerequisites
Before starting, ensure you have:Go 1.24 or later
Go 1.24 or later
go-jwt-middleware v3 requires Go 1.24+ and uses generics for type-safe claims. Check your version:Install or update at go.dev/doc/install
Auth0 Account
Auth0 Account
Sign up for free if you don’t have an account.
Basic Go Knowledge
Basic Go Knowledge
Familiarity with Go syntax, HTTP servers, and package management.
Step 1: Configure Auth0 API
Create an API
In the APIs section of the Auth0 dashboard, click Create API.

Configure API settings
- Name:
Quickstarts(or any descriptive name) - Identifier:
https://quickstarts/api(this becomes youraudience) - Signing Algorithm: Leave as RS256 (recommended)
Understand RS256
By default, your API uses RS256 (asymmetric algorithm) for signing tokens:
- Auth0 signs tokens with a private key
- Your API verifies tokens with a public key (from JWKS)
- Public keys are automatically fetched from:
https://{yourDomain}/.well-known/jwks.json
Step 2: Define API Permissions
Permissions (scopes) let you define how resources can be accessed. For example, grantread access to managers and write access to administrators.
What this tutorial covers
What this tutorial covers
This example demonstrates:
- ✅ Extracting JWTs from the
Authorization: Bearer <token>header - ✅ Validating tokens using Auth0’s JWKS
- ✅ Checking token expiration and claims
- ✅ Enforcing permission-based access with scopes
- ✅ Type-safe claims extraction using Go generics
Step 3: Install Dependencies
Step 4: Configure Environment Variables
Create a.env file in your project root to store Auth0 configuration:
Step 5: Create Configuration Package
Createinternal/config/auth.go to load and validate environment variables:
Why use a config package?
Why use a config package?
Benefits of centralized configuration:
- Single source of truth for environment variables
- Clear error messages for missing configuration
- Type-safe configuration access
- Easy to test and mock in unit tests
- Fail fast on startup if config is invalid
Production best practices
Production best practices
Configuration management:
- Validate all required config on startup (fail fast)
- Use different
.envfiles for different environments - Consider using config validation libraries for complex setups
- Document all environment variables in a
.env.examplefile
Step 6: Create Custom Claims
Custom claims allow you to extract and validate application-specific data from JWTs. Createinternal/auth/claims.go:
Understanding Custom Claims Validation
Understanding Custom Claims Validation
Automatic Validation: The
Validate method is called automatically by the middleware after parsing the JWT. You don’t need to call it manually - it’s part of the validation chain.Scope is Optional: The validation allows empty scopes because not all endpoints require permissions. This works for:/api/private- Requires only authentication (no scope needed)/api/private-scoped- Requires authentication +read:messagesscope
- No leading/trailing whitespace
- No double spaces (ensures clean parsing)
- Properly formatted space-separated values
false if scope is empty, preventing false positives.Validation Flow:- JWT is extracted from the
Authorizationheader - Claims are unmarshaled into
CustomClaimsstruct Validate(ctx)is automatically called- If validation fails, JWT is rejected before reaching your handlers
Step 7: Create JWT Validator
The validator is the core component that verifies tokens. Createinternal/auth/validator.go:
What does the validator check?
What does the validator check?
The validator performs these security checks on every JWT:
- Signature verification - Using Auth0’s public keys from JWKS
- Issuer validation -
issclaim matches your Auth0 domain - Audience validation -
audclaim matches your API identifier - Expiration check - Token hasn’t expired (
expclaim) - Time validity - Token is currently valid (
nbfandiatclaims)
Key features explained
Key features explained
JWKS Caching: Automatically fetches and caches Auth0’s public keys every 5 minutes, reducing network calls.Algorithm Specification: Explicitly sets RS256 to prevent algorithm confusion attacks.Clock Skew Tolerance: Allows 30 seconds for distributed system clock differences.Options Pattern: V3 uses functional options (
WithIssuerURL(), WithKeyFunc(), WithAlgorithm(), etc.) for flexible, type-safe configuration.Custom Claims: The CustomClaims struct lets you extract custom data from JWTs, like permission scopes.Step 8: Create HTTP Middleware
Createinternal/auth/middleware.go to wrap your validator:
- Extracts JWT from
Authorization: Bearer <token>header - Validates token using the core validator
- Skips validation for OPTIONS requests (CORS preflight)
- Injects validated claims into request context
- Returns custom error responses on validation failure with structured logging
Step 9: Create API Handlers
Createinternal/handlers/api.go with three handlers demonstrating different protection levels:
Step 10: Create Main Server
Createcmd/server/main.go to wire everything together:
Project Structure
Project Structure
Step 11: Test Your API
Get a test token
Navigate to your API in the Auth0 Dashboard, click the Test tab, and copy the access token.
Continue Learning
Using Your API
Learn how to call your protected API from different client applications
Troubleshooting Guide
Comprehensive debugging tips and solutions to common issues
Quick Troubleshooting
Invalid Audience Error
Invalid Audience Error
Error:
{"error": "invalid_token", "description": "aud claim mismatch"}Solution: Verify AUTH0_AUDIENCE matches your API Identifier exactly from the Auth0 Dashboard.JWKS Endpoint Unreachable
JWKS Endpoint Unreachable
Error:
error fetching keys: connection refusedSolution:- Check network connectivity to Auth0
- Test JWKS endpoint:
curl https://your-tenant.us.auth0.com/.well-known/jwks.json - Verify firewall/proxy settings
Token Expired
Token Expired
Error:
{"error": "invalid_token", "description": "token is expired"}Solution: Get a new token from the Auth0 Dashboard Test tab.Next Steps
Custom Claims
Add domain-specific authorization with type-safe claims
Token Refresh
Implement refresh tokens for long-lived sessions
DPoP Security
Enable proof-of-possession for enhanced security
Production Checklist
Pre-launch security review and best practices
Edit on GitHub
