[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-encore-encore-go-auth":3,"mdc--a7e7rn-key":37,"related-org-encore-encore-go-auth":2268,"related-repo-encore-encore-go-auth":2434},{"slug":4,"name":4,"fn":5,"description":6,"org":7,"tags":12,"stars":24,"repoUrl":25,"updatedAt":26,"license":27,"forks":28,"topics":29,"repo":32,"sourceUrl":35,"mdContent":36},"encore-go-auth","implement authentication with Encore Go","Protect Encore Go endpoints with authentication and authorize callers. Covers `auth.AuthHandler`, `auth.UserID`, the `Authorization` header, and `\u002F\u002Fencore:api auth`.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},"encore","Encore","https:\u002F\u002Fpexgzepcugksgbtrxkhf.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Forg-logos\u002Fencore.png","encoredev",[13,17,20,21],{"name":14,"slug":15,"type":16},"Backend","backend","tag",{"name":18,"slug":19,"type":16},"Auth","auth",{"name":9,"slug":8,"type":16},{"name":22,"slug":23,"type":16},"Go","go",26,"https:\u002F\u002Fgithub.com\u002Fencoredev\u002Fskills","2026-04-06T18:09:51.065102",null,5,[30,31],"claude-code","skills",{"repoUrl":25,"stars":24,"forks":28,"topics":33,"description":34},[30,31],"Agent Skills for development with Encore.","https:\u002F\u002Fgithub.com\u002Fencoredev\u002Fskills\u002Ftree\u002FHEAD\u002Fencore\u002Fgo-auth","---\nname: encore-go-auth\ndescription: Protect Encore Go endpoints with authentication and authorize callers. Covers `auth.AuthHandler`, `auth.UserID`, the `Authorization` header, and `\u002F\u002Fencore:api auth`.\nwhen_to_use: >-\n  User wants to require login on a Go endpoint, restrict an endpoint to authenticated\u002Fsigned-in users, validate a bearer token \u002F JWT \u002F API key from an `Authorization` header, read the current user inside a handler (`auth.UserID()` \u002F `auth.Data()`), define an `auth.AuthHandler`, return `errs.Unauthenticated` from a handler, or use `\u002F\u002Fencore:api auth` on a handler. Trigger phrases: \"protect this endpoint\", \"only authenticated users\", \"require login\", \"Authorization header\", \"bearer token\", \"401\", \"403\", \"who is calling\", \"current user\".\n---\n\n# Encore Go Authentication\n\n## Instructions\n\nEncore Go provides a built-in authentication system using the `\u002F\u002Fencore:authhandler` annotation.\n\n### 1. Create an Auth Handler\n\n```go\npackage auth\n\nimport (\n    \"context\"\n    \"encore.dev\u002Fbeta\u002Fauth\"\n    \"encore.dev\u002Fbeta\u002Ferrs\"\n)\n\n\u002F\u002F AuthParams defines what the auth handler receives\ntype AuthParams struct {\n    Authorization string `header:\"Authorization\"`\n}\n\n\u002F\u002F AuthData defines what authenticated requests have access to\ntype AuthData struct {\n    UserID string\n    Email  string\n    Role   string\n}\n\n\u002F\u002Fencore:authhandler\nfunc Authenticate(ctx context.Context, params *AuthParams) (auth.UID, *AuthData, error) {\n    token := strings.TrimPrefix(params.Authorization, \"Bearer \")\n    \n    payload, err := verifyToken(token)\n    if err != nil {\n        return \"\", nil, &errs.Error{\n            Code:    errs.Unauthenticated,\n            Message: \"invalid token\",\n        }\n    }\n    \n    return auth.UID(payload.UserID), &AuthData{\n        UserID: payload.UserID,\n        Email:  payload.Email,\n        Role:   payload.Role,\n    }, nil\n}\n```\n\n### 2. Protect Endpoints\n\n```go\npackage user\n\nimport \"context\"\n\n\u002F\u002F Protected endpoint - requires authentication\n\u002F\u002Fencore:api auth method=GET path=\u002Fprofile\nfunc GetProfile(ctx context.Context) (*Profile, error) {\n    \u002F\u002F Only authenticated users reach here\n}\n\n\u002F\u002F Public endpoint - no authentication required\n\u002F\u002Fencore:api public method=GET path=\u002Fhealth\nfunc Health(ctx context.Context) (*HealthResponse, error) {\n    return &HealthResponse{Status: \"ok\"}, nil\n}\n```\n\n### 3. Access Auth Data in Endpoints\n\n```go\npackage user\n\nimport (\n    \"context\"\n    \"encore.dev\u002Fbeta\u002Fauth\"\n    myauth \"myapp\u002Fauth\"  \u002F\u002F Import your auth package\n)\n\n\u002F\u002Fencore:api auth method=GET path=\u002Fprofile\nfunc GetProfile(ctx context.Context) (*Profile, error) {\n    \u002F\u002F Get the user ID\n    userID, ok := auth.UserID()\n    if !ok {\n        \u002F\u002F Should not happen with auth endpoint\n    }\n    \n    \u002F\u002F Get full auth data\n    data := auth.Data().(*myauth.AuthData)\n    \n    return &Profile{\n        UserID: string(userID),\n        Email:  data.Email,\n        Role:   data.Role,\n    }, nil\n}\n```\n\n## Auth Handler Signature\n\nThe auth handler must:\n1. Have the `\u002F\u002Fencore:authhandler` annotation\n2. Accept `context.Context` and a params struct pointer\n3. Return `(auth.UID, *YourAuthData, error)`\n\n```go\n\u002F\u002Fencore:authhandler\nfunc MyAuthHandler(ctx context.Context, params *Params) (auth.UID, *AuthData, error)\n```\n\n## Auth Handler Behavior\n\n| Scenario | Returns | Result |\n|----------|---------|--------|\n| Valid credentials | `(uid, data, nil)` | Request authenticated |\n| Invalid credentials | `(\"\", nil, err)` with `errs.Unauthenticated` | 401 response |\n| Other error | `(\"\", nil, err)` | Request aborted |\n\n## Common Auth Patterns\n\n### JWT Token Validation\n\n```go\nimport \"github.com\u002Fgolang-jwt\u002Fjwt\u002Fv5\"\n\nvar secrets struct {\n    JWTSecret string\n}\n\nfunc verifyToken(tokenString string) (*Claims, error) {\n    token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(t *jwt.Token) (interface{}, error) {\n        return []byte(secrets.JWTSecret), nil\n    })\n    if err != nil {\n        return nil, err\n    }\n    \n    claims, ok := token.Claims.(*Claims)\n    if !ok || !token.Valid {\n        return nil, errors.New(\"invalid token\")\n    }\n    \n    return claims, nil\n}\n```\n\n### API Key Authentication\n\n```go\n\u002F\u002Fencore:authhandler\nfunc Authenticate(ctx context.Context, params *AuthParams) (auth.UID, *AuthData, error) {\n    apiKey := params.Authorization\n    \n    user, err := db.QueryRow[User](ctx, `\n        SELECT id, email, role FROM users WHERE api_key = $1\n    `, apiKey)\n    if err != nil {\n        return \"\", nil, &errs.Error{\n            Code:    errs.Unauthenticated,\n            Message: \"invalid API key\",\n        }\n    }\n    \n    return auth.UID(user.ID), &AuthData{\n        UserID: user.ID,\n        Email:  user.Email,\n        Role:   user.Role,\n    }, nil\n}\n```\n\n### Cookie-Based Auth\n\n```go\ntype AuthParams struct {\n    Cookie string `header:\"Cookie\"`\n}\n\n\u002F\u002Fencore:authhandler\nfunc Authenticate(ctx context.Context, params *AuthParams) (auth.UID, *AuthData, error) {\n    sessionID := parseCookie(params.Cookie, \"session\")\n    if sessionID == \"\" {\n        return \"\", nil, &errs.Error{\n            Code:    errs.Unauthenticated,\n            Message: \"no session\",\n        }\n    }\n\n    session, err := getSession(ctx, sessionID)\n    if err != nil || session.ExpiresAt.Before(time.Now()) {\n        return \"\", nil, &errs.Error{\n            Code:    errs.Unauthenticated,\n            Message: \"session expired\",\n        }\n    }\n\n    return auth.UID(session.UserID), &AuthData{\n        UserID: session.UserID,\n        Email:  session.Email,\n        Role:   session.Role,\n    }, nil\n}\n```\n\n### Multi-Source Auth (Cookie + Header + Query)\n\nAuth params can extract data from multiple sources:\n\n```go\nimport \"net\u002Fhttp\"\n\ntype AuthParams struct {\n    SessionCookie *http.Cookie `cookie:\"session\"`       \u002F\u002F From cookie\n    Authorization string       `header:\"Authorization\"` \u002F\u002F From header\n    ClientID      string       `query:\"client_id\"`      \u002F\u002F From query string\n}\n\n\u002F\u002Fencore:authhandler\nfunc Authenticate(ctx context.Context, params *AuthParams) (auth.UID, *AuthData, error) {\n    \u002F\u002F Try session cookie first\n    if params.SessionCookie != nil {\n        return authenticateWithSession(ctx, params.SessionCookie.Value)\n    }\n\n    \u002F\u002F Fall back to Authorization header\n    if params.Authorization != \"\" {\n        return authenticateWithToken(ctx, params.Authorization)\n    }\n\n    return \"\", nil, &errs.Error{\n        Code:    errs.Unauthenticated,\n        Message: \"no credentials provided\",\n    }\n}\n```\n\n## Service-to-Service Auth\n\nAuth data automatically propagates in internal service calls:\n\n```go\npackage order\n\nimport (\n    \"context\"\n    \"myapp\u002Fuser\"  \u002F\u002F Import the user service\n)\n\n\u002F\u002Fencore:api auth method=GET path=\u002Forders\u002F:id\nfunc GetOrderWithUser(ctx context.Context, params *GetOrderParams) (*OrderWithUser, error) {\n    order, err := getOrder(ctx, params.ID)\n    if err != nil {\n        return nil, err\n    }\n    \n    \u002F\u002F Auth is automatically propagated to this call\n    profile, err := user.GetProfile(ctx)\n    if err != nil {\n        return nil, err\n    }\n    \n    return &OrderWithUser{Order: order, User: profile}, nil\n}\n```\n\n## Testing with Auth\n\nOverride auth data in tests using `auth.WithContext`:\n\n```go\npackage user_test\n\nimport (\n    \"context\"\n    \"testing\"\n\n    \"encore.dev\u002Fbeta\u002Fauth\"\n    myauth \"myapp\u002Fauth\"\n    \"myapp\u002Fuser\"\n)\n\nfunc TestGetProfile(t *testing.T) {\n    \u002F\u002F Create a context with auth data\n    ctx := auth.WithContext(\n        context.Background(),\n        auth.UID(\"test-user-123\"),\n        &myauth.AuthData{\n            UserID: \"test-user-123\",\n            Email:  \"test@example.com\",\n            Role:   \"user\",\n        },\n    )\n\n    \u002F\u002F Call the endpoint with the authenticated context\n    profile, err := user.GetProfile(ctx)\n    if err != nil {\n        t.Fatalf(\"unexpected error: %v\", err)\n    }\n\n    if profile.Email != \"test@example.com\" {\n        t.Errorf(\"expected test@example.com, got %s\", profile.Email)\n    }\n}\n```\n\n## Guidelines\n\n- Only one `\u002F\u002Fencore:authhandler` per application\n- Return `auth.UID` as the first return value (user identifier)\n- Return your custom `AuthData` struct as second value\n- Use `auth.UserID()` to get the authenticated user ID\n- Use `auth.Data()` and type assert to get full auth data\n- Auth propagates automatically in service-to-service calls\n- Use `auth.WithContext()` to override auth in tests\n- Keep auth handlers fast - they run on every authenticated request\n",{"data":38,"body":40},{"name":4,"description":6,"when_to_use":39},"User wants to require login on a Go endpoint, restrict an endpoint to authenticated\u002Fsigned-in users, validate a bearer token \u002F JWT \u002F API key from an `Authorization` header, read the current user inside a handler (`auth.UserID()` \u002F `auth.Data()`), define an `auth.AuthHandler`, return `errs.Unauthenticated` from a handler, or use `\u002F\u002Fencore:api auth` on a handler. Trigger phrases: \"protect this endpoint\", \"only authenticated users\", \"require login\", \"Authorization header\", \"bearer token\", \"401\", \"403\", \"who is calling\", \"current user\".",{"type":41,"children":42},"root",[43,52,59,74,81,428,434,556,562,755,761,766,807,829,835,941,947,953,1119,1125,1281,1287,1501,1507,1512,1707,1713,1718,1892,1898,1911,2168,2174,2262],{"type":44,"tag":45,"props":46,"children":48},"element","h1",{"id":47},"encore-go-authentication",[49],{"type":50,"value":51},"text","Encore Go Authentication",{"type":44,"tag":53,"props":54,"children":56},"h2",{"id":55},"instructions",[57],{"type":50,"value":58},"Instructions",{"type":44,"tag":60,"props":61,"children":62},"p",{},[63,65,72],{"type":50,"value":64},"Encore Go provides a built-in authentication system using the ",{"type":44,"tag":66,"props":67,"children":69},"code",{"className":68},[],[70],{"type":50,"value":71},"\u002F\u002Fencore:authhandler",{"type":50,"value":73}," annotation.",{"type":44,"tag":75,"props":76,"children":78},"h3",{"id":77},"_1-create-an-auth-handler",[79],{"type":50,"value":80},"1. Create an Auth Handler",{"type":44,"tag":82,"props":83,"children":87},"pre",{"className":84,"code":85,"language":23,"meta":86,"style":86},"language-go shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","package auth\n\nimport (\n    \"context\"\n    \"encore.dev\u002Fbeta\u002Fauth\"\n    \"encore.dev\u002Fbeta\u002Ferrs\"\n)\n\n\u002F\u002F AuthParams defines what the auth handler receives\ntype AuthParams struct {\n    Authorization string `header:\"Authorization\"`\n}\n\n\u002F\u002F AuthData defines what authenticated requests have access to\ntype AuthData struct {\n    UserID string\n    Email  string\n    Role   string\n}\n\n\u002F\u002Fencore:authhandler\nfunc Authenticate(ctx context.Context, params *AuthParams) (auth.UID, *AuthData, error) {\n    token := strings.TrimPrefix(params.Authorization, \"Bearer \")\n    \n    payload, err := verifyToken(token)\n    if err != nil {\n        return \"\", nil, &errs.Error{\n            Code:    errs.Unauthenticated,\n            Message: \"invalid token\",\n        }\n    }\n    \n    return auth.UID(payload.UserID), &AuthData{\n        UserID: payload.UserID,\n        Email:  payload.Email,\n        Role:   payload.Role,\n    }, nil\n}\n","",[88],{"type":44,"tag":66,"props":89,"children":90},{"__ignoreMap":86},[91,102,112,121,130,138,147,156,164,173,182,191,200,208,217,226,235,244,253,261,269,278,287,296,305,314,322,331,340,349,358,367,375,384,393,402,411,420],{"type":44,"tag":92,"props":93,"children":96},"span",{"class":94,"line":95},"line",1,[97],{"type":44,"tag":92,"props":98,"children":99},{},[100],{"type":50,"value":101},"package auth\n",{"type":44,"tag":92,"props":103,"children":105},{"class":94,"line":104},2,[106],{"type":44,"tag":92,"props":107,"children":109},{"emptyLinePlaceholder":108},true,[110],{"type":50,"value":111},"\n",{"type":44,"tag":92,"props":113,"children":115},{"class":94,"line":114},3,[116],{"type":44,"tag":92,"props":117,"children":118},{},[119],{"type":50,"value":120},"import (\n",{"type":44,"tag":92,"props":122,"children":124},{"class":94,"line":123},4,[125],{"type":44,"tag":92,"props":126,"children":127},{},[128],{"type":50,"value":129},"    \"context\"\n",{"type":44,"tag":92,"props":131,"children":132},{"class":94,"line":28},[133],{"type":44,"tag":92,"props":134,"children":135},{},[136],{"type":50,"value":137},"    \"encore.dev\u002Fbeta\u002Fauth\"\n",{"type":44,"tag":92,"props":139,"children":141},{"class":94,"line":140},6,[142],{"type":44,"tag":92,"props":143,"children":144},{},[145],{"type":50,"value":146},"    \"encore.dev\u002Fbeta\u002Ferrs\"\n",{"type":44,"tag":92,"props":148,"children":150},{"class":94,"line":149},7,[151],{"type":44,"tag":92,"props":152,"children":153},{},[154],{"type":50,"value":155},")\n",{"type":44,"tag":92,"props":157,"children":159},{"class":94,"line":158},8,[160],{"type":44,"tag":92,"props":161,"children":162},{"emptyLinePlaceholder":108},[163],{"type":50,"value":111},{"type":44,"tag":92,"props":165,"children":167},{"class":94,"line":166},9,[168],{"type":44,"tag":92,"props":169,"children":170},{},[171],{"type":50,"value":172},"\u002F\u002F AuthParams defines what the auth handler receives\n",{"type":44,"tag":92,"props":174,"children":176},{"class":94,"line":175},10,[177],{"type":44,"tag":92,"props":178,"children":179},{},[180],{"type":50,"value":181},"type AuthParams struct {\n",{"type":44,"tag":92,"props":183,"children":185},{"class":94,"line":184},11,[186],{"type":44,"tag":92,"props":187,"children":188},{},[189],{"type":50,"value":190},"    Authorization string `header:\"Authorization\"`\n",{"type":44,"tag":92,"props":192,"children":194},{"class":94,"line":193},12,[195],{"type":44,"tag":92,"props":196,"children":197},{},[198],{"type":50,"value":199},"}\n",{"type":44,"tag":92,"props":201,"children":203},{"class":94,"line":202},13,[204],{"type":44,"tag":92,"props":205,"children":206},{"emptyLinePlaceholder":108},[207],{"type":50,"value":111},{"type":44,"tag":92,"props":209,"children":211},{"class":94,"line":210},14,[212],{"type":44,"tag":92,"props":213,"children":214},{},[215],{"type":50,"value":216},"\u002F\u002F AuthData defines what authenticated requests have access to\n",{"type":44,"tag":92,"props":218,"children":220},{"class":94,"line":219},15,[221],{"type":44,"tag":92,"props":222,"children":223},{},[224],{"type":50,"value":225},"type AuthData struct {\n",{"type":44,"tag":92,"props":227,"children":229},{"class":94,"line":228},16,[230],{"type":44,"tag":92,"props":231,"children":232},{},[233],{"type":50,"value":234},"    UserID string\n",{"type":44,"tag":92,"props":236,"children":238},{"class":94,"line":237},17,[239],{"type":44,"tag":92,"props":240,"children":241},{},[242],{"type":50,"value":243},"    Email  string\n",{"type":44,"tag":92,"props":245,"children":247},{"class":94,"line":246},18,[248],{"type":44,"tag":92,"props":249,"children":250},{},[251],{"type":50,"value":252},"    Role   string\n",{"type":44,"tag":92,"props":254,"children":256},{"class":94,"line":255},19,[257],{"type":44,"tag":92,"props":258,"children":259},{},[260],{"type":50,"value":199},{"type":44,"tag":92,"props":262,"children":264},{"class":94,"line":263},20,[265],{"type":44,"tag":92,"props":266,"children":267},{"emptyLinePlaceholder":108},[268],{"type":50,"value":111},{"type":44,"tag":92,"props":270,"children":272},{"class":94,"line":271},21,[273],{"type":44,"tag":92,"props":274,"children":275},{},[276],{"type":50,"value":277},"\u002F\u002Fencore:authhandler\n",{"type":44,"tag":92,"props":279,"children":281},{"class":94,"line":280},22,[282],{"type":44,"tag":92,"props":283,"children":284},{},[285],{"type":50,"value":286},"func Authenticate(ctx context.Context, params *AuthParams) (auth.UID, *AuthData, error) {\n",{"type":44,"tag":92,"props":288,"children":290},{"class":94,"line":289},23,[291],{"type":44,"tag":92,"props":292,"children":293},{},[294],{"type":50,"value":295},"    token := strings.TrimPrefix(params.Authorization, \"Bearer \")\n",{"type":44,"tag":92,"props":297,"children":299},{"class":94,"line":298},24,[300],{"type":44,"tag":92,"props":301,"children":302},{},[303],{"type":50,"value":304},"    \n",{"type":44,"tag":92,"props":306,"children":308},{"class":94,"line":307},25,[309],{"type":44,"tag":92,"props":310,"children":311},{},[312],{"type":50,"value":313},"    payload, err := verifyToken(token)\n",{"type":44,"tag":92,"props":315,"children":316},{"class":94,"line":24},[317],{"type":44,"tag":92,"props":318,"children":319},{},[320],{"type":50,"value":321},"    if err != nil {\n",{"type":44,"tag":92,"props":323,"children":325},{"class":94,"line":324},27,[326],{"type":44,"tag":92,"props":327,"children":328},{},[329],{"type":50,"value":330},"        return \"\", nil, &errs.Error{\n",{"type":44,"tag":92,"props":332,"children":334},{"class":94,"line":333},28,[335],{"type":44,"tag":92,"props":336,"children":337},{},[338],{"type":50,"value":339},"            Code:    errs.Unauthenticated,\n",{"type":44,"tag":92,"props":341,"children":343},{"class":94,"line":342},29,[344],{"type":44,"tag":92,"props":345,"children":346},{},[347],{"type":50,"value":348},"            Message: \"invalid token\",\n",{"type":44,"tag":92,"props":350,"children":352},{"class":94,"line":351},30,[353],{"type":44,"tag":92,"props":354,"children":355},{},[356],{"type":50,"value":357},"        }\n",{"type":44,"tag":92,"props":359,"children":361},{"class":94,"line":360},31,[362],{"type":44,"tag":92,"props":363,"children":364},{},[365],{"type":50,"value":366},"    }\n",{"type":44,"tag":92,"props":368,"children":370},{"class":94,"line":369},32,[371],{"type":44,"tag":92,"props":372,"children":373},{},[374],{"type":50,"value":304},{"type":44,"tag":92,"props":376,"children":378},{"class":94,"line":377},33,[379],{"type":44,"tag":92,"props":380,"children":381},{},[382],{"type":50,"value":383},"    return auth.UID(payload.UserID), &AuthData{\n",{"type":44,"tag":92,"props":385,"children":387},{"class":94,"line":386},34,[388],{"type":44,"tag":92,"props":389,"children":390},{},[391],{"type":50,"value":392},"        UserID: payload.UserID,\n",{"type":44,"tag":92,"props":394,"children":396},{"class":94,"line":395},35,[397],{"type":44,"tag":92,"props":398,"children":399},{},[400],{"type":50,"value":401},"        Email:  payload.Email,\n",{"type":44,"tag":92,"props":403,"children":405},{"class":94,"line":404},36,[406],{"type":44,"tag":92,"props":407,"children":408},{},[409],{"type":50,"value":410},"        Role:   payload.Role,\n",{"type":44,"tag":92,"props":412,"children":414},{"class":94,"line":413},37,[415],{"type":44,"tag":92,"props":416,"children":417},{},[418],{"type":50,"value":419},"    }, nil\n",{"type":44,"tag":92,"props":421,"children":423},{"class":94,"line":422},38,[424],{"type":44,"tag":92,"props":425,"children":426},{},[427],{"type":50,"value":199},{"type":44,"tag":75,"props":429,"children":431},{"id":430},"_2-protect-endpoints",[432],{"type":50,"value":433},"2. Protect Endpoints",{"type":44,"tag":82,"props":435,"children":437},{"className":84,"code":436,"language":23,"meta":86,"style":86},"package user\n\nimport \"context\"\n\n\u002F\u002F Protected endpoint - requires authentication\n\u002F\u002Fencore:api auth method=GET path=\u002Fprofile\nfunc GetProfile(ctx context.Context) (*Profile, error) {\n    \u002F\u002F Only authenticated users reach here\n}\n\n\u002F\u002F Public endpoint - no authentication required\n\u002F\u002Fencore:api public method=GET path=\u002Fhealth\nfunc Health(ctx context.Context) (*HealthResponse, error) {\n    return &HealthResponse{Status: \"ok\"}, nil\n}\n",[438],{"type":44,"tag":66,"props":439,"children":440},{"__ignoreMap":86},[441,449,456,464,471,479,487,495,503,510,517,525,533,541,549],{"type":44,"tag":92,"props":442,"children":443},{"class":94,"line":95},[444],{"type":44,"tag":92,"props":445,"children":446},{},[447],{"type":50,"value":448},"package user\n",{"type":44,"tag":92,"props":450,"children":451},{"class":94,"line":104},[452],{"type":44,"tag":92,"props":453,"children":454},{"emptyLinePlaceholder":108},[455],{"type":50,"value":111},{"type":44,"tag":92,"props":457,"children":458},{"class":94,"line":114},[459],{"type":44,"tag":92,"props":460,"children":461},{},[462],{"type":50,"value":463},"import \"context\"\n",{"type":44,"tag":92,"props":465,"children":466},{"class":94,"line":123},[467],{"type":44,"tag":92,"props":468,"children":469},{"emptyLinePlaceholder":108},[470],{"type":50,"value":111},{"type":44,"tag":92,"props":472,"children":473},{"class":94,"line":28},[474],{"type":44,"tag":92,"props":475,"children":476},{},[477],{"type":50,"value":478},"\u002F\u002F Protected endpoint - requires authentication\n",{"type":44,"tag":92,"props":480,"children":481},{"class":94,"line":140},[482],{"type":44,"tag":92,"props":483,"children":484},{},[485],{"type":50,"value":486},"\u002F\u002Fencore:api auth method=GET path=\u002Fprofile\n",{"type":44,"tag":92,"props":488,"children":489},{"class":94,"line":149},[490],{"type":44,"tag":92,"props":491,"children":492},{},[493],{"type":50,"value":494},"func GetProfile(ctx context.Context) (*Profile, error) {\n",{"type":44,"tag":92,"props":496,"children":497},{"class":94,"line":158},[498],{"type":44,"tag":92,"props":499,"children":500},{},[501],{"type":50,"value":502},"    \u002F\u002F Only authenticated users reach here\n",{"type":44,"tag":92,"props":504,"children":505},{"class":94,"line":166},[506],{"type":44,"tag":92,"props":507,"children":508},{},[509],{"type":50,"value":199},{"type":44,"tag":92,"props":511,"children":512},{"class":94,"line":175},[513],{"type":44,"tag":92,"props":514,"children":515},{"emptyLinePlaceholder":108},[516],{"type":50,"value":111},{"type":44,"tag":92,"props":518,"children":519},{"class":94,"line":184},[520],{"type":44,"tag":92,"props":521,"children":522},{},[523],{"type":50,"value":524},"\u002F\u002F Public endpoint - no authentication required\n",{"type":44,"tag":92,"props":526,"children":527},{"class":94,"line":193},[528],{"type":44,"tag":92,"props":529,"children":530},{},[531],{"type":50,"value":532},"\u002F\u002Fencore:api public method=GET path=\u002Fhealth\n",{"type":44,"tag":92,"props":534,"children":535},{"class":94,"line":202},[536],{"type":44,"tag":92,"props":537,"children":538},{},[539],{"type":50,"value":540},"func Health(ctx context.Context) (*HealthResponse, error) {\n",{"type":44,"tag":92,"props":542,"children":543},{"class":94,"line":210},[544],{"type":44,"tag":92,"props":545,"children":546},{},[547],{"type":50,"value":548},"    return &HealthResponse{Status: \"ok\"}, nil\n",{"type":44,"tag":92,"props":550,"children":551},{"class":94,"line":219},[552],{"type":44,"tag":92,"props":553,"children":554},{},[555],{"type":50,"value":199},{"type":44,"tag":75,"props":557,"children":559},{"id":558},"_3-access-auth-data-in-endpoints",[560],{"type":50,"value":561},"3. Access Auth Data in Endpoints",{"type":44,"tag":82,"props":563,"children":565},{"className":84,"code":564,"language":23,"meta":86,"style":86},"package user\n\nimport (\n    \"context\"\n    \"encore.dev\u002Fbeta\u002Fauth\"\n    myauth \"myapp\u002Fauth\"  \u002F\u002F Import your auth package\n)\n\n\u002F\u002Fencore:api auth method=GET path=\u002Fprofile\nfunc GetProfile(ctx context.Context) (*Profile, error) {\n    \u002F\u002F Get the user ID\n    userID, ok := auth.UserID()\n    if !ok {\n        \u002F\u002F Should not happen with auth endpoint\n    }\n    \n    \u002F\u002F Get full auth data\n    data := auth.Data().(*myauth.AuthData)\n    \n    return &Profile{\n        UserID: string(userID),\n        Email:  data.Email,\n        Role:   data.Role,\n    }, nil\n}\n",[566],{"type":44,"tag":66,"props":567,"children":568},{"__ignoreMap":86},[569,576,583,590,597,604,612,619,626,633,640,648,656,664,672,679,686,694,702,709,717,725,733,741,748],{"type":44,"tag":92,"props":570,"children":571},{"class":94,"line":95},[572],{"type":44,"tag":92,"props":573,"children":574},{},[575],{"type":50,"value":448},{"type":44,"tag":92,"props":577,"children":578},{"class":94,"line":104},[579],{"type":44,"tag":92,"props":580,"children":581},{"emptyLinePlaceholder":108},[582],{"type":50,"value":111},{"type":44,"tag":92,"props":584,"children":585},{"class":94,"line":114},[586],{"type":44,"tag":92,"props":587,"children":588},{},[589],{"type":50,"value":120},{"type":44,"tag":92,"props":591,"children":592},{"class":94,"line":123},[593],{"type":44,"tag":92,"props":594,"children":595},{},[596],{"type":50,"value":129},{"type":44,"tag":92,"props":598,"children":599},{"class":94,"line":28},[600],{"type":44,"tag":92,"props":601,"children":602},{},[603],{"type":50,"value":137},{"type":44,"tag":92,"props":605,"children":606},{"class":94,"line":140},[607],{"type":44,"tag":92,"props":608,"children":609},{},[610],{"type":50,"value":611},"    myauth \"myapp\u002Fauth\"  \u002F\u002F Import your auth package\n",{"type":44,"tag":92,"props":613,"children":614},{"class":94,"line":149},[615],{"type":44,"tag":92,"props":616,"children":617},{},[618],{"type":50,"value":155},{"type":44,"tag":92,"props":620,"children":621},{"class":94,"line":158},[622],{"type":44,"tag":92,"props":623,"children":624},{"emptyLinePlaceholder":108},[625],{"type":50,"value":111},{"type":44,"tag":92,"props":627,"children":628},{"class":94,"line":166},[629],{"type":44,"tag":92,"props":630,"children":631},{},[632],{"type":50,"value":486},{"type":44,"tag":92,"props":634,"children":635},{"class":94,"line":175},[636],{"type":44,"tag":92,"props":637,"children":638},{},[639],{"type":50,"value":494},{"type":44,"tag":92,"props":641,"children":642},{"class":94,"line":184},[643],{"type":44,"tag":92,"props":644,"children":645},{},[646],{"type":50,"value":647},"    \u002F\u002F Get the user ID\n",{"type":44,"tag":92,"props":649,"children":650},{"class":94,"line":193},[651],{"type":44,"tag":92,"props":652,"children":653},{},[654],{"type":50,"value":655},"    userID, ok := auth.UserID()\n",{"type":44,"tag":92,"props":657,"children":658},{"class":94,"line":202},[659],{"type":44,"tag":92,"props":660,"children":661},{},[662],{"type":50,"value":663},"    if !ok {\n",{"type":44,"tag":92,"props":665,"children":666},{"class":94,"line":210},[667],{"type":44,"tag":92,"props":668,"children":669},{},[670],{"type":50,"value":671},"        \u002F\u002F Should not happen with auth endpoint\n",{"type":44,"tag":92,"props":673,"children":674},{"class":94,"line":219},[675],{"type":44,"tag":92,"props":676,"children":677},{},[678],{"type":50,"value":366},{"type":44,"tag":92,"props":680,"children":681},{"class":94,"line":228},[682],{"type":44,"tag":92,"props":683,"children":684},{},[685],{"type":50,"value":304},{"type":44,"tag":92,"props":687,"children":688},{"class":94,"line":237},[689],{"type":44,"tag":92,"props":690,"children":691},{},[692],{"type":50,"value":693},"    \u002F\u002F Get full auth data\n",{"type":44,"tag":92,"props":695,"children":696},{"class":94,"line":246},[697],{"type":44,"tag":92,"props":698,"children":699},{},[700],{"type":50,"value":701},"    data := auth.Data().(*myauth.AuthData)\n",{"type":44,"tag":92,"props":703,"children":704},{"class":94,"line":255},[705],{"type":44,"tag":92,"props":706,"children":707},{},[708],{"type":50,"value":304},{"type":44,"tag":92,"props":710,"children":711},{"class":94,"line":263},[712],{"type":44,"tag":92,"props":713,"children":714},{},[715],{"type":50,"value":716},"    return &Profile{\n",{"type":44,"tag":92,"props":718,"children":719},{"class":94,"line":271},[720],{"type":44,"tag":92,"props":721,"children":722},{},[723],{"type":50,"value":724},"        UserID: string(userID),\n",{"type":44,"tag":92,"props":726,"children":727},{"class":94,"line":280},[728],{"type":44,"tag":92,"props":729,"children":730},{},[731],{"type":50,"value":732},"        Email:  data.Email,\n",{"type":44,"tag":92,"props":734,"children":735},{"class":94,"line":289},[736],{"type":44,"tag":92,"props":737,"children":738},{},[739],{"type":50,"value":740},"        Role:   data.Role,\n",{"type":44,"tag":92,"props":742,"children":743},{"class":94,"line":298},[744],{"type":44,"tag":92,"props":745,"children":746},{},[747],{"type":50,"value":419},{"type":44,"tag":92,"props":749,"children":750},{"class":94,"line":307},[751],{"type":44,"tag":92,"props":752,"children":753},{},[754],{"type":50,"value":199},{"type":44,"tag":53,"props":756,"children":758},{"id":757},"auth-handler-signature",[759],{"type":50,"value":760},"Auth Handler Signature",{"type":44,"tag":60,"props":762,"children":763},{},[764],{"type":50,"value":765},"The auth handler must:",{"type":44,"tag":767,"props":768,"children":769},"ol",{},[770,783,796],{"type":44,"tag":771,"props":772,"children":773},"li",{},[774,776,781],{"type":50,"value":775},"Have the ",{"type":44,"tag":66,"props":777,"children":779},{"className":778},[],[780],{"type":50,"value":71},{"type":50,"value":782}," annotation",{"type":44,"tag":771,"props":784,"children":785},{},[786,788,794],{"type":50,"value":787},"Accept ",{"type":44,"tag":66,"props":789,"children":791},{"className":790},[],[792],{"type":50,"value":793},"context.Context",{"type":50,"value":795}," and a params struct pointer",{"type":44,"tag":771,"props":797,"children":798},{},[799,801],{"type":50,"value":800},"Return ",{"type":44,"tag":66,"props":802,"children":804},{"className":803},[],[805],{"type":50,"value":806},"(auth.UID, *YourAuthData, error)",{"type":44,"tag":82,"props":808,"children":810},{"className":84,"code":809,"language":23,"meta":86,"style":86},"\u002F\u002Fencore:authhandler\nfunc MyAuthHandler(ctx context.Context, params *Params) (auth.UID, *AuthData, error)\n",[811],{"type":44,"tag":66,"props":812,"children":813},{"__ignoreMap":86},[814,821],{"type":44,"tag":92,"props":815,"children":816},{"class":94,"line":95},[817],{"type":44,"tag":92,"props":818,"children":819},{},[820],{"type":50,"value":277},{"type":44,"tag":92,"props":822,"children":823},{"class":94,"line":104},[824],{"type":44,"tag":92,"props":825,"children":826},{},[827],{"type":50,"value":828},"func MyAuthHandler(ctx context.Context, params *Params) (auth.UID, *AuthData, error)\n",{"type":44,"tag":53,"props":830,"children":832},{"id":831},"auth-handler-behavior",[833],{"type":50,"value":834},"Auth Handler Behavior",{"type":44,"tag":836,"props":837,"children":838},"table",{},[839,863],{"type":44,"tag":840,"props":841,"children":842},"thead",{},[843],{"type":44,"tag":844,"props":845,"children":846},"tr",{},[847,853,858],{"type":44,"tag":848,"props":849,"children":850},"th",{},[851],{"type":50,"value":852},"Scenario",{"type":44,"tag":848,"props":854,"children":855},{},[856],{"type":50,"value":857},"Returns",{"type":44,"tag":848,"props":859,"children":860},{},[861],{"type":50,"value":862},"Result",{"type":44,"tag":864,"props":865,"children":866},"tbody",{},[867,890,920],{"type":44,"tag":844,"props":868,"children":869},{},[870,876,885],{"type":44,"tag":871,"props":872,"children":873},"td",{},[874],{"type":50,"value":875},"Valid credentials",{"type":44,"tag":871,"props":877,"children":878},{},[879],{"type":44,"tag":66,"props":880,"children":882},{"className":881},[],[883],{"type":50,"value":884},"(uid, data, nil)",{"type":44,"tag":871,"props":886,"children":887},{},[888],{"type":50,"value":889},"Request authenticated",{"type":44,"tag":844,"props":891,"children":892},{},[893,898,915],{"type":44,"tag":871,"props":894,"children":895},{},[896],{"type":50,"value":897},"Invalid credentials",{"type":44,"tag":871,"props":899,"children":900},{},[901,907,909],{"type":44,"tag":66,"props":902,"children":904},{"className":903},[],[905],{"type":50,"value":906},"(\"\", nil, err)",{"type":50,"value":908}," with ",{"type":44,"tag":66,"props":910,"children":912},{"className":911},[],[913],{"type":50,"value":914},"errs.Unauthenticated",{"type":44,"tag":871,"props":916,"children":917},{},[918],{"type":50,"value":919},"401 response",{"type":44,"tag":844,"props":921,"children":922},{},[923,928,936],{"type":44,"tag":871,"props":924,"children":925},{},[926],{"type":50,"value":927},"Other error",{"type":44,"tag":871,"props":929,"children":930},{},[931],{"type":44,"tag":66,"props":932,"children":934},{"className":933},[],[935],{"type":50,"value":906},{"type":44,"tag":871,"props":937,"children":938},{},[939],{"type":50,"value":940},"Request aborted",{"type":44,"tag":53,"props":942,"children":944},{"id":943},"common-auth-patterns",[945],{"type":50,"value":946},"Common Auth Patterns",{"type":44,"tag":75,"props":948,"children":950},{"id":949},"jwt-token-validation",[951],{"type":50,"value":952},"JWT Token Validation",{"type":44,"tag":82,"props":954,"children":956},{"className":84,"code":955,"language":23,"meta":86,"style":86},"import \"github.com\u002Fgolang-jwt\u002Fjwt\u002Fv5\"\n\nvar secrets struct {\n    JWTSecret string\n}\n\nfunc verifyToken(tokenString string) (*Claims, error) {\n    token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(t *jwt.Token) (interface{}, error) {\n        return []byte(secrets.JWTSecret), nil\n    })\n    if err != nil {\n        return nil, err\n    }\n    \n    claims, ok := token.Claims.(*Claims)\n    if !ok || !token.Valid {\n        return nil, errors.New(\"invalid token\")\n    }\n    \n    return claims, nil\n}\n",[957],{"type":44,"tag":66,"props":958,"children":959},{"__ignoreMap":86},[960,968,975,983,991,998,1005,1013,1021,1029,1037,1044,1052,1059,1066,1074,1082,1090,1097,1104,1112],{"type":44,"tag":92,"props":961,"children":962},{"class":94,"line":95},[963],{"type":44,"tag":92,"props":964,"children":965},{},[966],{"type":50,"value":967},"import \"github.com\u002Fgolang-jwt\u002Fjwt\u002Fv5\"\n",{"type":44,"tag":92,"props":969,"children":970},{"class":94,"line":104},[971],{"type":44,"tag":92,"props":972,"children":973},{"emptyLinePlaceholder":108},[974],{"type":50,"value":111},{"type":44,"tag":92,"props":976,"children":977},{"class":94,"line":114},[978],{"type":44,"tag":92,"props":979,"children":980},{},[981],{"type":50,"value":982},"var secrets struct {\n",{"type":44,"tag":92,"props":984,"children":985},{"class":94,"line":123},[986],{"type":44,"tag":92,"props":987,"children":988},{},[989],{"type":50,"value":990},"    JWTSecret string\n",{"type":44,"tag":92,"props":992,"children":993},{"class":94,"line":28},[994],{"type":44,"tag":92,"props":995,"children":996},{},[997],{"type":50,"value":199},{"type":44,"tag":92,"props":999,"children":1000},{"class":94,"line":140},[1001],{"type":44,"tag":92,"props":1002,"children":1003},{"emptyLinePlaceholder":108},[1004],{"type":50,"value":111},{"type":44,"tag":92,"props":1006,"children":1007},{"class":94,"line":149},[1008],{"type":44,"tag":92,"props":1009,"children":1010},{},[1011],{"type":50,"value":1012},"func verifyToken(tokenString string) (*Claims, error) {\n",{"type":44,"tag":92,"props":1014,"children":1015},{"class":94,"line":158},[1016],{"type":44,"tag":92,"props":1017,"children":1018},{},[1019],{"type":50,"value":1020},"    token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(t *jwt.Token) (interface{}, error) {\n",{"type":44,"tag":92,"props":1022,"children":1023},{"class":94,"line":166},[1024],{"type":44,"tag":92,"props":1025,"children":1026},{},[1027],{"type":50,"value":1028},"        return []byte(secrets.JWTSecret), nil\n",{"type":44,"tag":92,"props":1030,"children":1031},{"class":94,"line":175},[1032],{"type":44,"tag":92,"props":1033,"children":1034},{},[1035],{"type":50,"value":1036},"    })\n",{"type":44,"tag":92,"props":1038,"children":1039},{"class":94,"line":184},[1040],{"type":44,"tag":92,"props":1041,"children":1042},{},[1043],{"type":50,"value":321},{"type":44,"tag":92,"props":1045,"children":1046},{"class":94,"line":193},[1047],{"type":44,"tag":92,"props":1048,"children":1049},{},[1050],{"type":50,"value":1051},"        return nil, err\n",{"type":44,"tag":92,"props":1053,"children":1054},{"class":94,"line":202},[1055],{"type":44,"tag":92,"props":1056,"children":1057},{},[1058],{"type":50,"value":366},{"type":44,"tag":92,"props":1060,"children":1061},{"class":94,"line":210},[1062],{"type":44,"tag":92,"props":1063,"children":1064},{},[1065],{"type":50,"value":304},{"type":44,"tag":92,"props":1067,"children":1068},{"class":94,"line":219},[1069],{"type":44,"tag":92,"props":1070,"children":1071},{},[1072],{"type":50,"value":1073},"    claims, ok := token.Claims.(*Claims)\n",{"type":44,"tag":92,"props":1075,"children":1076},{"class":94,"line":228},[1077],{"type":44,"tag":92,"props":1078,"children":1079},{},[1080],{"type":50,"value":1081},"    if !ok || !token.Valid {\n",{"type":44,"tag":92,"props":1083,"children":1084},{"class":94,"line":237},[1085],{"type":44,"tag":92,"props":1086,"children":1087},{},[1088],{"type":50,"value":1089},"        return nil, errors.New(\"invalid token\")\n",{"type":44,"tag":92,"props":1091,"children":1092},{"class":94,"line":246},[1093],{"type":44,"tag":92,"props":1094,"children":1095},{},[1096],{"type":50,"value":366},{"type":44,"tag":92,"props":1098,"children":1099},{"class":94,"line":255},[1100],{"type":44,"tag":92,"props":1101,"children":1102},{},[1103],{"type":50,"value":304},{"type":44,"tag":92,"props":1105,"children":1106},{"class":94,"line":263},[1107],{"type":44,"tag":92,"props":1108,"children":1109},{},[1110],{"type":50,"value":1111},"    return claims, nil\n",{"type":44,"tag":92,"props":1113,"children":1114},{"class":94,"line":271},[1115],{"type":44,"tag":92,"props":1116,"children":1117},{},[1118],{"type":50,"value":199},{"type":44,"tag":75,"props":1120,"children":1122},{"id":1121},"api-key-authentication",[1123],{"type":50,"value":1124},"API Key Authentication",{"type":44,"tag":82,"props":1126,"children":1128},{"className":84,"code":1127,"language":23,"meta":86,"style":86},"\u002F\u002Fencore:authhandler\nfunc Authenticate(ctx context.Context, params *AuthParams) (auth.UID, *AuthData, error) {\n    apiKey := params.Authorization\n    \n    user, err := db.QueryRow[User](ctx, `\n        SELECT id, email, role FROM users WHERE api_key = $1\n    `, apiKey)\n    if err != nil {\n        return \"\", nil, &errs.Error{\n            Code:    errs.Unauthenticated,\n            Message: \"invalid API key\",\n        }\n    }\n    \n    return auth.UID(user.ID), &AuthData{\n        UserID: user.ID,\n        Email:  user.Email,\n        Role:   user.Role,\n    }, nil\n}\n",[1129],{"type":44,"tag":66,"props":1130,"children":1131},{"__ignoreMap":86},[1132,1139,1146,1154,1161,1169,1177,1185,1192,1199,1206,1214,1221,1228,1235,1243,1251,1259,1267,1274],{"type":44,"tag":92,"props":1133,"children":1134},{"class":94,"line":95},[1135],{"type":44,"tag":92,"props":1136,"children":1137},{},[1138],{"type":50,"value":277},{"type":44,"tag":92,"props":1140,"children":1141},{"class":94,"line":104},[1142],{"type":44,"tag":92,"props":1143,"children":1144},{},[1145],{"type":50,"value":286},{"type":44,"tag":92,"props":1147,"children":1148},{"class":94,"line":114},[1149],{"type":44,"tag":92,"props":1150,"children":1151},{},[1152],{"type":50,"value":1153},"    apiKey := params.Authorization\n",{"type":44,"tag":92,"props":1155,"children":1156},{"class":94,"line":123},[1157],{"type":44,"tag":92,"props":1158,"children":1159},{},[1160],{"type":50,"value":304},{"type":44,"tag":92,"props":1162,"children":1163},{"class":94,"line":28},[1164],{"type":44,"tag":92,"props":1165,"children":1166},{},[1167],{"type":50,"value":1168},"    user, err := db.QueryRow[User](ctx, `\n",{"type":44,"tag":92,"props":1170,"children":1171},{"class":94,"line":140},[1172],{"type":44,"tag":92,"props":1173,"children":1174},{},[1175],{"type":50,"value":1176},"        SELECT id, email, role FROM users WHERE api_key = $1\n",{"type":44,"tag":92,"props":1178,"children":1179},{"class":94,"line":149},[1180],{"type":44,"tag":92,"props":1181,"children":1182},{},[1183],{"type":50,"value":1184},"    `, apiKey)\n",{"type":44,"tag":92,"props":1186,"children":1187},{"class":94,"line":158},[1188],{"type":44,"tag":92,"props":1189,"children":1190},{},[1191],{"type":50,"value":321},{"type":44,"tag":92,"props":1193,"children":1194},{"class":94,"line":166},[1195],{"type":44,"tag":92,"props":1196,"children":1197},{},[1198],{"type":50,"value":330},{"type":44,"tag":92,"props":1200,"children":1201},{"class":94,"line":175},[1202],{"type":44,"tag":92,"props":1203,"children":1204},{},[1205],{"type":50,"value":339},{"type":44,"tag":92,"props":1207,"children":1208},{"class":94,"line":184},[1209],{"type":44,"tag":92,"props":1210,"children":1211},{},[1212],{"type":50,"value":1213},"            Message: \"invalid API key\",\n",{"type":44,"tag":92,"props":1215,"children":1216},{"class":94,"line":193},[1217],{"type":44,"tag":92,"props":1218,"children":1219},{},[1220],{"type":50,"value":357},{"type":44,"tag":92,"props":1222,"children":1223},{"class":94,"line":202},[1224],{"type":44,"tag":92,"props":1225,"children":1226},{},[1227],{"type":50,"value":366},{"type":44,"tag":92,"props":1229,"children":1230},{"class":94,"line":210},[1231],{"type":44,"tag":92,"props":1232,"children":1233},{},[1234],{"type":50,"value":304},{"type":44,"tag":92,"props":1236,"children":1237},{"class":94,"line":219},[1238],{"type":44,"tag":92,"props":1239,"children":1240},{},[1241],{"type":50,"value":1242},"    return auth.UID(user.ID), &AuthData{\n",{"type":44,"tag":92,"props":1244,"children":1245},{"class":94,"line":228},[1246],{"type":44,"tag":92,"props":1247,"children":1248},{},[1249],{"type":50,"value":1250},"        UserID: user.ID,\n",{"type":44,"tag":92,"props":1252,"children":1253},{"class":94,"line":237},[1254],{"type":44,"tag":92,"props":1255,"children":1256},{},[1257],{"type":50,"value":1258},"        Email:  user.Email,\n",{"type":44,"tag":92,"props":1260,"children":1261},{"class":94,"line":246},[1262],{"type":44,"tag":92,"props":1263,"children":1264},{},[1265],{"type":50,"value":1266},"        Role:   user.Role,\n",{"type":44,"tag":92,"props":1268,"children":1269},{"class":94,"line":255},[1270],{"type":44,"tag":92,"props":1271,"children":1272},{},[1273],{"type":50,"value":419},{"type":44,"tag":92,"props":1275,"children":1276},{"class":94,"line":263},[1277],{"type":44,"tag":92,"props":1278,"children":1279},{},[1280],{"type":50,"value":199},{"type":44,"tag":75,"props":1282,"children":1284},{"id":1283},"cookie-based-auth",[1285],{"type":50,"value":1286},"Cookie-Based Auth",{"type":44,"tag":82,"props":1288,"children":1290},{"className":84,"code":1289,"language":23,"meta":86,"style":86},"type AuthParams struct {\n    Cookie string `header:\"Cookie\"`\n}\n\n\u002F\u002Fencore:authhandler\nfunc Authenticate(ctx context.Context, params *AuthParams) (auth.UID, *AuthData, error) {\n    sessionID := parseCookie(params.Cookie, \"session\")\n    if sessionID == \"\" {\n        return \"\", nil, &errs.Error{\n            Code:    errs.Unauthenticated,\n            Message: \"no session\",\n        }\n    }\n\n    session, err := getSession(ctx, sessionID)\n    if err != nil || session.ExpiresAt.Before(time.Now()) {\n        return \"\", nil, &errs.Error{\n            Code:    errs.Unauthenticated,\n            Message: \"session expired\",\n        }\n    }\n\n    return auth.UID(session.UserID), &AuthData{\n        UserID: session.UserID,\n        Email:  session.Email,\n        Role:   session.Role,\n    }, nil\n}\n",[1291],{"type":44,"tag":66,"props":1292,"children":1293},{"__ignoreMap":86},[1294,1301,1309,1316,1323,1330,1337,1345,1353,1360,1367,1375,1382,1389,1396,1404,1412,1419,1426,1434,1441,1448,1455,1463,1471,1479,1487,1494],{"type":44,"tag":92,"props":1295,"children":1296},{"class":94,"line":95},[1297],{"type":44,"tag":92,"props":1298,"children":1299},{},[1300],{"type":50,"value":181},{"type":44,"tag":92,"props":1302,"children":1303},{"class":94,"line":104},[1304],{"type":44,"tag":92,"props":1305,"children":1306},{},[1307],{"type":50,"value":1308},"    Cookie string `header:\"Cookie\"`\n",{"type":44,"tag":92,"props":1310,"children":1311},{"class":94,"line":114},[1312],{"type":44,"tag":92,"props":1313,"children":1314},{},[1315],{"type":50,"value":199},{"type":44,"tag":92,"props":1317,"children":1318},{"class":94,"line":123},[1319],{"type":44,"tag":92,"props":1320,"children":1321},{"emptyLinePlaceholder":108},[1322],{"type":50,"value":111},{"type":44,"tag":92,"props":1324,"children":1325},{"class":94,"line":28},[1326],{"type":44,"tag":92,"props":1327,"children":1328},{},[1329],{"type":50,"value":277},{"type":44,"tag":92,"props":1331,"children":1332},{"class":94,"line":140},[1333],{"type":44,"tag":92,"props":1334,"children":1335},{},[1336],{"type":50,"value":286},{"type":44,"tag":92,"props":1338,"children":1339},{"class":94,"line":149},[1340],{"type":44,"tag":92,"props":1341,"children":1342},{},[1343],{"type":50,"value":1344},"    sessionID := parseCookie(params.Cookie, \"session\")\n",{"type":44,"tag":92,"props":1346,"children":1347},{"class":94,"line":158},[1348],{"type":44,"tag":92,"props":1349,"children":1350},{},[1351],{"type":50,"value":1352},"    if sessionID == \"\" {\n",{"type":44,"tag":92,"props":1354,"children":1355},{"class":94,"line":166},[1356],{"type":44,"tag":92,"props":1357,"children":1358},{},[1359],{"type":50,"value":330},{"type":44,"tag":92,"props":1361,"children":1362},{"class":94,"line":175},[1363],{"type":44,"tag":92,"props":1364,"children":1365},{},[1366],{"type":50,"value":339},{"type":44,"tag":92,"props":1368,"children":1369},{"class":94,"line":184},[1370],{"type":44,"tag":92,"props":1371,"children":1372},{},[1373],{"type":50,"value":1374},"            Message: \"no session\",\n",{"type":44,"tag":92,"props":1376,"children":1377},{"class":94,"line":193},[1378],{"type":44,"tag":92,"props":1379,"children":1380},{},[1381],{"type":50,"value":357},{"type":44,"tag":92,"props":1383,"children":1384},{"class":94,"line":202},[1385],{"type":44,"tag":92,"props":1386,"children":1387},{},[1388],{"type":50,"value":366},{"type":44,"tag":92,"props":1390,"children":1391},{"class":94,"line":210},[1392],{"type":44,"tag":92,"props":1393,"children":1394},{"emptyLinePlaceholder":108},[1395],{"type":50,"value":111},{"type":44,"tag":92,"props":1397,"children":1398},{"class":94,"line":219},[1399],{"type":44,"tag":92,"props":1400,"children":1401},{},[1402],{"type":50,"value":1403},"    session, err := getSession(ctx, sessionID)\n",{"type":44,"tag":92,"props":1405,"children":1406},{"class":94,"line":228},[1407],{"type":44,"tag":92,"props":1408,"children":1409},{},[1410],{"type":50,"value":1411},"    if err != nil || session.ExpiresAt.Before(time.Now()) {\n",{"type":44,"tag":92,"props":1413,"children":1414},{"class":94,"line":237},[1415],{"type":44,"tag":92,"props":1416,"children":1417},{},[1418],{"type":50,"value":330},{"type":44,"tag":92,"props":1420,"children":1421},{"class":94,"line":246},[1422],{"type":44,"tag":92,"props":1423,"children":1424},{},[1425],{"type":50,"value":339},{"type":44,"tag":92,"props":1427,"children":1428},{"class":94,"line":255},[1429],{"type":44,"tag":92,"props":1430,"children":1431},{},[1432],{"type":50,"value":1433},"            Message: \"session expired\",\n",{"type":44,"tag":92,"props":1435,"children":1436},{"class":94,"line":263},[1437],{"type":44,"tag":92,"props":1438,"children":1439},{},[1440],{"type":50,"value":357},{"type":44,"tag":92,"props":1442,"children":1443},{"class":94,"line":271},[1444],{"type":44,"tag":92,"props":1445,"children":1446},{},[1447],{"type":50,"value":366},{"type":44,"tag":92,"props":1449,"children":1450},{"class":94,"line":280},[1451],{"type":44,"tag":92,"props":1452,"children":1453},{"emptyLinePlaceholder":108},[1454],{"type":50,"value":111},{"type":44,"tag":92,"props":1456,"children":1457},{"class":94,"line":289},[1458],{"type":44,"tag":92,"props":1459,"children":1460},{},[1461],{"type":50,"value":1462},"    return auth.UID(session.UserID), &AuthData{\n",{"type":44,"tag":92,"props":1464,"children":1465},{"class":94,"line":298},[1466],{"type":44,"tag":92,"props":1467,"children":1468},{},[1469],{"type":50,"value":1470},"        UserID: session.UserID,\n",{"type":44,"tag":92,"props":1472,"children":1473},{"class":94,"line":307},[1474],{"type":44,"tag":92,"props":1475,"children":1476},{},[1477],{"type":50,"value":1478},"        Email:  session.Email,\n",{"type":44,"tag":92,"props":1480,"children":1481},{"class":94,"line":24},[1482],{"type":44,"tag":92,"props":1483,"children":1484},{},[1485],{"type":50,"value":1486},"        Role:   session.Role,\n",{"type":44,"tag":92,"props":1488,"children":1489},{"class":94,"line":324},[1490],{"type":44,"tag":92,"props":1491,"children":1492},{},[1493],{"type":50,"value":419},{"type":44,"tag":92,"props":1495,"children":1496},{"class":94,"line":333},[1497],{"type":44,"tag":92,"props":1498,"children":1499},{},[1500],{"type":50,"value":199},{"type":44,"tag":75,"props":1502,"children":1504},{"id":1503},"multi-source-auth-cookie-header-query",[1505],{"type":50,"value":1506},"Multi-Source Auth (Cookie + Header + Query)",{"type":44,"tag":60,"props":1508,"children":1509},{},[1510],{"type":50,"value":1511},"Auth params can extract data from multiple sources:",{"type":44,"tag":82,"props":1513,"children":1515},{"className":84,"code":1514,"language":23,"meta":86,"style":86},"import \"net\u002Fhttp\"\n\ntype AuthParams struct {\n    SessionCookie *http.Cookie `cookie:\"session\"`       \u002F\u002F From cookie\n    Authorization string       `header:\"Authorization\"` \u002F\u002F From header\n    ClientID      string       `query:\"client_id\"`      \u002F\u002F From query string\n}\n\n\u002F\u002Fencore:authhandler\nfunc Authenticate(ctx context.Context, params *AuthParams) (auth.UID, *AuthData, error) {\n    \u002F\u002F Try session cookie first\n    if params.SessionCookie != nil {\n        return authenticateWithSession(ctx, params.SessionCookie.Value)\n    }\n\n    \u002F\u002F Fall back to Authorization header\n    if params.Authorization != \"\" {\n        return authenticateWithToken(ctx, params.Authorization)\n    }\n\n    return \"\", nil, &errs.Error{\n        Code:    errs.Unauthenticated,\n        Message: \"no credentials provided\",\n    }\n}\n",[1516],{"type":44,"tag":66,"props":1517,"children":1518},{"__ignoreMap":86},[1519,1527,1534,1541,1549,1557,1565,1572,1579,1586,1593,1601,1609,1617,1624,1631,1639,1647,1655,1662,1669,1677,1685,1693,1700],{"type":44,"tag":92,"props":1520,"children":1521},{"class":94,"line":95},[1522],{"type":44,"tag":92,"props":1523,"children":1524},{},[1525],{"type":50,"value":1526},"import \"net\u002Fhttp\"\n",{"type":44,"tag":92,"props":1528,"children":1529},{"class":94,"line":104},[1530],{"type":44,"tag":92,"props":1531,"children":1532},{"emptyLinePlaceholder":108},[1533],{"type":50,"value":111},{"type":44,"tag":92,"props":1535,"children":1536},{"class":94,"line":114},[1537],{"type":44,"tag":92,"props":1538,"children":1539},{},[1540],{"type":50,"value":181},{"type":44,"tag":92,"props":1542,"children":1543},{"class":94,"line":123},[1544],{"type":44,"tag":92,"props":1545,"children":1546},{},[1547],{"type":50,"value":1548},"    SessionCookie *http.Cookie `cookie:\"session\"`       \u002F\u002F From cookie\n",{"type":44,"tag":92,"props":1550,"children":1551},{"class":94,"line":28},[1552],{"type":44,"tag":92,"props":1553,"children":1554},{},[1555],{"type":50,"value":1556},"    Authorization string       `header:\"Authorization\"` \u002F\u002F From header\n",{"type":44,"tag":92,"props":1558,"children":1559},{"class":94,"line":140},[1560],{"type":44,"tag":92,"props":1561,"children":1562},{},[1563],{"type":50,"value":1564},"    ClientID      string       `query:\"client_id\"`      \u002F\u002F From query string\n",{"type":44,"tag":92,"props":1566,"children":1567},{"class":94,"line":149},[1568],{"type":44,"tag":92,"props":1569,"children":1570},{},[1571],{"type":50,"value":199},{"type":44,"tag":92,"props":1573,"children":1574},{"class":94,"line":158},[1575],{"type":44,"tag":92,"props":1576,"children":1577},{"emptyLinePlaceholder":108},[1578],{"type":50,"value":111},{"type":44,"tag":92,"props":1580,"children":1581},{"class":94,"line":166},[1582],{"type":44,"tag":92,"props":1583,"children":1584},{},[1585],{"type":50,"value":277},{"type":44,"tag":92,"props":1587,"children":1588},{"class":94,"line":175},[1589],{"type":44,"tag":92,"props":1590,"children":1591},{},[1592],{"type":50,"value":286},{"type":44,"tag":92,"props":1594,"children":1595},{"class":94,"line":184},[1596],{"type":44,"tag":92,"props":1597,"children":1598},{},[1599],{"type":50,"value":1600},"    \u002F\u002F Try session cookie first\n",{"type":44,"tag":92,"props":1602,"children":1603},{"class":94,"line":193},[1604],{"type":44,"tag":92,"props":1605,"children":1606},{},[1607],{"type":50,"value":1608},"    if params.SessionCookie != nil {\n",{"type":44,"tag":92,"props":1610,"children":1611},{"class":94,"line":202},[1612],{"type":44,"tag":92,"props":1613,"children":1614},{},[1615],{"type":50,"value":1616},"        return authenticateWithSession(ctx, params.SessionCookie.Value)\n",{"type":44,"tag":92,"props":1618,"children":1619},{"class":94,"line":210},[1620],{"type":44,"tag":92,"props":1621,"children":1622},{},[1623],{"type":50,"value":366},{"type":44,"tag":92,"props":1625,"children":1626},{"class":94,"line":219},[1627],{"type":44,"tag":92,"props":1628,"children":1629},{"emptyLinePlaceholder":108},[1630],{"type":50,"value":111},{"type":44,"tag":92,"props":1632,"children":1633},{"class":94,"line":228},[1634],{"type":44,"tag":92,"props":1635,"children":1636},{},[1637],{"type":50,"value":1638},"    \u002F\u002F Fall back to Authorization header\n",{"type":44,"tag":92,"props":1640,"children":1641},{"class":94,"line":237},[1642],{"type":44,"tag":92,"props":1643,"children":1644},{},[1645],{"type":50,"value":1646},"    if params.Authorization != \"\" {\n",{"type":44,"tag":92,"props":1648,"children":1649},{"class":94,"line":246},[1650],{"type":44,"tag":92,"props":1651,"children":1652},{},[1653],{"type":50,"value":1654},"        return authenticateWithToken(ctx, params.Authorization)\n",{"type":44,"tag":92,"props":1656,"children":1657},{"class":94,"line":255},[1658],{"type":44,"tag":92,"props":1659,"children":1660},{},[1661],{"type":50,"value":366},{"type":44,"tag":92,"props":1663,"children":1664},{"class":94,"line":263},[1665],{"type":44,"tag":92,"props":1666,"children":1667},{"emptyLinePlaceholder":108},[1668],{"type":50,"value":111},{"type":44,"tag":92,"props":1670,"children":1671},{"class":94,"line":271},[1672],{"type":44,"tag":92,"props":1673,"children":1674},{},[1675],{"type":50,"value":1676},"    return \"\", nil, &errs.Error{\n",{"type":44,"tag":92,"props":1678,"children":1679},{"class":94,"line":280},[1680],{"type":44,"tag":92,"props":1681,"children":1682},{},[1683],{"type":50,"value":1684},"        Code:    errs.Unauthenticated,\n",{"type":44,"tag":92,"props":1686,"children":1687},{"class":94,"line":289},[1688],{"type":44,"tag":92,"props":1689,"children":1690},{},[1691],{"type":50,"value":1692},"        Message: \"no credentials provided\",\n",{"type":44,"tag":92,"props":1694,"children":1695},{"class":94,"line":298},[1696],{"type":44,"tag":92,"props":1697,"children":1698},{},[1699],{"type":50,"value":366},{"type":44,"tag":92,"props":1701,"children":1702},{"class":94,"line":307},[1703],{"type":44,"tag":92,"props":1704,"children":1705},{},[1706],{"type":50,"value":199},{"type":44,"tag":53,"props":1708,"children":1710},{"id":1709},"service-to-service-auth",[1711],{"type":50,"value":1712},"Service-to-Service Auth",{"type":44,"tag":60,"props":1714,"children":1715},{},[1716],{"type":50,"value":1717},"Auth data automatically propagates in internal service calls:",{"type":44,"tag":82,"props":1719,"children":1721},{"className":84,"code":1720,"language":23,"meta":86,"style":86},"package order\n\nimport (\n    \"context\"\n    \"myapp\u002Fuser\"  \u002F\u002F Import the user service\n)\n\n\u002F\u002Fencore:api auth method=GET path=\u002Forders\u002F:id\nfunc GetOrderWithUser(ctx context.Context, params *GetOrderParams) (*OrderWithUser, error) {\n    order, err := getOrder(ctx, params.ID)\n    if err != nil {\n        return nil, err\n    }\n    \n    \u002F\u002F Auth is automatically propagated to this call\n    profile, err := user.GetProfile(ctx)\n    if err != nil {\n        return nil, err\n    }\n    \n    return &OrderWithUser{Order: order, User: profile}, nil\n}\n",[1722],{"type":44,"tag":66,"props":1723,"children":1724},{"__ignoreMap":86},[1725,1733,1740,1747,1754,1767,1774,1781,1789,1797,1805,1812,1819,1826,1833,1841,1849,1856,1863,1870,1877,1885],{"type":44,"tag":92,"props":1726,"children":1727},{"class":94,"line":95},[1728],{"type":44,"tag":92,"props":1729,"children":1730},{},[1731],{"type":50,"value":1732},"package order\n",{"type":44,"tag":92,"props":1734,"children":1735},{"class":94,"line":104},[1736],{"type":44,"tag":92,"props":1737,"children":1738},{"emptyLinePlaceholder":108},[1739],{"type":50,"value":111},{"type":44,"tag":92,"props":1741,"children":1742},{"class":94,"line":114},[1743],{"type":44,"tag":92,"props":1744,"children":1745},{},[1746],{"type":50,"value":120},{"type":44,"tag":92,"props":1748,"children":1749},{"class":94,"line":123},[1750],{"type":44,"tag":92,"props":1751,"children":1752},{},[1753],{"type":50,"value":129},{"type":44,"tag":92,"props":1755,"children":1756},{"class":94,"line":28},[1757,1762],{"type":44,"tag":92,"props":1758,"children":1759},{},[1760],{"type":50,"value":1761},"    \"myapp\u002Fuser\"",{"type":44,"tag":92,"props":1763,"children":1764},{},[1765],{"type":50,"value":1766},"  \u002F\u002F Import the user service\n",{"type":44,"tag":92,"props":1768,"children":1769},{"class":94,"line":140},[1770],{"type":44,"tag":92,"props":1771,"children":1772},{},[1773],{"type":50,"value":155},{"type":44,"tag":92,"props":1775,"children":1776},{"class":94,"line":149},[1777],{"type":44,"tag":92,"props":1778,"children":1779},{"emptyLinePlaceholder":108},[1780],{"type":50,"value":111},{"type":44,"tag":92,"props":1782,"children":1783},{"class":94,"line":158},[1784],{"type":44,"tag":92,"props":1785,"children":1786},{},[1787],{"type":50,"value":1788},"\u002F\u002Fencore:api auth method=GET path=\u002Forders\u002F:id\n",{"type":44,"tag":92,"props":1790,"children":1791},{"class":94,"line":166},[1792],{"type":44,"tag":92,"props":1793,"children":1794},{},[1795],{"type":50,"value":1796},"func GetOrderWithUser(ctx context.Context, params *GetOrderParams) (*OrderWithUser, error) {\n",{"type":44,"tag":92,"props":1798,"children":1799},{"class":94,"line":175},[1800],{"type":44,"tag":92,"props":1801,"children":1802},{},[1803],{"type":50,"value":1804},"    order, err := getOrder(ctx, params.ID)\n",{"type":44,"tag":92,"props":1806,"children":1807},{"class":94,"line":184},[1808],{"type":44,"tag":92,"props":1809,"children":1810},{},[1811],{"type":50,"value":321},{"type":44,"tag":92,"props":1813,"children":1814},{"class":94,"line":193},[1815],{"type":44,"tag":92,"props":1816,"children":1817},{},[1818],{"type":50,"value":1051},{"type":44,"tag":92,"props":1820,"children":1821},{"class":94,"line":202},[1822],{"type":44,"tag":92,"props":1823,"children":1824},{},[1825],{"type":50,"value":366},{"type":44,"tag":92,"props":1827,"children":1828},{"class":94,"line":210},[1829],{"type":44,"tag":92,"props":1830,"children":1831},{},[1832],{"type":50,"value":304},{"type":44,"tag":92,"props":1834,"children":1835},{"class":94,"line":219},[1836],{"type":44,"tag":92,"props":1837,"children":1838},{},[1839],{"type":50,"value":1840},"    \u002F\u002F Auth is automatically propagated to this call\n",{"type":44,"tag":92,"props":1842,"children":1843},{"class":94,"line":228},[1844],{"type":44,"tag":92,"props":1845,"children":1846},{},[1847],{"type":50,"value":1848},"    profile, err := user.GetProfile(ctx)\n",{"type":44,"tag":92,"props":1850,"children":1851},{"class":94,"line":237},[1852],{"type":44,"tag":92,"props":1853,"children":1854},{},[1855],{"type":50,"value":321},{"type":44,"tag":92,"props":1857,"children":1858},{"class":94,"line":246},[1859],{"type":44,"tag":92,"props":1860,"children":1861},{},[1862],{"type":50,"value":1051},{"type":44,"tag":92,"props":1864,"children":1865},{"class":94,"line":255},[1866],{"type":44,"tag":92,"props":1867,"children":1868},{},[1869],{"type":50,"value":366},{"type":44,"tag":92,"props":1871,"children":1872},{"class":94,"line":263},[1873],{"type":44,"tag":92,"props":1874,"children":1875},{},[1876],{"type":50,"value":304},{"type":44,"tag":92,"props":1878,"children":1879},{"class":94,"line":271},[1880],{"type":44,"tag":92,"props":1881,"children":1882},{},[1883],{"type":50,"value":1884},"    return &OrderWithUser{Order: order, User: profile}, nil\n",{"type":44,"tag":92,"props":1886,"children":1887},{"class":94,"line":280},[1888],{"type":44,"tag":92,"props":1889,"children":1890},{},[1891],{"type":50,"value":199},{"type":44,"tag":53,"props":1893,"children":1895},{"id":1894},"testing-with-auth",[1896],{"type":50,"value":1897},"Testing with Auth",{"type":44,"tag":60,"props":1899,"children":1900},{},[1901,1903,1909],{"type":50,"value":1902},"Override auth data in tests using ",{"type":44,"tag":66,"props":1904,"children":1906},{"className":1905},[],[1907],{"type":50,"value":1908},"auth.WithContext",{"type":50,"value":1910},":",{"type":44,"tag":82,"props":1912,"children":1914},{"className":84,"code":1913,"language":23,"meta":86,"style":86},"package user_test\n\nimport (\n    \"context\"\n    \"testing\"\n\n    \"encore.dev\u002Fbeta\u002Fauth\"\n    myauth \"myapp\u002Fauth\"\n    \"myapp\u002Fuser\"\n)\n\nfunc TestGetProfile(t *testing.T) {\n    \u002F\u002F Create a context with auth data\n    ctx := auth.WithContext(\n        context.Background(),\n        auth.UID(\"test-user-123\"),\n        &myauth.AuthData{\n            UserID: \"test-user-123\",\n            Email:  \"test@example.com\",\n            Role:   \"user\",\n        },\n    )\n\n    \u002F\u002F Call the endpoint with the authenticated context\n    profile, err := user.GetProfile(ctx)\n    if err != nil {\n        t.Fatalf(\"unexpected error: %v\", err)\n    }\n\n    if profile.Email != \"test@example.com\" {\n        t.Errorf(\"expected test@example.com, got %s\", profile.Email)\n    }\n}\n",[1915],{"type":44,"tag":66,"props":1916,"children":1917},{"__ignoreMap":86},[1918,1926,1933,1940,1947,1955,1962,1969,1977,1985,1992,1999,2007,2015,2023,2031,2039,2047,2055,2063,2071,2079,2087,2094,2102,2109,2116,2124,2131,2138,2146,2154,2161],{"type":44,"tag":92,"props":1919,"children":1920},{"class":94,"line":95},[1921],{"type":44,"tag":92,"props":1922,"children":1923},{},[1924],{"type":50,"value":1925},"package user_test\n",{"type":44,"tag":92,"props":1927,"children":1928},{"class":94,"line":104},[1929],{"type":44,"tag":92,"props":1930,"children":1931},{"emptyLinePlaceholder":108},[1932],{"type":50,"value":111},{"type":44,"tag":92,"props":1934,"children":1935},{"class":94,"line":114},[1936],{"type":44,"tag":92,"props":1937,"children":1938},{},[1939],{"type":50,"value":120},{"type":44,"tag":92,"props":1941,"children":1942},{"class":94,"line":123},[1943],{"type":44,"tag":92,"props":1944,"children":1945},{},[1946],{"type":50,"value":129},{"type":44,"tag":92,"props":1948,"children":1949},{"class":94,"line":28},[1950],{"type":44,"tag":92,"props":1951,"children":1952},{},[1953],{"type":50,"value":1954},"    \"testing\"\n",{"type":44,"tag":92,"props":1956,"children":1957},{"class":94,"line":140},[1958],{"type":44,"tag":92,"props":1959,"children":1960},{"emptyLinePlaceholder":108},[1961],{"type":50,"value":111},{"type":44,"tag":92,"props":1963,"children":1964},{"class":94,"line":149},[1965],{"type":44,"tag":92,"props":1966,"children":1967},{},[1968],{"type":50,"value":137},{"type":44,"tag":92,"props":1970,"children":1971},{"class":94,"line":158},[1972],{"type":44,"tag":92,"props":1973,"children":1974},{},[1975],{"type":50,"value":1976},"    myauth \"myapp\u002Fauth\"\n",{"type":44,"tag":92,"props":1978,"children":1979},{"class":94,"line":166},[1980],{"type":44,"tag":92,"props":1981,"children":1982},{},[1983],{"type":50,"value":1984},"    \"myapp\u002Fuser\"\n",{"type":44,"tag":92,"props":1986,"children":1987},{"class":94,"line":175},[1988],{"type":44,"tag":92,"props":1989,"children":1990},{},[1991],{"type":50,"value":155},{"type":44,"tag":92,"props":1993,"children":1994},{"class":94,"line":184},[1995],{"type":44,"tag":92,"props":1996,"children":1997},{"emptyLinePlaceholder":108},[1998],{"type":50,"value":111},{"type":44,"tag":92,"props":2000,"children":2001},{"class":94,"line":193},[2002],{"type":44,"tag":92,"props":2003,"children":2004},{},[2005],{"type":50,"value":2006},"func TestGetProfile(t *testing.T) {\n",{"type":44,"tag":92,"props":2008,"children":2009},{"class":94,"line":202},[2010],{"type":44,"tag":92,"props":2011,"children":2012},{},[2013],{"type":50,"value":2014},"    \u002F\u002F Create a context with auth data\n",{"type":44,"tag":92,"props":2016,"children":2017},{"class":94,"line":210},[2018],{"type":44,"tag":92,"props":2019,"children":2020},{},[2021],{"type":50,"value":2022},"    ctx := auth.WithContext(\n",{"type":44,"tag":92,"props":2024,"children":2025},{"class":94,"line":219},[2026],{"type":44,"tag":92,"props":2027,"children":2028},{},[2029],{"type":50,"value":2030},"        context.Background(),\n",{"type":44,"tag":92,"props":2032,"children":2033},{"class":94,"line":228},[2034],{"type":44,"tag":92,"props":2035,"children":2036},{},[2037],{"type":50,"value":2038},"        auth.UID(\"test-user-123\"),\n",{"type":44,"tag":92,"props":2040,"children":2041},{"class":94,"line":237},[2042],{"type":44,"tag":92,"props":2043,"children":2044},{},[2045],{"type":50,"value":2046},"        &myauth.AuthData{\n",{"type":44,"tag":92,"props":2048,"children":2049},{"class":94,"line":246},[2050],{"type":44,"tag":92,"props":2051,"children":2052},{},[2053],{"type":50,"value":2054},"            UserID: \"test-user-123\",\n",{"type":44,"tag":92,"props":2056,"children":2057},{"class":94,"line":255},[2058],{"type":44,"tag":92,"props":2059,"children":2060},{},[2061],{"type":50,"value":2062},"            Email:  \"test@example.com\",\n",{"type":44,"tag":92,"props":2064,"children":2065},{"class":94,"line":263},[2066],{"type":44,"tag":92,"props":2067,"children":2068},{},[2069],{"type":50,"value":2070},"            Role:   \"user\",\n",{"type":44,"tag":92,"props":2072,"children":2073},{"class":94,"line":271},[2074],{"type":44,"tag":92,"props":2075,"children":2076},{},[2077],{"type":50,"value":2078},"        },\n",{"type":44,"tag":92,"props":2080,"children":2081},{"class":94,"line":280},[2082],{"type":44,"tag":92,"props":2083,"children":2084},{},[2085],{"type":50,"value":2086},"    )\n",{"type":44,"tag":92,"props":2088,"children":2089},{"class":94,"line":289},[2090],{"type":44,"tag":92,"props":2091,"children":2092},{"emptyLinePlaceholder":108},[2093],{"type":50,"value":111},{"type":44,"tag":92,"props":2095,"children":2096},{"class":94,"line":298},[2097],{"type":44,"tag":92,"props":2098,"children":2099},{},[2100],{"type":50,"value":2101},"    \u002F\u002F Call the endpoint with the authenticated context\n",{"type":44,"tag":92,"props":2103,"children":2104},{"class":94,"line":307},[2105],{"type":44,"tag":92,"props":2106,"children":2107},{},[2108],{"type":50,"value":1848},{"type":44,"tag":92,"props":2110,"children":2111},{"class":94,"line":24},[2112],{"type":44,"tag":92,"props":2113,"children":2114},{},[2115],{"type":50,"value":321},{"type":44,"tag":92,"props":2117,"children":2118},{"class":94,"line":324},[2119],{"type":44,"tag":92,"props":2120,"children":2121},{},[2122],{"type":50,"value":2123},"        t.Fatalf(\"unexpected error: %v\", err)\n",{"type":44,"tag":92,"props":2125,"children":2126},{"class":94,"line":333},[2127],{"type":44,"tag":92,"props":2128,"children":2129},{},[2130],{"type":50,"value":366},{"type":44,"tag":92,"props":2132,"children":2133},{"class":94,"line":342},[2134],{"type":44,"tag":92,"props":2135,"children":2136},{"emptyLinePlaceholder":108},[2137],{"type":50,"value":111},{"type":44,"tag":92,"props":2139,"children":2140},{"class":94,"line":351},[2141],{"type":44,"tag":92,"props":2142,"children":2143},{},[2144],{"type":50,"value":2145},"    if profile.Email != \"test@example.com\" {\n",{"type":44,"tag":92,"props":2147,"children":2148},{"class":94,"line":360},[2149],{"type":44,"tag":92,"props":2150,"children":2151},{},[2152],{"type":50,"value":2153},"        t.Errorf(\"expected test@example.com, got %s\", profile.Email)\n",{"type":44,"tag":92,"props":2155,"children":2156},{"class":94,"line":369},[2157],{"type":44,"tag":92,"props":2158,"children":2159},{},[2160],{"type":50,"value":366},{"type":44,"tag":92,"props":2162,"children":2163},{"class":94,"line":377},[2164],{"type":44,"tag":92,"props":2165,"children":2166},{},[2167],{"type":50,"value":199},{"type":44,"tag":53,"props":2169,"children":2171},{"id":2170},"guidelines",[2172],{"type":50,"value":2173},"Guidelines",{"type":44,"tag":2175,"props":2176,"children":2177},"ul",{},[2178,2190,2202,2215,2228,2240,2245,2257],{"type":44,"tag":771,"props":2179,"children":2180},{},[2181,2183,2188],{"type":50,"value":2182},"Only one ",{"type":44,"tag":66,"props":2184,"children":2186},{"className":2185},[],[2187],{"type":50,"value":71},{"type":50,"value":2189}," per application",{"type":44,"tag":771,"props":2191,"children":2192},{},[2193,2194,2200],{"type":50,"value":800},{"type":44,"tag":66,"props":2195,"children":2197},{"className":2196},[],[2198],{"type":50,"value":2199},"auth.UID",{"type":50,"value":2201}," as the first return value (user identifier)",{"type":44,"tag":771,"props":2203,"children":2204},{},[2205,2207,2213],{"type":50,"value":2206},"Return your custom ",{"type":44,"tag":66,"props":2208,"children":2210},{"className":2209},[],[2211],{"type":50,"value":2212},"AuthData",{"type":50,"value":2214}," struct as second value",{"type":44,"tag":771,"props":2216,"children":2217},{},[2218,2220,2226],{"type":50,"value":2219},"Use ",{"type":44,"tag":66,"props":2221,"children":2223},{"className":2222},[],[2224],{"type":50,"value":2225},"auth.UserID()",{"type":50,"value":2227}," to get the authenticated user ID",{"type":44,"tag":771,"props":2229,"children":2230},{},[2231,2232,2238],{"type":50,"value":2219},{"type":44,"tag":66,"props":2233,"children":2235},{"className":2234},[],[2236],{"type":50,"value":2237},"auth.Data()",{"type":50,"value":2239}," and type assert to get full auth data",{"type":44,"tag":771,"props":2241,"children":2242},{},[2243],{"type":50,"value":2244},"Auth propagates automatically in service-to-service calls",{"type":44,"tag":771,"props":2246,"children":2247},{},[2248,2249,2255],{"type":50,"value":2219},{"type":44,"tag":66,"props":2250,"children":2252},{"className":2251},[],[2253],{"type":50,"value":2254},"auth.WithContext()",{"type":50,"value":2256}," to override auth in tests",{"type":44,"tag":771,"props":2258,"children":2259},{},[2260],{"type":50,"value":2261},"Keep auth handlers fast - they run on every authenticated request",{"type":44,"tag":2263,"props":2264,"children":2265},"style",{},[2266],{"type":50,"value":2267},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"items":2269,"total":333},[2270,2284,2294,2310,2326,2338,2354,2369,2386,2403,2413,2420],{"slug":2271,"name":2271,"fn":2272,"description":2273,"org":2274,"tags":2275,"stars":24,"repoUrl":25,"updatedAt":2283},"encore-api","build type-safe APIs with Encore","Define typed API endpoints in Encore.ts using `api(...)` from `encore.dev\u002Fapi`. Covers typed request\u002Fresponse interfaces, path\u002Fquery\u002Fheader\u002Fcookie params, request validation, and `APIError`. For raw endpoints (`api.raw()`) and inbound webhooks, use `encore-webhook` instead.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2276,2279,2280],{"name":2277,"slug":2278,"type":16},"API Development","api-development",{"name":9,"slug":8,"type":16},{"name":2281,"slug":2282,"type":16},"TypeScript","typescript","2026-04-06T18:09:46.044101",{"slug":2285,"name":2285,"fn":2286,"description":2287,"org":2288,"tags":2289,"stars":24,"repoUrl":25,"updatedAt":2293},"encore-auth","implement Encore.ts authentication","Protect Encore.ts endpoints with authentication and authorize callers. Covers `authHandler`, `Gateway`, `getAuthData`, and `auth: true`.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2290,2291,2292],{"name":18,"slug":19,"type":16},{"name":9,"slug":8,"type":16},{"name":2281,"slug":2282,"type":16},"2026-04-06T18:09:47.336322",{"slug":2295,"name":2295,"fn":2296,"description":2297,"org":2298,"tags":2299,"stars":24,"repoUrl":25,"updatedAt":2309},"encore-bucket","store files in Encore.ts buckets","Store unstructured files in Encore.ts using `Bucket` from `encore.dev\u002Fstorage\u002Fobjects` — uploads, images, documents, blobs.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2300,2301,2302,2305,2308],{"name":14,"slug":15,"type":16},{"name":9,"slug":8,"type":16},{"name":2303,"slug":2304,"type":16},"File Storage","file-storage",{"name":2306,"slug":2307,"type":16},"File Uploads","file-uploads",{"name":2281,"slug":2282,"type":16},"2026-05-16T05:59:52.813772",{"slug":2311,"name":2311,"fn":2312,"description":2313,"org":2314,"tags":2315,"stars":24,"repoUrl":25,"updatedAt":2325},"encore-cache","cache data in Redis with Encore.ts","Cache data in Redis from Encore.ts using `CacheCluster` and typed keyspaces from `encore.dev\u002Fstorage\u002Fcache`. Type-safe key\u002Fvalue access with TTLs, atomic increments, and per-keyspace data shapes.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2316,2317,2320,2321,2324],{"name":14,"slug":15,"type":16},{"name":2318,"slug":2319,"type":16},"Caching","caching",{"name":9,"slug":8,"type":16},{"name":2322,"slug":2323,"type":16},"Redis","redis",{"name":2281,"slug":2282,"type":16},"2026-05-16T05:59:56.808328",{"slug":2327,"name":2327,"fn":2328,"description":2329,"org":2330,"tags":2331,"stars":24,"repoUrl":25,"updatedAt":2337},"encore-code-review","review Encore.ts code","Review existing Encore.ts code for best practices and common anti-patterns.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2332,2335,2336],{"name":2333,"slug":2334,"type":16},"Code Review","code-review",{"name":9,"slug":8,"type":16},{"name":2281,"slug":2282,"type":16},"2026-04-06T18:10:01.123999",{"slug":2339,"name":2339,"fn":2340,"description":2341,"org":2342,"tags":2343,"stars":24,"repoUrl":25,"updatedAt":2353},"encore-cron","schedule recurring jobs in Encore.ts","Schedule periodic \u002F recurring work in Encore.ts using `CronJob` from `encore.dev\u002Fcron`. Covers `every: \"1h\"` interval syntax and `schedule: \"0 9 * * 1\"` cron expressions.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2344,2347,2348,2349,2352],{"name":2345,"slug":2346,"type":16},"Automation","automation",{"name":14,"slug":15,"type":16},{"name":9,"slug":8,"type":16},{"name":2350,"slug":2351,"type":16},"Scheduling","scheduling",{"name":2281,"slug":2282,"type":16},"2026-05-16T05:59:54.146651",{"slug":2355,"name":2355,"fn":2356,"description":2357,"org":2358,"tags":2359,"stars":24,"repoUrl":25,"updatedAt":2368},"encore-database","build Encore databases","Work with PostgreSQL in Encore.ts using `SQLDatabase` from `encore.dev\u002Fstorage\u002Fsqldb` — schema migrations and SQL queries.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2360,2363,2364,2367],{"name":2361,"slug":2362,"type":16},"Database","database",{"name":9,"slug":8,"type":16},{"name":2365,"slug":2366,"type":16},"ORM","orm",{"name":2281,"slug":2282,"type":16},"2026-04-06T18:09:54.823017",{"slug":2370,"name":2370,"fn":2371,"description":2372,"org":2373,"tags":2374,"stars":24,"repoUrl":25,"updatedAt":2385},"encore-frontend","connect frontend to Encore backend","Connect a frontend application (React, Next.js, Vue, Svelte, etc.) to an Encore.ts backend.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2375,2376,2379,2382],{"name":9,"slug":8,"type":16},{"name":2377,"slug":2378,"type":16},"Frontend","frontend",{"name":2380,"slug":2381,"type":16},"Next.js","next-js",{"name":2383,"slug":2384,"type":16},"React","react","2026-04-06T18:09:56.091006",{"slug":2387,"name":2387,"fn":2388,"description":2389,"org":2390,"tags":2391,"stars":24,"repoUrl":25,"updatedAt":2402},"encore-getting-started","build and run applications with Encore.ts","Bootstrap a brand-new Encore.ts project from zero. Only for first-time CLI install and `encore app create` — not for architecture or feature questions.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2392,2393,2394,2395,2398,2399],{"name":2277,"slug":2278,"type":16},{"name":14,"slug":15,"type":16},{"name":9,"slug":8,"type":16},{"name":2396,"slug":2397,"type":16},"Local Development","local-development",{"name":2281,"slug":2282,"type":16},{"name":2400,"slug":2401,"type":16},"Web Development","web-development","2026-04-06T18:10:04.885446",{"slug":2404,"name":2404,"fn":2405,"description":2406,"org":2407,"tags":2408,"stars":24,"repoUrl":25,"updatedAt":2412},"encore-go-api","build APIs with Encore Go","Define typed API endpoints in Encore Go using `\u002F\u002Fencore:api` annotations. Covers typed request\u002Fresponse structs, path\u002Fquery\u002Fheader\u002Fcookie params, and error returns. For raw endpoints (`\u002F\u002Fencore:api raw`) and inbound webhooks, use `encore-go-webhook` instead.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2409,2410,2411],{"name":2277,"slug":2278,"type":16},{"name":9,"slug":8,"type":16},{"name":22,"slug":23,"type":16},"2026-04-06T18:09:48.578781",{"slug":4,"name":4,"fn":5,"description":6,"org":2414,"tags":2415,"stars":24,"repoUrl":25,"updatedAt":26},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2416,2417,2418,2419],{"name":18,"slug":19,"type":16},{"name":14,"slug":15,"type":16},{"name":9,"slug":8,"type":16},{"name":22,"slug":23,"type":16},{"slug":2421,"name":2421,"fn":2422,"description":2423,"org":2424,"tags":2425,"stars":24,"repoUrl":25,"updatedAt":2433},"encore-go-bucket","store files in Encore Go buckets","Store unstructured files in Encore Go using `objects.NewBucket` from `encore.dev\u002Fstorage\u002Fobjects` — uploads, images, documents, blobs.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2426,2427,2428,2429,2430],{"name":14,"slug":15,"type":16},{"name":9,"slug":8,"type":16},{"name":2303,"slug":2304,"type":16},{"name":22,"slug":23,"type":16},{"name":2431,"slug":2432,"type":16},"Storage","storage","2026-05-16T06:00:03.633918",{"items":2435,"total":333},[2436,2442,2448,2456,2464,2470,2478],{"slug":2271,"name":2271,"fn":2272,"description":2273,"org":2437,"tags":2438,"stars":24,"repoUrl":25,"updatedAt":2283},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2439,2440,2441],{"name":2277,"slug":2278,"type":16},{"name":9,"slug":8,"type":16},{"name":2281,"slug":2282,"type":16},{"slug":2285,"name":2285,"fn":2286,"description":2287,"org":2443,"tags":2444,"stars":24,"repoUrl":25,"updatedAt":2293},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2445,2446,2447],{"name":18,"slug":19,"type":16},{"name":9,"slug":8,"type":16},{"name":2281,"slug":2282,"type":16},{"slug":2295,"name":2295,"fn":2296,"description":2297,"org":2449,"tags":2450,"stars":24,"repoUrl":25,"updatedAt":2309},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2451,2452,2453,2454,2455],{"name":14,"slug":15,"type":16},{"name":9,"slug":8,"type":16},{"name":2303,"slug":2304,"type":16},{"name":2306,"slug":2307,"type":16},{"name":2281,"slug":2282,"type":16},{"slug":2311,"name":2311,"fn":2312,"description":2313,"org":2457,"tags":2458,"stars":24,"repoUrl":25,"updatedAt":2325},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2459,2460,2461,2462,2463],{"name":14,"slug":15,"type":16},{"name":2318,"slug":2319,"type":16},{"name":9,"slug":8,"type":16},{"name":2322,"slug":2323,"type":16},{"name":2281,"slug":2282,"type":16},{"slug":2327,"name":2327,"fn":2328,"description":2329,"org":2465,"tags":2466,"stars":24,"repoUrl":25,"updatedAt":2337},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2467,2468,2469],{"name":2333,"slug":2334,"type":16},{"name":9,"slug":8,"type":16},{"name":2281,"slug":2282,"type":16},{"slug":2339,"name":2339,"fn":2340,"description":2341,"org":2471,"tags":2472,"stars":24,"repoUrl":25,"updatedAt":2353},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2473,2474,2475,2476,2477],{"name":2345,"slug":2346,"type":16},{"name":14,"slug":15,"type":16},{"name":9,"slug":8,"type":16},{"name":2350,"slug":2351,"type":16},{"name":2281,"slug":2282,"type":16},{"slug":2355,"name":2355,"fn":2356,"description":2357,"org":2479,"tags":2480,"stars":24,"repoUrl":25,"updatedAt":2368},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2481,2482,2483,2484],{"name":2361,"slug":2362,"type":16},{"name":9,"slug":8,"type":16},{"name":2365,"slug":2366,"type":16},{"name":2281,"slug":2282,"type":16}]