encore-go-auth
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseEncore Go Authentication
Encore Go 身份验证
Instructions
操作步骤
Encore Go provides a built-in authentication system using the annotation.
//encore:authhandlerEncore Go 提供了一个内置的身份验证系统,使用注解。
//encore:authhandler1. Create an Auth Handler
1. 创建身份验证处理器
go
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
}go
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
}2. Protect Endpoints
2. 保护接口端点
go
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
}go
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
}3. Access Auth Data in Endpoints
3. 在端点中访问身份验证数据
go
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
}go
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
}Auth Handler Signature
身份验证处理器签名
The auth handler must:
- Have the annotation
//encore:authhandler - Accept and a params struct pointer
context.Context - Return
(auth.UID, *YourAuthData, error)
go
//encore:authhandler
func MyAuthHandler(ctx context.Context, params *Params) (auth.UID, *AuthData, error)身份验证处理器必须满足:
- 带有注解
//encore:authhandler - 接收和一个参数结构体指针
context.Context - 返回
(auth.UID, *YourAuthData, error)
go
//encore:authhandler
func MyAuthHandler(ctx context.Context, params *Params) (auth.UID, *AuthData, error)Auth Handler Behavior
身份验证处理器行为
| Scenario | Returns | Result |
|---|---|---|
| Valid credentials | | Request authenticated |
| Invalid credentials | | 401 response |
| Other error | | Request aborted |
| 场景 | 返回值 | 结果 |
|---|---|---|
| 有效凭据 | | 请求已认证 |
| 无效凭据 | | 返回401响应 |
| 其他错误 | | 请求被中止 |
Common Auth Patterns
常见身份验证模式
JWT Token Validation
JWT令牌验证
go
import (
"github.com/golang-jwt/jwt/v5"
"encore.dev/config"
)
var secrets struct {
JWTSecret config.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
}go
import (
"github.com/golang-jwt/jwt/v5"
"encore.dev/config"
)
var secrets struct {
JWTSecret config.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
}API Key Authentication
API密钥身份验证
go
//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
}go
//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
}Cookie-Based Auth
基于Cookie的身份验证
go
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
}go
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
}Service-to-Service Auth
服务间身份验证
Auth data automatically propagates in internal service calls:
go
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
}身份验证数据会在内部服务调用中自动传播:
go
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
}Guidelines
注意事项
- Only one per application
//encore:authhandler - Return as the first return value (user identifier)
auth.UID - Return your custom struct as second value
AuthData - Use to get the authenticated user ID
auth.UserID() - Use and type assert to get full auth data
auth.Data() - Auth propagates automatically in service-to-service calls
- Keep auth handlers fast - they run on every authenticated request
- 每个应用只能有一个
//encore:authhandler - 第一个返回值必须是(用户标识符)
auth.UID - 第二个返回值是自定义的结构体
AuthData - 使用获取已认证用户的ID
auth.UserID() - 使用并进行类型断言来获取完整的身份验证数据
auth.Data() - 身份验证会在服务间调用中自动传播
- 保持身份验证处理器的执行速度——它们会在每个已认证请求中运行