go-jwt-middleware

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Go JWT Middleware Integration

Go JWT中间件集成

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.

使用github.com/auth0/go-jwt-middleware/v3实现JWT访问令牌验证,保护Go HTTP API端点。
Agent操作指引: 在提供SDK安装说明前,需执行以下命令获取最新版本号:
bash
gh api repos/auth0/go-jwt-middleware/releases/latest --jq '.tag_name'
将返回的版本号替换下方所有依赖行中的硬编码版本。

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
  • Go 1.21或更高版本
  • 已配置Auth0 API(注意:必须是API资源,而非普通应用)
  • 若尚未配置Auth0,请先使用
    auth0-quickstart
    技能

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

  • Go服务端渲染Web应用 - 基于会话的Web应用请使用
    go-auth0
  • 单页应用(SPA) - 客户端认证请使用
    auth0-react
    auth0-vue
    auth0-angular
  • 移动应用 - 请使用
    auth0-swift
    auth0-android
    auth0-react-native
  • 非Go后端 - .NET后端请使用
    auth0-aspnetcore-api
    ,Node.js后端请使用
    express-jwt

Quick Start Workflow

快速开始流程

1. Install SDK

1. 安装SDK

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

2. Create Auth0 API

2. 创建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 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
    <domain>
    . Is this the correct tenant?"
    Wait for confirmation. If they say no, ask them to run
    auth0 tenants use <tenant>
    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://<module-name>
    ).
  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
    AUTH0_DOMAIN
    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 (Manual Setup section) for full instructions. Then continue with Step 3 below.
Quick reference for manual API creation:
bash
undefined
你需要在Auth0中创建一个API(而非普通应用)。
Agent操作指引: 如果用户的请求已提供Auth0凭证(domain和audience),请直接使用这些信息——跳过下方的创建方式选择问题,直接进入步骤3编写
.env
文件。
暂停操作——请先询问用户。
请严格按照以下问题询问用户,等待回复后再继续:
"你希望如何创建Auth0 API资源?
  1. 自动创建 — 我将使用Auth0 CLI创建API资源,并自动将准确值写入你的.env文件。
  2. 手动创建 — 你自行在Auth0控制台(或通过
    auth0 apis create
    命令)创建API,并提供Domain和Audience信息。
你选择哪种方式?(1=自动创建 / 2=手动创建)"
在用户回复前,请勿进行任何后续设置步骤。请勿默认选择手动创建。
若用户选择自动创建,请遵循设置指南中的「初始设置」部分(步骤1–6)。自动流程会帮你写入
.env
文件——跳过下方步骤3,直接进入步骤4。
Agent操作指引(自动流程检查点):
执行自动流程时,必须按顺序完成以下检查点,不得跳过:
  1. 检查Auth0 CLI — 确认
    auth0
    已安装。
  2. 检查Auth0登录状态 — 执行
    auth0 tenants list
    命令确认已认证。
  3. 确认当前租户 — 告知用户当前激活的租户,并询问:"你当前激活的Auth0租户是
    <domain>
    。是否为正确的租户?"
    等待确认。若用户表示错误,请让他们在终端执行
    auth0 tenants use <tenant>
    切换租户。
  4. 询问API名称和标识符 — 使用
    AskUserQuestion
    询问:"你希望为Auth0 API命名什么?使用什么标识符(audience)?例如:名称:'My Go API',标识符:'https://my-api.example.com'。标识符是一个逻辑URI,无需实际可访问——仅需唯一标识你的API即可。" 等待回复。若用户不确定,建议从go.mod中的项目模块名派生标识符(例如:
    https://<module-name>
    )。
  5. 询问权限范围 — 使用
    AskUserQuestion
    询问:"你的API需要哪些权限范围(permissions)?例如:
    read:users
    write:users
    read:products
    。若暂时不确定,我可以先配置常见默认值,后续你可再添加。"
    等待回复。
  6. 检查现有API — 执行
    auth0 apis list
    命令,检查是否已存在使用目标标识符的API。若存在,询问用户是复用该API还是使用不同标识符创建新API。
  7. 创建API资源 — 使用步骤4–5获取的名称、标识符和权限范围创建API。
  8. 处理.env文件 — 若已存在
    .env
    文件,修改前需询问用户。切勿读取现有
    .env
    内容(可能包含敏感信息)。若不存在
    .env
    文件,创建并写入
    AUTH0_DOMAIN
    AUTH0_AUDIENCE
  9. 将.env添加到.gitignore — 若尚未添加。
  10. 进入代码集成环节 — 跳过步骤3(已完成),直接进入步骤4编写中间件代码。
若用户选择手动创建,请遵循设置指南中的「手动设置」部分获取完整说明。然后继续下方步骤3。
手动创建API的快速参考命令:
bash
undefined

Using Auth0 CLI

使用Auth0 CLI

auth0 apis create
--name "My Go API"
--identifier https://my-api.example.com

Or create manually in Auth0 Dashboard → Applications → APIs
auth0 apis create
--name "My Go API"
--identifier https://my-api.example.com

或在Auth0控制台手动创建:应用 → APIs

3. Configure .env

3. 配置.env

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.
env
AUTH0_DOMAIN=your-tenant.auth0.com
AUTH0_AUDIENCE=https://my-api.example.com
重要提示: Domain不得包含
https://
。中间件会自动构造issuer URL。

4. Configure main.go

4. 配置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()
.
go
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))
}
Agent操作指引(集成到现有代码):
编写代码前,需确定以下场景:
  • A) 为现有项目添加认证 — 用户已有定义好路由的
    main.go
    文件。此时请勿用下方模板替换用户文件,而是:
    1. 添加必要的导入包(
      jwtmiddleware
      jwks
      validator
      godotenv
      net/url
      os
      context
      strings
      )。
    2. 添加
      CustomClaims
      结构体及方法。
    3. main()
      函数开头附近添加中间件设置代码(issuer URL、JWKS提供器、验证器、中间件)。
    4. 询问用户需要保护哪些端点(见下方说明)。
    5. 使用
      middleware.CheckJWT()
      包裹指定的处理器。
  • B) 从零创建新项目 — 使用下方完整模板作为起点。
暂停操作——询问需要保护的端点:
如果用户的请求未明确指定需要保护的端点,请询问:
"哪些端点需要认证?例如:
  • 除健康检查/公开接口外全部保护 — 保护所有端点,仅开放特定公开路由
  • 特定路由 — 告知我哪些路由需要认证
另外,是否有端点需要特定的权限范围/权限检查(例如:POST/DELETE请求需要
write:users
),还是所有受保护端点仅需有效的JWT即可?"
等待用户回复。若用户表示“全部”或“除健康检查外全部”,则保护所有路由,仅开放
/health
(或用户指定的其他公开路由)。若用户指定了每个端点的权限范围要求,请使用
customClaims.HasScope()
实现路由级别的权限检查。
go
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 包含我们需要从令牌中获取的自定义数据。
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("加载.env文件出错: %v", err)
	}

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

	provider, err := jwks.NewCachingProvider(
		jwks.WithIssuerURL(issuerURL),
	)
	if err != nil {
		log.Fatalf("设置JWKS提供器失败: %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("设置JWT验证器失败: %v", err)
	}

	middleware, err := jwtmiddleware.New(
		jwtmiddleware.WithValidator(jwtValidator),
	)
	if err != nil {
		log.Fatalf("设置JWT中间件失败: %v", err)
	}

	mux := http.NewServeMux()

	// 公开端点 - 无需认证
	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": "来自公开端点的问候!"})
	})

	// 受保护端点 - 需要有效的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":"获取令牌声明失败。"}`, http.StatusInternalServerError)
			return
		}
		w.Header().Set("Content-Type", "application/json")
		json.NewEncoder(w).Encode(map[string]string{
			"message": "来自私有端点的问候!",
			"userId":  claims.RegisteredClaims.Subject,
		})
	})))

	// 受保护+权限范围限制端点 - 需要包含特定权限范围的JWT
	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":"获取令牌声明失败。"}`, 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": "权限范围不足。"})
			return
		}
		w.Header().Set("Content-Type", "application/json")
		json.NewEncoder(w).Encode(map[string]string{"message": "来自权限范围限制端点的问候!"})
	})))

	log.Println("服务器监听端口 :8080")
	log.Fatal(http.ListenAndServe(":8080", mux))
}

5. Protect Endpoints

5. 保护端点

Use
middleware.CheckJWT()
to wrap handlers that require authentication:
go
// 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)))
使用
middleware.CheckJWT()
包裹需要认证的处理器:
go
// 公开端点 - 无需认证
mux.HandleFunc("/api/public", publicHandler)

// 受保护端点 - 需要有效的JWT
mux.Handle("/api/private", middleware.CheckJWT(http.HandlerFunc(privateHandler)))

// 受保护+权限范围限制 - 需要包含特定权限的JWT
mux.Handle("/api/private-scoped", middleware.CheckJWT(http.HandlerFunc(privateScopedHandler)))

6. Test API

6. 测试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 "<PROJECT_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": "<CLIENT_ID>",
  "audience": "<API_IDENTIFIER>",
  "scope": ["<SCOPES>"]
}'
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": "<USER_PROVIDED_CLIENT_ID>",
  "audience": "<API_IDENTIFIER>",
  "scope": ["<SCOPES>"]
}'
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 <CLIENT_ID> --audience <AUDIENCE> --scopes <SCOPE1,SCOPE2> 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/<ENDPOINT> -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 (${#TEST_TOKEN} chars)"
    confirms success by printing LENGTH only, never the value
  • The shell variable
    $TEST_TOKEN
    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 <CLIENT_ID> --audience <AUDIENCE> --scopes <SCOPE1,SCOPE2> 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 $TEST_TOKEN
    ,
    printf $TEST_TOKEN
    , or any command that would print the raw token value
  5. If the token acquisition fails (empty variable), the
    [ -n "$TEST_TOKEN" ]
    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
    client_id
    obtained from the M2M app setup step (create new or use existing). If the M2M step has not been completed yet (no Client ID available), do NOT attempt to run the test token command. Instead, ask the user: "I need an M2M application Client ID to get a test token. Would you like me to create one or do you have an existing one?" — then complete the M2M setup first.
If the user does NOT ask to test, just provide the commands for them to run manually:
auth0 test token <CLIENT_ID> --audience <AUDIENCE> --scopes <SCOPE1,SCOPE2>
curl http://localhost:8080/<endpoint> -H "Authorization: Bearer <PASTE_TOKEN_HERE>"
After M2M setup is complete:
  1. Start the server with
    go run .
    in the background
  2. Verify public endpoints return 200 and protected endpoints return 401 (no token needed)
  3. If the user asked to test: use the secure single-command chain above for authenticated requests
  4. If the user did NOT ask to test: provide the manual commands and tell them to run in their terminal
Test public endpoint:
bash
curl http://localhost:8080/api/public
Test protected endpoint without token (should return 401):
bash
curl http://localhost:8080/api/private
Test protected endpoint with token (secure single-command chain):
bash
TEST_TOKEN=$(auth0 test token <M2M_CLIENT_ID> --audience https://my-api.example.com --scopes <SCOPE1,SCOPE2> 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/api/private -H "Authorization: Bearer $TEST_TOKEN"

Agent操作指引: 编写代码后,需验证编译是否通过:
bash
go build ./...
若编译失败,请诊断错误并修复。最多尝试5-6次。
失败处理: 若经过5-6次修复尝试后仍编译失败,请停止操作并使用
AskUserQuestion
询问用户: "经过多次修复尝试后,编译仍失败。你希望如何处理?"
  • 继续迭代修复
  • 手动修复 — 我将展示剩余错误
  • 跳过编译验证 — 直接继续后续步骤
若错误持续存在,每尝试5-6次后重复此检查。
Agent操作指引: 编译成功后暂停——询问用户。
编译成功后,请询问用户:
"你的API已配置完成且编译成功。是否需要我协助设置测试环节?
  1. — 我将帮你配置M2M应用以获取测试令牌。
  2. — 目前操作已完成。
你选择哪种方式?"
除非用户选择“是”,否则请勿进行测试设置。若用户选择“否”,请总结已完成的操作并停止。
Agent操作指引(M2M应用设置——仅当用户需要测试时执行):
若用户选择设置测试,请询问:
"要测试受保护的端点,你需要一个已授权的机器对机器(M2M)应用来请求该API的令牌。
  1. 创建新应用 — 我将创建一个新的M2M应用并授权其访问该API。
  2. 使用现有应用 — 你已有M2M应用,请提供Client ID,我将授权其访问该API。
你选择哪种方式?(1=创建新应用 / 2=使用现有应用)"
在用户回复前,请勿继续操作。请勿从租户中静默选择现有应用。
若用户选择“创建新应用”:
bash
auth0 apps create \
  --name "<PROJECT_NAME> (Test App)" \
  --type m2m \
  --no-input --json
解析JSON提取
client_id
。请勿使用
--reveal-secrets
参数——切勿在Agent上下文暴露客户端密钥。 然后创建客户端授权:
bash
auth0 api post "client-grants" --data '{
  "client_id": "<CLIENT_ID>",
  "audience": "<API_IDENTIFIER>",
  "scope": ["<SCOPES>"]
}'
若用户选择“使用现有应用”: 询问用户获取Client ID。然后创建客户端授权以允许其访问该API:
bash
auth0 api post "client-grants" --data '{
  "client_id": "<USER_PROVIDED_CLIENT_ID>",
  "audience": "<API_IDENTIFIER>",
  "scope": ["<SCOPES>"]
}'
若授权已存在(返回409冲突),无需处理——应用已授权。
Agent操作指引(令牌隔离——至关重要):
Agent绝对不能直接查看或显示访问令牌值。令牌安全规则:
  • 请勿单独执行
    auth0 test token
    命令——该命令会将令牌输出到标准输出
  • 请勿单独执行
    curl
    命令调用
    /oauth/token
    端点
  • 请勿要求用户将令牌粘贴到对话中
  • 请勿回显、打印或记录令牌值
  • 请勿将令牌存储到文件中
安全测试方式(单命令链):
若用户明确要求测试受保护端点,Agent可使用单命令链将令牌捕获到shell变量中并立即使用——令牌值永远不会被打印或被Agent看到:
bash
TEST_TOKEN=$(auth0 test token <CLIENT_ID> --audience <AUDIENCE> --scopes <SCOPE1,SCOPE2> 2>/dev/null | grep -o 'ey[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*') && \
[ -n "$TEST_TOKEN" ] && echo "令牌已获取(${#TEST_TOKEN} 字符)" && \
curl -s http://localhost:8080/<ENDPOINT> -H "Authorization: Bearer $TEST_TOKEN"
该方式的安全保障:
  • $(...)
    捕获标准输出——令牌被存入变量,不会显示
  • grep -o
    仅提取JWT格式内容(ey...)——无多余输出泄露
  • echo "令牌已获取(${#TEST_TOKEN} 字符)"
    仅通过打印长度确认成功,不会显示令牌值
  • shell变量
    $TEST_TOKEN
    仅在该单命令链执行期间存在——命令执行后立即销毁
  • Agent仅能看到:
    "令牌已获取(834 字符)"
    + API响应体(JSON)
  • 不会写入文件、导出环境变量,无任何持久化数据
使用该模式的规则:
  1. 仅当用户明确要求测试时使用(例如:“测试一下”、“运行测试”、“验证端点是否可用”)
  2. 必须将令牌获取+curl命令合并为单个
    &&
    命令链——不得拆分为两个Bash调用
  3. 若要测试多个端点,可在同一命令链中添加多个curl命令:
    bash
    TEST_TOKEN=$(auth0 test token <CLIENT_ID> --audience <AUDIENCE> --scopes <SCOPE1,SCOPE2> 2>/dev/null | grep -o 'ey[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*') && \
    [ -n "$TEST_TOKEN" ] && echo "令牌已获取(${#TEST_TOKEN} 字符)" && \
    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. 绝对不能添加
    echo $TEST_TOKEN
    printf $TEST_TOKEN
    或任何会打印原始令牌值的命令
  5. 若令牌获取失败(变量为空),
    [ -n "$TEST_TOKEN" ]
    检查会终止命令链——告知用户M2M应用可能未授权
  6. 必须提供Client ID
    auth0 test token
    命令要求第一个参数为Client ID。该参数必须是从M2M应用设置步骤(创建新应用或使用现有应用)获取的
    client_id
    。若尚未完成M2M设置(无Client ID可用),请勿尝试执行测试令牌命令。请询问用户:"获取测试令牌需要M2M应用的Client ID。你希望我创建一个还是已有现成的?" — 先完成M2M设置。
若用户未要求测试,仅需提供手动执行的命令:
auth0 test token <CLIENT_ID> --audience <AUDIENCE> --scopes <SCOPE1,SCOPE2>
curl http://localhost:8080/<endpoint> -H "Authorization: Bearer <粘贴令牌到此处>"
完成M2M应用设置后:
  1. 在后台执行
    go run .
    启动服务器
  2. 验证公开端点返回200,未携带令牌时受保护端点返回401
  3. 若用户要求测试:使用上述安全单命令链执行认证请求
  4. 若用户未要求测试:提供手动命令并告知用户在终端执行
测试公开端点:
bash
curl http://localhost:8080/api/public
未携带令牌测试受保护端点(应返回401):
bash
curl http://localhost:8080/api/private
携带令牌测试受保护端点(安全单命令链):
bash
TEST_TOKEN=$(auth0 test token <M2M_CLIENT_ID> --audience https://my-api.example.com --scopes <SCOPE1,SCOPE2> 2>/dev/null | grep -o 'ey[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*') && \
[ -n "$TEST_TOKEN" ] && echo "令牌已获取(${#TEST_TOKEN} 字符)" && \
curl -s http://localhost:8080/api/private -H "Authorization: Bearer $TEST_TOKEN"

Common Mistakes

常见错误

MistakeFix
Created Application instead of API in Auth0Must create API resource in Auth0 Dashboard → Applications → APIs
Audience doesn't match API IdentifierMust exactly match the API Identifier set in Auth0 Dashboard
Domain includes
https://
Use
your-tenant.auth0.com
format only - the issuer URL is constructed automatically
Using v2 positional parameters instead of v3 optionsv3 uses
validator.WithKeyFunc()
,
validator.WithAlgorithm()
etc.
Missing trailing slash on issuer URLIssuer must be
https://domain/
with trailing slash
Checking
scope
claim instead of
permissions
for RBAC
Use custom claims struct with
Permissions []string
field
Missing
godotenv.Load()
call
Add
github.com/joho/godotenv
and call
godotenv.Load()
before reading env vars
Using
ContextKey{}
to access claims (v2 pattern)
Use
jwtmiddleware.GetClaims[T]()
type-safe generics instead

错误修复方案
在Auth0中创建了普通应用而非API必须在Auth0控制台 → 应用 → APIs中创建API资源
Audience与API标识符不匹配必须与Auth0控制台中设置的API标识符完全一致
Domain包含
https://
仅使用
your-tenant.auth0.com
格式——issuer URL会自动构造
使用v2的位置参数而非v3的选项参数v3使用
validator.WithKeyFunc()
validator.WithAlgorithm()
等选项
issuer URL缺少末尾斜杠issuer必须为
https://domain/
格式,包含末尾斜杠
针对RBAC检查
scope
声明而非
permissions
使用包含
Permissions []string
字段的自定义声明结构体
缺少
godotenv.Load()
调用
添加
github.com/joho/godotenv
包,并在读取环境变量前调用
godotenv.Load()
使用
ContextKey{}
访问声明(v2模式)
使用类型安全的泛型
jwtmiddleware.GetClaims[T]()
替代

Scope-Based Authorization

基于权限范围的授权

See Integration Guide for defining and enforcing scope and permission policies.

请查看集成指南了解权限范围和权限策略的定义与实施。

CORS Configuration

CORS配置

For APIs called from browser-based SPAs, configure CORS before any auth middleware:
go
func corsMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Access-Control-Allow-Origin", "http://localhost:3000")
		w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
		w.Header().Set("Access-Control-Allow-Headers", "Authorization, Content-Type")
		if r.Method == "OPTIONS" {
			w.WriteHeader(http.StatusNoContent)
			return
		}
		next.ServeHTTP(w, r)
	})
}
Apply it as the outermost handler wrapping your mux:
go
handler := corsMiddleware(mux)
log.Fatal(http.ListenAndServe(":8080", handler))
See Integration Guide for detailed CORS patterns.

若API被基于浏览器的SPA调用,请在任何认证中间件之前配置CORS:
go
func corsMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Access-Control-Allow-Origin", "http://localhost:3000")
		w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
		w.Header().Set("Access-Control-Allow-Headers", "Authorization, Content-Type")
		if r.Method == "OPTIONS" {
			w.WriteHeader(http.StatusNoContent)
			return
		}
		next.ServeHTTP(w, r)
	})
}
将其作为最外层处理器包裹你的mux:
go
handler := corsMiddleware(mux)
log.Fatal(http.ListenAndServe(":8080", handler))
请查看集成指南了解详细的CORS模式。

DPoP Support

DPoP支持

Built-in proof-of-possession token binding per RFC 9449. See Integration Guide for configuration.

内置符合RFC 9449的令牌持有证明绑定。请查看集成指南了解配置方法。

Related Skills

相关技能

  • auth0-quickstart
    - Basic Auth0 setup
  • auth0-mfa
    - Add Multi-Factor Authentication

  • auth0-quickstart
    - Auth0基础设置
  • auth0-mfa
    - 添加多因素认证

Quick Reference

快速参考

Configuration Options:
  • validator.WithKeyFunc(provider.KeyFunc)
    - JWKS key function for signature verification (required)
  • validator.WithAlgorithm(validator.RS256)
    - Expected signing algorithm (required)
  • validator.WithIssuer(url)
    - Token issuer URL with trailing slash (required)
  • validator.WithAudience(aud)
    - API Identifier from Auth0 API settings (required)
  • validator.WithCustomClaims(fn)
    - Factory for custom claims struct
  • validator.WithAllowedClockSkew(d)
    - Clock skew tolerance
Claims Access:
  • jwtmiddleware.GetClaims[*validator.ValidatedClaims](r.Context())
    - Type-safe claims retrieval
  • claims.RegisteredClaims.Subject
    - User ID (sub)
  • claims.CustomClaims.(*CustomClaims).Scope
    - Space-separated scopes
  • claims.CustomClaims.(*CustomClaims).Permissions
    - Permission strings
Common Use Cases:
  • Protect routes →
    middleware.CheckJWT(handler)
    (see Step 5)
  • Permission enforcement → Integration Guide
  • DPoP token binding → Integration Guide
  • Framework adapters (Gin, Echo) → Integration Guide
  • Advanced JWT config → API Reference

配置选项:
  • validator.WithKeyFunc(provider.KeyFunc)
    - 用于签名验证的JWKS密钥函数(必填)
  • validator.WithAlgorithm(validator.RS256)
    - 预期的签名算法(必填)
  • validator.WithIssuer(url)
    - 包含末尾斜杠的令牌issuer URL(必填)
  • validator.WithAudience(aud)
    - Auth0 API设置中的API标识符(必填)
  • validator.WithCustomClaims(fn)
    - 自定义声明结构体的工厂函数
  • validator.WithAllowedClockSkew(d)
    - 时钟偏差容忍度
声明访问:
  • jwtmiddleware.GetClaims[*validator.ValidatedClaims](r.Context())
    - 类型安全的声明获取方式
  • claims.RegisteredClaims.Subject
    - 用户ID(sub)
  • claims.CustomClaims.(*CustomClaims).Scope
    - 空格分隔的权限范围
  • claims.CustomClaims.(*CustomClaims).Permissions
    - 权限字符串
常见用例:
  • 保护路由 →
    middleware.CheckJWT(handler)
    (见步骤5)
  • 权限实施 → 集成指南
  • DPoP令牌绑定 → 集成指南
  • 框架适配器(Gin、Echo) → 集成指南
  • 高级JWT配置 → API参考

Detailed Documentation

详细文档

  • Setup Guide - Auth0 CLI setup, environment configuration
  • Integration Guide - Scope policies, DPoP, framework adapters, error handling
  • API Reference - Complete configuration options and validator/middleware reference

  • 设置指南 - Auth0 CLI设置、环境配置
  • 集成指南 - 权限范围策略、DPoP、框架适配器、错误处理
  • API参考 - 完整配置选项及验证器/中间件参考

References

参考链接