Use this file to discover all available pages before exploring further.
Use AI to integrate Auth0
If you use an AI coding assistant like Claude Code, Cursor, or GitHub Copilot, you can add Auth0 API authentication automatically in minutes using agent skills.Install:
Your AI assistant will automatically create your Auth0 API, fetch credentials, install go-jwt-middleware, configure the validator, and protect your API endpoints with JWT validation. Full agent skills documentation →
Prerequisites: Before you begin, ensure you have the following installed:
Go 1.24 or newer (required for generics support in go-jwt-middleware v3)
You’ll build a Go API with three endpoints demonstrating different protection levels: public access, JWT-authenticated, and permission-scoped. The complete implementation uses go-jwt-middleware v3 with Go’s standard net/http library.
View Sample on GitHub
Complete working example with tests
1
Create a new project
Create a new directory for your Go API and initialize a module.
mkdir myapi && cd myapigo mod init github.com/yourorg/myapi
Install the required dependencies:
go get github.com/auth0/go-jwt-middleware/v3go get github.com/joho/godotenvgo mod download
Next, you need to create a new API on your Auth0 tenant and add the environment variables to your project.You have two options to set up your Auth0 API: use a CLI command or configure manually via the Dashboard:
CLI
Dashboard
Run the following command in your project’s root directory to create an Auth0 API:
# Install Auth0 CLI (if not already installed)brew tap auth0/auth0-cli && brew install auth0# Create Auth0 APIauth0 apis create \ --name "My Go API" \ --identifier https://my-go-api.example.com
After creation, copy the Identifier and your Domain values, then create your .env file:
This command will:
Check if you’re authenticated (and prompt for login if needed)
Create an Auth0 API with the specified identifier
Display the API details including the domain and identifier
Replace YOUR_AUTH0_DOMAIN with your Auth0 tenant domain (e.g., dev-abc123.us.auth0.com) and YOUR_API_IDENTIFIER with your API identifier from the dashboard (e.g., https://my-go-api.example.com).
Security: Never commit .env files to version control. Add .env to your .gitignore file.
3
Define API permissions
Permissions (scopes) let you define how resources can be accessed. For example, grant read access to managers and write access to administrators.
In your API settings, click the Permissions tab
Create the following permission:
Permission
Description
read:messages
Read messages from the API
This tutorial uses the read:messages scope to protect the scoped endpoint. You can define additional permissions based on your application’s needs.
4
Create configuration loader
Create a config package to load and validate environment variables.
Loads Auth0 domain and audience from environment variables
Validates that required configuration is present at startup
Returns a type-safe config struct for use across the application
5
Create custom claims and JWT validator
Custom claims allow you to extract and validate application-specific data from JWTs. The validator is the core component that verifies tokens against Auth0.
claims.go
validator.go
internal/auth/claims.go
package authimport ( "context" "fmt" "strings")// CustomClaims contains custom data we want to parse from the JWT.type CustomClaims struct { Scope string `json:"scope"`}// Validate ensures the custom claims are properly formatted.func (c *CustomClaims) Validate(ctx context.Context) error { if c.Scope == "" { return nil } if strings.TrimSpace(c.Scope) != c.Scope { return fmt.Errorf("scope claim has invalid whitespace") } if strings.Contains(c.Scope, " ") { return fmt.Errorf("scope claim contains double spaces") } return nil}// HasScope checks whether our claims have a specific scope.func (c *CustomClaims) HasScope(expectedScope string) bool { if c.Scope == "" { return false } scopes := strings.Split(c.Scope, " ") for _, scope := range scopes { if scope == expectedScope { return true } } return false}
package handlersimport ( "encoding/json" "net/http" "github.com/yourorg/myapi/internal/auth" jwtmiddleware "github.com/auth0/go-jwt-middleware/v3" "github.com/auth0/go-jwt-middleware/v3/validator")// PublicHandler - no authentication requiredfunc PublicHandler(w http.ResponseWriter, r *http.Request) { response := map[string]string{ "message": "Hello from a public endpoint! You don't need to be authenticated to see this.", } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(response)}// PrivateHandler - requires valid JWTfunc PrivateHandler(w http.ResponseWriter, r *http.Request) { response := map[string]string{ "message": "Hello from a private endpoint! You need to be authenticated to see this.", } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(response)}// ScopedHandler - requires 'read:messages' permissionfunc ScopedHandler(w http.ResponseWriter, r *http.Request) { claims, err := jwtmiddleware.GetClaims[*validator.ValidatedClaims](r.Context()) if err != nil { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusUnauthorized) w.Write([]byte(`{"message":"Unauthorized."}`)) return } customClaims, ok := claims.CustomClaims.(*auth.CustomClaims) if !ok || !customClaims.HasScope("read:messages") { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusForbidden) w.Write([]byte(`{"message":"Insufficient scope."}`)) return } response := map[string]string{ "message": "Hello from a private endpoint! You need to be authenticated and have a scope of read:messages to see this.", } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(response)}
If you are calling the API from a Single-Page Application or a Mobile/Native application, after the authorization flow is completed, you will get an access token. How you get the token and how you make the call to the API will depend on the type of application you are developing and the framework you are using.
Single-Page Applications
React, Vue, Angular quickstarts with examples
Mobile / Native Applications
iOS, Android, React Native quickstarts
If you are calling the API from a command-line tool or another service where there isn’t a user entering their credentials, you need to use the OAuth Client Credentials flow.
Token Reuse: Auth0 customers are billed based on the number of Machine to Machine access tokens issued. Once your application gets an access token, it should keep using it until it expires to minimize the number of tokens requested.
Problem: The token’s audience doesn’t match your API.Solution: Verify AUTH0_AUDIENCE exactly matches your API Identifier from the Auth0 Dashboard. The audience should NOT have a trailing slash:
# CorrectAUTH0_AUDIENCE=https://my-go-api.example.com# Wrong (no trailing slash)AUTH0_AUDIENCE=https://my-go-api.example.com/
The client application must also request a token with the correct audience parameter.
Problem: Server clock is out of sync, causing valid tokens to appear expired.Solution: The validator already includes 30s clock skew tolerance. If you need more, adjust: