smithery/encoredev

encore-go-auth

Protect Encore Go endpoints with authentication and authorize callers. Covers `auth.AuthHandler`, `auth.UserID`, the `Authorization` header, and `//encore:api auth`.

Installation

$ npx skills add smithery/encoredev --skill encore-go-auth

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from smithery/encoredev · top by installs.

npx skills add smithery/encoredev

Browse all from smithery/encoredev

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 8,358 B
  • docs SUMMARY.md 62 B

History

  1. First recorded snapshot · 0 installs

SKILL.md

Encore Go Authentication

Instructions

Encore Go provides a built-in authentication system using the //encore:authhandler annotation.

1. Create an Auth Handler

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
}

2. Protect Endpoints

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
}

3. Access Auth Data in Endpoints

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
}

Auth Handler Signature

The auth handler must:

  1. Have the //encore:authhandler annotation
  2. Accept context.Context and a params struct pointer
  3. Return (auth.UID, *YourAuthData, error)
//encore:authhandler
func MyAuthHandler(ctx context.Context, params *Params) (auth.UID, *AuthData, error)

Auth Handler Behavior

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

Common Auth Patterns

JWT Token Validation

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
}

API Key Authentication

//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

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
}

Multi-Source Auth (Cookie + Header + Query)

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",
    }
}

Service-to-Service Auth

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
}

Testing with Auth

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:  "[email protected]",
            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 != "[email protected]" {
        t.Errorf("expected [email protected], got %s", profile.Email)
    }
}

Guidelines

  • Only one //encore:authhandler per application
  • Return auth.UID as the first return value (user identifier)
  • Return your custom AuthData struct as second value
  • Use auth.UserID() to get the authenticated user ID
  • Use auth.Data() and type assert to get full auth data
  • Auth propagates automatically in service-to-service calls
  • Use auth.WithContext() to override auth in tests
  • Keep auth handlers fast - they run on every authenticated request