Authentification Encore Go
Instructions
Encore Go fournit un système d'authentification intégré utilisant l'annotation //encore:authhandler.
1. Créer un gestionnaire d'authentification
package auth
import (
"context"
"encore.dev/beta/auth"
"encore.dev/beta/errs"
)
// AuthParams définit ce que reçoit le gestionnaire d'authentification
type AuthParams struct {
Authorization string `header:"Authorization"`
}
// AuthData définit ce à quoi les requêtes authentifiées ont accès
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. Protéger les endpoints
package user
import "context"
// Endpoint protégé - requiert l'authentification
//encore:api auth method=GET path=/profile
func GetProfile(ctx context.Context) (*Profile, error) {
// Seuls les utilisateurs authentifiés arrivent ici
}
// Endpoint public - pas d'authentification requise
//encore:api public method=GET path=/health
func Health(ctx context.Context) (*HealthResponse, error) {
return &HealthResponse{Status: "ok"}, nil
}
3. Accéder aux données d'authentification dans les endpoints
package user
import (
"context"
"encore.dev/beta/auth"
myauth "myapp/auth" // Importer votre package d'authentification
)
//encore:api auth method=GET path=/profile
func GetProfile(ctx context.Context) (*Profile, error) {
// Obtenir l'ID utilisateur
userID, ok := auth.UserID()
if !ok {
// Ne devrait pas se produire avec un endpoint authentifié
}
// Obtenir les données d'authentification complètes
data := auth.Data().(*myauth.AuthData)
return &Profile{
UserID: string(userID),
Email: data.Email,
Role: data.Role,
}, nil
}
Signature du gestionnaire d'authentification
Le gestionnaire d'authentification doit :
- Avoir l'annotation
//encore:authhandler - Accepter
context.Contextet un pointeur sur un struct de paramètres - Retourner
(auth.UID, *YourAuthData, error)
//encore:authhandler
func MyAuthHandler(ctx context.Context, params *Params) (auth.UID, *AuthData, error)
Comportement du gestionnaire d'authentification
| Scénario | Retourne | Résultat |
|---|---|---|
| Identifiants valides | (uid, data, nil) |
Requête authentifiée |
| Identifiants invalides | ("", nil, err) avec errs.Unauthenticated |
Réponse 401 |
| Autre erreur | ("", nil, err) |
Requête interrompue |
Patterns d'authentification courants
Validation de token JWT
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
}
Authentification par clé API
//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
}
Authentification par cookie
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
}
Authentification multi-sources (Cookie + Header + Query)
Les paramètres d'authentification peuvent extraire des données de plusieurs sources :
import "net/http"
type AuthParams struct {
SessionCookie *http.Cookie `cookie:"session"` // Depuis un cookie
Authorization string `header:"Authorization"` // Depuis un header
ClientID string `query:"client_id"` // Depuis la query string
}
//encore:authhandler
func Authenticate(ctx context.Context, params *AuthParams) (auth.UID, *AuthData, error) {
// Essayer d'abord le cookie de session
if params.SessionCookie != nil {
return authenticateWithSession(ctx, params.SessionCookie.Value)
}
// Retomber sur le header Authorization
if params.Authorization != "" {
return authenticateWithToken(ctx, params.Authorization)
}
return "", nil, &errs.Error{
Code: errs.Unauthenticated,
Message: "no credentials provided",
}
}
Authentification de service à service
Les données d'authentification se propagent automatiquement dans les appels de service interne :
package order
import (
"context"
"myapp/user" // Importer le service utilisateur
)
//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
}
// L'authentification est automatiquement propagée à cet appel
profile, err := user.GetProfile(ctx)
if err != nil {
return nil, err
}
return &OrderWithUser{Order: order, User: profile}, nil
}
Tests avec authentification
Surcharger les données d'authentification dans les tests avec auth.WithContext :
package user_test
import (
"context"
"testing"
"encore.dev/beta/auth"
myauth "myapp/auth"
"myapp/user"
)
func TestGetProfile(t *testing.T) {
// Créer un contexte avec les données d'authentification
ctx := auth.WithContext(
context.Background(),
auth.UID("test-user-123"),
&myauth.AuthData{
UserID: "test-user-123",
Email: "test@example.com",
Role: "user",
},
)
// Appeler l'endpoint avec le contexte authentifié
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)
}
}
Recommandations
- Un seul
//encore:authhandlerpar application - Retourner
auth.UIDcomme première valeur retournée (identifiant utilisateur) - Retourner votre struct
AuthDatapersonnalisé comme deuxième valeur - Utiliser
auth.UserID()pour obtenir l'ID utilisateur authentifié - Utiliser
auth.Data()et type assert pour obtenir les données d'authentification complètes - L'authentification se propage automatiquement dans les appels de service à service
- Utiliser
auth.WithContext()pour surcharger l'authentification dans les tests - Garder les gestionnaires d'authentification rapides - ils s'exécutent à chaque requête authentifiée