encore-go-auth by encoredev/skills
npx skills add https://github.com/encoredev/skills --skill encore-go-authEncore Go 提供了一个使用 //encore:authhandler 注解的内置身份验证系统。
package auth
import (
"context"
"encore.dev/beta/auth"
"encore.dev/beta/errs"
)
// AuthParams 定义了身份验证处理程序接收的参数
type AuthParams struct {
Authorization string `header:"Authorization"`
}
// AuthData 定义了经过身份验证的请求可以访问的数据
type AuthData struct {
UserID string
Email string
Role string
}
//encore:authhandler
func Authenticate(ctx context.Context, params *AuthParams) (auth.UID, *AuthData, error) {
token := strings.TrimPrefix(params.Authorization, "Bearer ")
payload, err := verifyToken(token)
if err != nil {
return "", nil, &errs.Error{
Code: errs.Unauthenticated,
Message: "invalid token",
}
}
return auth.UID(payload.UserID), &AuthData{
UserID: payload.UserID,
Email: payload.Email,
Role: payload.Role,
}, nil
}
package user
import "context"
// 受保护的端点 - 需要身份验证
//encore:api auth method=GET path=/profile
func GetProfile(ctx context.Context) (*Profile, error) {
// 只有经过身份验证的用户才能到达这里
}
// 公共端点 - 不需要身份验证
//encore:api public method=GET path=/health
func Health(ctx context.Context) (*HealthResponse, error) {
return &HealthResponse{Status: "ok"}, nil
}
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
package user
import (
"context"
"encore.dev/beta/auth"
myauth "myapp/auth" // 导入你的身份验证包
)
//encore:api auth method=GET path=/profile
func GetProfile(ctx context.Context) (*Profile, error) {
// 获取用户 ID
userID, ok := auth.UserID()
if !ok {
// 对于需要身份验证的端点,这不应该发生
}
// 获取完整的身份验证数据
data := auth.Data().(*myauth.AuthData)
return &Profile{
UserID: string(userID),
Email: data.Email,
Role: data.Role,
}, nil
}
身份验证处理程序必须:
//encore:authhandler 注解context.Context 和一个参数结构体指针(auth.UID, *YourAuthData, error)//encore:authhandler
func MyAuthHandler(ctx context.Context, params *Params) (auth.UID, *AuthData, error)
| 场景 | 返回值 | 结果 |
|---|---|---|
| 有效凭据 | (uid, data, nil) | 请求通过身份验证 |
| 无效凭据 | ("", nil, err) 并附带 errs.Unauthenticated | 401 响应 |
| 其他错误 | ("", nil, err) | 请求中止 |
import "github.com/golang-jwt/jwt/v5"
var secrets struct {
JWTSecret string
}
func verifyToken(tokenString string) (*Claims, error) {
token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(t *jwt.Token) (interface{}, error) {
return []byte(secrets.JWTSecret), nil
})
if err != nil {
return nil, err
}
claims, ok := token.Claims.(*Claims)
if !ok || !token.Valid {
return nil, errors.New("invalid token")
}
return claims, nil
}
//encore:authhandler
func Authenticate(ctx context.Context, params *AuthParams) (auth.UID, *AuthData, error) {
apiKey := params.Authorization
user, err := db.QueryRow[User](ctx, `
SELECT id, email, role FROM users WHERE api_key = $1
`, apiKey)
if err != nil {
return "", nil, &errs.Error{
Code: errs.Unauthenticated,
Message: "invalid API key",
}
}
return auth.UID(user.ID), &AuthData{
UserID: user.ID,
Email: user.Email,
Role: user.Role,
}, nil
}
type AuthParams struct {
Cookie string `header:"Cookie"`
}
//encore:authhandler
func Authenticate(ctx context.Context, params *AuthParams) (auth.UID, *AuthData, error) {
sessionID := parseCookie(params.Cookie, "session")
if sessionID == "" {
return "", nil, &errs.Error{
Code: errs.Unauthenticated,
Message: "no session",
}
}
session, err := getSession(ctx, sessionID)
if err != nil || session.ExpiresAt.Before(time.Now()) {
return "", nil, &errs.Error{
Code: errs.Unauthenticated,
Message: "session expired",
}
}
return auth.UID(session.UserID), &AuthData{
UserID: session.UserID,
Email: session.Email,
Role: session.Role,
}, nil
}
身份验证参数可以从多个来源提取数据:
import "net/http"
type AuthParams struct {
SessionCookie *http.Cookie `cookie:"session"` // 来自 Cookie
Authorization string `header:"Authorization"` // 来自请求头
ClientID string `query:"client_id"` // 来自查询字符串
}
//encore:authhandler
func Authenticate(ctx context.Context, params *AuthParams) (auth.UID, *AuthData, error) {
// 首先尝试会话 Cookie
if params.SessionCookie != nil {
return authenticateWithSession(ctx, params.SessionCookie.Value)
}
// 回退到 Authorization 请求头
if params.Authorization != "" {
return authenticateWithToken(ctx, params.Authorization)
}
return "", nil, &errs.Error{
Code: errs.Unauthenticated,
Message: "no credentials provided",
}
}
身份验证数据在内部服务调用中自动传播:
package order
import (
"context"
"myapp/user" // 导入用户服务
)
//encore:api auth method=GET path=/orders/:id
func GetOrderWithUser(ctx context.Context, params *GetOrderParams) (*OrderWithUser, error) {
order, err := getOrder(ctx, params.ID)
if err != nil {
return nil, err
}
// 身份验证会自动传播到此调用
profile, err := user.GetProfile(ctx)
if err != nil {
return nil, err
}
return &OrderWithUser{Order: order, User: profile}, nil
}
使用 auth.WithContext 在测试中覆盖身份验证数据:
package user_test
import (
"context"
"testing"
"encore.dev/beta/auth"
myauth "myapp/auth"
"myapp/user"
)
func TestGetProfile(t *testing.T) {
// 创建包含身份验证数据的上下文
ctx := auth.WithContext(
context.Background(),
auth.UID("test-user-123"),
&myauth.AuthData{
UserID: "test-user-123",
Email: "test@example.com",
Role: "user",
},
)
// 使用经过身份验证的上下文调用端点
profile, err := user.GetProfile(ctx)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if profile.Email != "test@example.com" {
t.Errorf("expected test@example.com, got %s", profile.Email)
}
}
//encore:authhandlerauth.UID 作为第一个返回值(用户标识符)AuthData 结构体作为第二个值返回auth.UserID() 获取经过身份验证的用户 IDauth.Data() 并进行类型断言以获取完整的身份验证数据auth.WithContext() 在测试中覆盖身份验证每周安装次数
123
代码仓库
GitHub 星标数
20
首次出现时间
2026年1月21日
安全审计
安装于
opencode104
gemini-cli103
codex103
claude-code94
github-copilot85
cursor81
Encore Go provides a built-in authentication system using the //encore:authhandler annotation.
package auth
import (
"context"
"encore.dev/beta/auth"
"encore.dev/beta/errs"
)
// AuthParams defines what the auth handler receives
type AuthParams struct {
Authorization string `header:"Authorization"`
}
// AuthData defines what authenticated requests have access to
type AuthData struct {
UserID string
Email string
Role string
}
//encore:authhandler
func Authenticate(ctx context.Context, params *AuthParams) (auth.UID, *AuthData, error) {
token := strings.TrimPrefix(params.Authorization, "Bearer ")
payload, err := verifyToken(token)
if err != nil {
return "", nil, &errs.Error{
Code: errs.Unauthenticated,
Message: "invalid token",
}
}
return auth.UID(payload.UserID), &AuthData{
UserID: payload.UserID,
Email: payload.Email,
Role: payload.Role,
}, nil
}
package user
import "context"
// Protected endpoint - requires authentication
//encore:api auth method=GET path=/profile
func GetProfile(ctx context.Context) (*Profile, error) {
// Only authenticated users reach here
}
// Public endpoint - no authentication required
//encore:api public method=GET path=/health
func Health(ctx context.Context) (*HealthResponse, error) {
return &HealthResponse{Status: "ok"}, nil
}
package user
import (
"context"
"encore.dev/beta/auth"
myauth "myapp/auth" // Import your auth package
)
//encore:api auth method=GET path=/profile
func GetProfile(ctx context.Context) (*Profile, error) {
// Get the user ID
userID, ok := auth.UserID()
if !ok {
// Should not happen with auth endpoint
}
// Get full auth data
data := auth.Data().(*myauth.AuthData)
return &Profile{
UserID: string(userID),
Email: data.Email,
Role: data.Role,
}, nil
}
The auth handler must:
//encore:authhandler annotationcontext.Context and a params struct pointer(auth.UID, *YourAuthData, error)//encore:authhandler
func MyAuthHandler(ctx context.Context, params *Params) (auth.UID, *AuthData, error)
| Scenario | Returns | Result |
|---|---|---|
| Valid credentials | (uid, data, nil) | Request authenticated |
| Invalid credentials | ("", nil, err) with errs.Unauthenticated | 401 response |
| Other error | ("", nil, err) | Request aborted |
import "github.com/golang-jwt/jwt/v5"
var secrets struct {
JWTSecret string
}
func verifyToken(tokenString string) (*Claims, error) {
token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(t *jwt.Token) (interface{}, error) {
return []byte(secrets.JWTSecret), nil
})
if err != nil {
return nil, err
}
claims, ok := token.Claims.(*Claims)
if !ok || !token.Valid {
return nil, errors.New("invalid token")
}
return claims, nil
}
//encore:authhandler
func Authenticate(ctx context.Context, params *AuthParams) (auth.UID, *AuthData, error) {
apiKey := params.Authorization
user, err := db.QueryRow[User](ctx, `
SELECT id, email, role FROM users WHERE api_key = $1
`, apiKey)
if err != nil {
return "", nil, &errs.Error{
Code: errs.Unauthenticated,
Message: "invalid API key",
}
}
return auth.UID(user.ID), &AuthData{
UserID: user.ID,
Email: user.Email,
Role: user.Role,
}, nil
}
type AuthParams struct {
Cookie string `header:"Cookie"`
}
//encore:authhandler
func Authenticate(ctx context.Context, params *AuthParams) (auth.UID, *AuthData, error) {
sessionID := parseCookie(params.Cookie, "session")
if sessionID == "" {
return "", nil, &errs.Error{
Code: errs.Unauthenticated,
Message: "no session",
}
}
session, err := getSession(ctx, sessionID)
if err != nil || session.ExpiresAt.Before(time.Now()) {
return "", nil, &errs.Error{
Code: errs.Unauthenticated,
Message: "session expired",
}
}
return auth.UID(session.UserID), &AuthData{
UserID: session.UserID,
Email: session.Email,
Role: session.Role,
}, nil
}
Auth params can extract data from multiple sources:
import "net/http"
type AuthParams struct {
SessionCookie *http.Cookie `cookie:"session"` // From cookie
Authorization string `header:"Authorization"` // From header
ClientID string `query:"client_id"` // From query string
}
//encore:authhandler
func Authenticate(ctx context.Context, params *AuthParams) (auth.UID, *AuthData, error) {
// Try session cookie first
if params.SessionCookie != nil {
return authenticateWithSession(ctx, params.SessionCookie.Value)
}
// Fall back to Authorization header
if params.Authorization != "" {
return authenticateWithToken(ctx, params.Authorization)
}
return "", nil, &errs.Error{
Code: errs.Unauthenticated,
Message: "no credentials provided",
}
}
Auth data automatically propagates in internal service calls:
package order
import (
"context"
"myapp/user" // Import the user service
)
//encore:api auth method=GET path=/orders/:id
func GetOrderWithUser(ctx context.Context, params *GetOrderParams) (*OrderWithUser, error) {
order, err := getOrder(ctx, params.ID)
if err != nil {
return nil, err
}
// Auth is automatically propagated to this call
profile, err := user.GetProfile(ctx)
if err != nil {
return nil, err
}
return &OrderWithUser{Order: order, User: profile}, nil
}
Override auth data in tests using auth.WithContext:
package user_test
import (
"context"
"testing"
"encore.dev/beta/auth"
myauth "myapp/auth"
"myapp/user"
)
func TestGetProfile(t *testing.T) {
// Create a context with auth data
ctx := auth.WithContext(
context.Background(),
auth.UID("test-user-123"),
&myauth.AuthData{
UserID: "test-user-123",
Email: "test@example.com",
Role: "user",
},
)
// Call the endpoint with the authenticated context
profile, err := user.GetProfile(ctx)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if profile.Email != "test@example.com" {
t.Errorf("expected test@example.com, got %s", profile.Email)
}
}
//encore:authhandler per applicationauth.UID as the first return value (user identifier)AuthData struct as second valueauth.UserID() to get the authenticated user IDauth.Data() and type assert to get full auth dataauth.WithContext() to override auth in testsWeekly Installs
123
Repository
GitHub Stars
20
First Seen
Jan 21, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
opencode104
gemini-cli103
codex103
claude-code94
github-copilot85
cursor81
Lark Skill Maker 教程:基于飞书CLI创建AI技能,自动化工作流与API调用指南
31,500 周安装
Hydrex 治理指南:锁定 HYDX 获取投票权,参与流动性池投票与单边挖矿
1 周安装
Base技能:Bankrbot/Clawdbot-Skill核心基础功能,GitHub星标984+的开发者工具
1 周安装
Bankr Signals - Base区块链交易信号验证与复制工具,提升交易绩效
1 周安装
Vue.js 官方文档与最佳实践指南 - 从入门到精通,涵盖响应式、TypeScript、组件等核心主题
1 周安装
SQLAlchemy 2.0 中文文档与参考指南 - Python ORM 数据库工具完整技能
1 周安装
Effect 错误管理指南:TypeScript 函数式编程中的预期错误与缺陷处理
2 周安装