go-jwt-middleware
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseGo 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:bashgh 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安装说明前,需执行以下命令获取最新版本号:bashgh 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 skill first
auth0-quickstart
- Go 1.21或更高版本
- 已配置Auth0 API(注意:必须是API资源,而非普通应用)
- 若尚未配置Auth0,请先使用技能
auth0-quickstart
When NOT to Use
不适用场景
- Go server-rendered web applications - Use for session-based web apps
go-auth0 - Single Page Applications - Use ,
auth0-react, orauth0-vuefor client-side authauth0-angular - Mobile applications - Use ,
auth0-swift, orauth0-androidauth0-react-native - Non-Go backends - Use for .NET,
auth0-aspnetcore-apifor Node.jsexpress-jwt
- Go服务端渲染Web应用 - 基于会话的Web应用请使用
go-auth0 - 单页应用(SPA) - 客户端认证请使用、
auth0-react或auth0-vueauth0-angular - 移动应用 - 请使用、
auth0-swift或auth0-androidauth0-react-native - 非Go后端 - .NET后端请使用,Node.js后端请使用
auth0-aspnetcore-apiexpress-jwt
Quick Start Workflow
快速开始流程
1. Install SDK
1. 安装SDK
bash
go get github.com/auth0/go-jwt-middleware/v3
go get github.com/joho/godotenvbash
go get github.com/auth0/go-jwt-middleware/v3
go get github.com/joho/godotenv2. 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 thefile..envSTOP — 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?
- Automated — I'll use the Auth0 CLI to create the API resource and write the exact values to your .env file automatically.
- Manual — You create the API yourself in the Auth0 Dashboard (or via
) and provide me the Domain and Audience.auth0 apis createWhich 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 for you — skip Step 3 below and proceed directly to Step 4.
.envAgent instruction (Automated path checkpoints):When following the automated path, you MUST complete these checkpoints in order. Do NOT skip any:
- Check Auth0 CLI — verify
is installed.auth0- Check Auth0 login — run
to verify authentication.auth0 tenants list- Confirm active tenant — show the user which tenant is active and ask: "Your active Auth0 tenant is
. Is this the correct tenant?" Wait for confirmation. If they say no, ask them to run<domain>in their terminal.auth0 tenants use <tenant>- Ask about API name and identifier — use
: "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.,AskUserQuestion).https://<module-name>- Ask about scopes — use
: "What scopes (permissions) does your API need? For example:AskUserQuestion,read:users,write:users. If you're not sure yet, I can start with common defaults and you can add more later." Wait for answer.read:products- Check for existing API — run
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.auth0 apis list- Create the API resource — using the name, identifier, and scopes from steps 4–5.
- Handle .env — if a
file already exists, ask before modifying it. Never read existing.envcontents (may contain secrets). If no.envexists, write one with.envandAUTH0_DOMAIN.AUTH0_AUDIENCE- Add
to.env— if not already present..gitignore- 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资源?
- 自动创建 — 我将使用Auth0 CLI创建API资源,并自动将准确值写入你的.env文件。
- 手动创建 — 你自行在Auth0控制台(或通过
命令)创建API,并提供Domain和Audience信息。auth0 apis create你选择哪种方式?(1=自动创建 / 2=手动创建)"在用户回复前,请勿进行任何后续设置步骤。请勿默认选择手动创建。
若用户选择自动创建,请遵循设置指南中的「初始设置」部分(步骤1–6)。自动流程会帮你写入文件——跳过下方步骤3,直接进入步骤4。
.envAgent操作指引(自动流程检查点):执行自动流程时,必须按顺序完成以下检查点,不得跳过:
- 检查Auth0 CLI — 确认
已安装。auth0- 检查Auth0登录状态 — 执行
命令确认已认证。auth0 tenants list- 确认当前租户 — 告知用户当前激活的租户,并询问:"你当前激活的Auth0租户是
。是否为正确的租户?" 等待确认。若用户表示错误,请让他们在终端执行<domain>切换租户。auth0 tenants use <tenant>- 询问API名称和标识符 — 使用
询问:"你希望为Auth0 API命名什么?使用什么标识符(audience)?例如:名称:'My Go API',标识符:'https://my-api.example.com'。标识符是一个逻辑URI,无需实际可访问——仅需唯一标识你的API即可。" 等待回复。若用户不确定,建议从go.mod中的项目模块名派生标识符(例如:AskUserQuestion)。https://<module-name>- 询问权限范围 — 使用
询问:"你的API需要哪些权限范围(permissions)?例如:AskUserQuestion、read:users、write:users。若暂时不确定,我可以先配置常见默认值,后续你可再添加。" 等待回复。read:products- 检查现有API — 执行
命令,检查是否已存在使用目标标识符的API。若存在,询问用户是复用该API还是使用不同标识符创建新API。auth0 apis list- 创建API资源 — 使用步骤4–5获取的名称、标识符和权限范围创建API。
- 处理.env文件 — 若已存在
文件,修改前需询问用户。切勿读取现有.env内容(可能包含敏感信息)。若不存在.env文件,创建并写入.env和AUTH0_DOMAIN。AUTH0_AUDIENCE- 将.env添加到.gitignore — 若尚未添加。
- 进入代码集成环节 — 跳过步骤3(已完成),直接进入步骤4编写中间件代码。
若用户选择手动创建,请遵循设置指南中的「手动设置」部分获取完整说明。然后继续下方步骤3。
手动创建API的快速参考命令:
bash
undefinedUsing Auth0 CLI
使用Auth0 CLI
Or create manually in Auth0 Dashboard → Applications → APIs
或在Auth0控制台手动创建:应用 → APIs3. Configure .env
3. 配置.env
env
AUTH0_DOMAIN=your-tenant.auth0.com
AUTH0_AUDIENCE=https://my-api.example.comImportant: Domain must NOT include . The middleware constructs the issuer URL automatically.
https://env
AUTH0_DOMAIN=your-tenant.auth0.com
AUTH0_AUDIENCE=https://my-api.example.com重要提示: Domain不得包含。中间件会自动构造issuer URL。
https://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
with routes defined. In this case, do NOT replace their file with the template below. Instead:main.go
- Add the necessary imports (
,jwtmiddleware,jwks,validator,godotenv,net/url,os,context).strings- Add the
struct and methods.CustomClaims- Add the middleware setup code (issuer URL, JWKS provider, validator, middleware) near the top of
.main()- Ask which endpoints to protect (see below).
- 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.,for POST/DELETE), or is a valid JWT sufficient for all?"write:usersWait for the answer. If the user says "all" or "everything except health", protect all routes except(or whatever they specify as public). If they specify scope requirements per endpoint, implement per-route scope checks using/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 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
- 添加必要的导入包(
、jwtmiddleware、jwks、validator、godotenv、net/url、os、context)。strings- 添加
结构体及方法。CustomClaims- 在
函数开头附近添加中间件设置代码(issuer URL、JWKS提供器、验证器、中间件)。main()- 询问用户需要保护哪些端点(见下方说明)。
- 使用
包裹指定的处理器。middleware.CheckJWT()- B) 从零创建新项目 — 使用下方完整模板作为起点。
暂停操作——询问需要保护的端点:如果用户的请求未明确指定需要保护的端点,请询问:"哪些端点需要认证?例如:
- 除健康检查/公开接口外全部保护 — 保护所有端点,仅开放特定公开路由
- 特定路由 — 告知我哪些路由需要认证
另外,是否有端点需要特定的权限范围/权限检查(例如:POST/DELETE请求需要),还是所有受保护端点仅需有效的JWT即可?"write:users等待用户回复。若用户表示“全部”或“除健康检查外全部”,则保护所有路由,仅开放(或用户指定的其他公开路由)。若用户指定了每个端点的权限范围要求,请使用/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 to wrap handlers that require authentication:
middleware.CheckJWT()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:bashgo 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: "The build is still failing after several fix attempts. How would you like to proceed?"AskUserQuestion
- 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?
- Yes — I'll help you configure an M2M application to get test tokens.
- 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.
- Create new — I'll create a new M2M application and authorize it for this API.
- 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":bashauth0 apps create \ --name "<PROJECT_NAME> (Test App)" \ --type m2m \ --no-input --jsonParse the JSON to extract. Do NOT useclient_id— never expose client secrets in agent context. Then create a client grant:--reveal-secretsbashauth0 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:bashauth0 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
on its own — it outputs the token to stdoutauth0 test token- Do NOT run
commands to thecurlendpoint on their own/oauth/token- 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:bashTEST_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$(...) extracts only the JWT pattern (ey...) — no surrounding output leaksgrep -o confirms success by printing LENGTH only, never the valueecho "Token acquired (${#TEST_TOKEN} chars)"- The shell variable
exists only for the duration of that single command chain — it dies immediately after$TEST_TOKEN- Agent sees only:
+ the API response body (JSON)"Token acquired (834 chars)"- No file is written, no env is exported, nothing persists
Rules for using this pattern:
- ONLY use when the user explicitly asks to test (e.g., "test it", "run the tests", "verify endpoints work")
- Always chain token acquisition + curl in a SINGLE
command — never separate them into two Bash calls&&- To test multiple endpoints, chain multiple curls in the same command:
bashTEST_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"- NEVER add
,echo $TEST_TOKEN, or any command that would print the raw token valueprintf $TEST_TOKEN- If the token acquisition fails (empty variable), the
check will halt the chain — report to the user that the M2M app may not be authorized[ -n "$TEST_TOKEN" ]- Client ID is REQUIRED — the
command requires a Client ID to be passed as the first argument. This MUST be theauth0 test tokenobtained 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.client_idIf 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:
- Start the server with in the background
go run . - Verify public endpoints return 200 and protected endpoints return 401 (no token needed)
- If the user asked to test: use the secure single-command chain above for authenticated requests
- 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/publicTest protected endpoint without token (should return 401):
bash
curl http://localhost:8080/api/privateTest 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操作指引: 编写代码后,需验证编译是否通过:bashgo build ./...若编译失败,请诊断错误并修复。最多尝试5-6次。失败处理: 若经过5-6次修复尝试后仍编译失败,请停止操作并使用询问用户: "经过多次修复尝试后,编译仍失败。你希望如何处理?"AskUserQuestion
- 继续迭代修复
- 手动修复 — 我将展示剩余错误
- 跳过编译验证 — 直接继续后续步骤
若错误持续存在,每尝试5-6次后重复此检查。
Agent操作指引: 编译成功后暂停——询问用户。编译成功后,请询问用户:"你的API已配置完成且编译成功。是否需要我协助设置测试环节?
- 是 — 我将帮你配置M2M应用以获取测试令牌。
- 否 — 目前操作已完成。
你选择哪种方式?"除非用户选择“是”,否则请勿进行测试设置。若用户选择“否”,请总结已完成的操作并停止。
Agent操作指引(M2M应用设置——仅当用户需要测试时执行):若用户选择设置测试,请询问:"要测试受保护的端点,你需要一个已授权的机器对机器(M2M)应用来请求该API的令牌。
- 创建新应用 — 我将创建一个新的M2M应用并授权其访问该API。
- 使用现有应用 — 你已有M2M应用,请提供Client ID,我将授权其访问该API。
你选择哪种方式?(1=创建新应用 / 2=使用现有应用)"在用户回复前,请勿继续操作。请勿从租户中静默选择现有应用。若用户选择“创建新应用”:bashauth0 apps create \ --name "<PROJECT_NAME> (Test App)" \ --type m2m \ --no-input --json解析JSON提取。请勿使用client_id参数——切勿在Agent上下文暴露客户端密钥。 然后创建客户端授权:--reveal-secretsbashauth0 api post "client-grants" --data '{ "client_id": "<CLIENT_ID>", "audience": "<API_IDENTIFIER>", "scope": ["<SCOPES>"] }'若用户选择“使用现有应用”: 询问用户获取Client ID。然后创建客户端授权以允许其访问该API:bashauth0 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看到:bashTEST_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"该方式的安全保障:
捕获标准输出——令牌被存入变量,不会显示$(...) 仅提取JWT格式内容(ey...)——无多余输出泄露grep -o 仅通过打印长度确认成功,不会显示令牌值echo "令牌已获取(${#TEST_TOKEN} 字符)"- shell变量
仅在该单命令链执行期间存在——命令执行后立即销毁$TEST_TOKEN- Agent仅能看到:
+ API响应体(JSON)"令牌已获取(834 字符)"- 不会写入文件、导出环境变量,无任何持久化数据
使用该模式的规则:
- 仅当用户明确要求测试时使用(例如:“测试一下”、“运行测试”、“验证端点是否可用”)
- 必须将令牌获取+curl命令合并为单个
命令链——不得拆分为两个Bash调用&&- 若要测试多个端点,可在同一命令链中添加多个curl命令:
bashTEST_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"- 绝对不能添加
、echo $TEST_TOKEN或任何会打印原始令牌值的命令printf $TEST_TOKEN- 若令牌获取失败(变量为空),
检查会终止命令链——告知用户M2M应用可能未授权[ -n "$TEST_TOKEN" ]- 必须提供Client ID —
命令要求第一个参数为Client ID。该参数必须是从M2M应用设置步骤(创建新应用或使用现有应用)获取的auth0 test token。若尚未完成M2M设置(无Client ID可用),请勿尝试执行测试令牌命令。请询问用户:"获取测试令牌需要M2M应用的Client ID。你希望我创建一个还是已有现成的?" — 先完成M2M设置。client_id若用户未要求测试,仅需提供手动执行的命令:auth0 test token <CLIENT_ID> --audience <AUDIENCE> --scopes <SCOPE1,SCOPE2> curl http://localhost:8080/<endpoint> -H "Authorization: Bearer <粘贴令牌到此处>"
完成M2M应用设置后:
- 在后台执行启动服务器
go run . - 验证公开端点返回200,未携带令牌时受保护端点返回401
- 若用户要求测试:使用上述安全单命令链执行认证请求
- 若用户未要求测试:提供手动命令并告知用户在终端执行
测试公开端点:
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
常见错误
| Mistake | Fix |
|---|---|
| Created Application instead of API in Auth0 | Must create API resource in Auth0 Dashboard → Applications → APIs |
| Audience doesn't match API Identifier | Must exactly match the API Identifier set in Auth0 Dashboard |
Domain includes | Use |
| Using v2 positional parameters instead of v3 options | v3 uses |
| Missing trailing slash on issuer URL | Issuer must be |
Checking | Use custom claims struct with |
Missing | Add |
Using | Use |
| 错误 | 修复方案 |
|---|---|
| 在Auth0中创建了普通应用而非API | 必须在Auth0控制台 → 应用 → APIs中创建API资源 |
| Audience与API标识符不匹配 | 必须与Auth0控制台中设置的API标识符完全一致 |
Domain包含 | 仅使用 |
| 使用v2的位置参数而非v3的选项参数 | v3使用 |
| issuer URL缺少末尾斜杠 | issuer必须为 |
针对RBAC检查 | 使用包含 |
缺少 | 添加 |
使用 | 使用类型安全的泛型 |
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
相关技能
- - Basic Auth0 setup
auth0-quickstart - - Add Multi-Factor Authentication
auth0-mfa
- - Auth0基础设置
auth0-quickstart - - 添加多因素认证
auth0-mfa
Quick Reference
快速参考
Configuration Options:
- - JWKS key function for signature verification (required)
validator.WithKeyFunc(provider.KeyFunc) - - Expected signing algorithm (required)
validator.WithAlgorithm(validator.RS256) - - Token issuer URL with trailing slash (required)
validator.WithIssuer(url) - - API Identifier from Auth0 API settings (required)
validator.WithAudience(aud) - - Factory for custom claims struct
validator.WithCustomClaims(fn) - - Clock skew tolerance
validator.WithAllowedClockSkew(d)
Claims Access:
- - Type-safe claims retrieval
jwtmiddleware.GetClaims[*validator.ValidatedClaims](r.Context()) - - User ID (sub)
claims.RegisteredClaims.Subject - - Space-separated scopes
claims.CustomClaims.(*CustomClaims).Scope - - Permission strings
claims.CustomClaims.(*CustomClaims).Permissions
Common Use Cases:
- Protect routes → (see Step 5)
middleware.CheckJWT(handler) - Permission enforcement → Integration Guide
- DPoP token binding → Integration Guide
- Framework adapters (Gin, Echo) → Integration Guide
- Advanced JWT config → API Reference
配置选项:
- - 用于签名验证的JWKS密钥函数(必填)
validator.WithKeyFunc(provider.KeyFunc) - - 预期的签名算法(必填)
validator.WithAlgorithm(validator.RS256) - - 包含末尾斜杠的令牌issuer URL(必填)
validator.WithIssuer(url) - - Auth0 API设置中的API标识符(必填)
validator.WithAudience(aud) - - 自定义声明结构体的工厂函数
validator.WithCustomClaims(fn) - - 时钟偏差容忍度
validator.WithAllowedClockSkew(d)
声明访问:
- - 类型安全的声明获取方式
jwtmiddleware.GetClaims[*validator.ValidatedClaims](r.Context()) - - 用户ID(sub)
claims.RegisteredClaims.Subject - - 空格分隔的权限范围
claims.CustomClaims.(*CustomClaims).Scope - - 权限字符串
claims.CustomClaims.(*CustomClaims).Permissions
常见用例:
- 保护路由 → (见步骤5)
middleware.CheckJWT(handler) - 权限实施 → 集成指南
- 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参考 - 完整配置选项及验证器/中间件参考