encore-auth
Protect Encore.ts endpoints with authentication and authorize callers. Covers `authHandler`, `Gateway`, `getAuthData`, and `auth: true`.
What this skill does
# Encore Authentication
## Instructions
Encore.ts provides a built-in authentication system for identifying API callers and protecting endpoints.
### 1. Create an Auth Handler
```typescript
// auth.ts
import { Header, Gateway } from "encore.dev/api";
import { authHandler } from "encore.dev/auth";
// Define what the auth handler receives
interface AuthParams {
authorization: Header<"Authorization">;
}
// Define what authenticated requests will have access to
interface AuthData {
userID: string;
email: string;
role: "admin" | "user";
}
export const auth = authHandler<AuthParams, AuthData>(
async (params) => {
// Validate the token (example with JWT)
const token = params.authorization.replace("Bearer ", "");
const payload = await verifyToken(token);
if (!payload) {
throw APIError.unauthenticated("invalid token");
}
return {
userID: payload.sub,
email: payload.email,
role: payload.role,
};
}
);
// Register the auth handler with a Gateway
export const gateway = new Gateway({
authHandler: auth,
});
```
### 2. Protect Endpoints
```typescript
import { api } from "encore.dev/api";
// Protected endpoint - requires authentication
export const getProfile = api(
{ method: "GET", path: "/profile", expose: true, auth: true },
async (): Promise<Profile> => {
// Only authenticated users reach here
}
);
// Public endpoint - no authentication required
export const healthCheck = api(
{ method: "GET", path: "/health", expose: true },
async () => ({ status: "ok" })
);
```
### 3. Access Auth Data in Endpoints
```typescript
import { api } from "encore.dev/api";
import { getAuthData } from "~encore/auth";
export const getProfile = api(
{ method: "GET", path: "/profile", expose: true, auth: true },
async (): Promise<Profile> => {
const auth = getAuthData()!; // Non-null when auth: true
return {
userID: auth.userID,
email: auth.email,
role: auth.role,
};
}
);
```
## Auth Handler Behavior
| Scenario | Handler Returns | Result |
|----------|----------------|--------|
| Valid credentials | `AuthData` object | Request authenticated |
| Invalid credentials | Throws `APIError.unauthenticated()` | Treated as no auth |
| Other error | Throws other error | Request aborted |
## Auth with Endpoints
| Endpoint Config | Request Has Auth | Result |
|-----------------|------------------|--------|
| `auth: true` | Yes | Proceeds with auth data |
| `auth: true` | No | 401 Unauthenticated |
| `auth: false` or omitted | Yes | Proceeds (auth data available) |
| `auth: false` or omitted | No | Proceeds (no auth data) |
## Service-to-Service Auth Propagation
Auth data automatically propagates to internal service calls:
```typescript
import { user } from "~encore/clients";
import { getAuthData } from "~encore/auth";
export const getOrderWithUser = api(
{ method: "GET", path: "/orders/:id", expose: true, auth: true },
async ({ id }): Promise<OrderWithUser> => {
const auth = getAuthData()!;
// Auth is automatically propagated to this call
const orderUser = await user.getProfile();
return { order: await getOrder(id), user: orderUser };
}
);
```
### Overriding Auth Data
You can explicitly override auth data when making service-to-service calls:
```typescript
import { user } from "~encore/clients";
// Override auth data for this specific call
const adminUser = await user.getProfile(
{},
{ authData: { userID: "admin-123", email: "[email protected]", role: "admin" } }
);
```
## Common Auth Patterns
### JWT Token Validation
```typescript
import { jwtVerify } from "jose";
import { secret } from "encore.dev/config";
const jwtSecret = secret("JWTSecret");
async function verifyToken(token: string): Promise<JWTPayload | null> {
try {
const { payload } = await jwtVerify(
token,
new TextEncoder().encode(jwtSecret())
);
return payload;
} catch {
return null;
}
}
```
### API Key Authentication
```typescript
export const auth = authHandler<AuthParams, AuthData>(
async (params) => {
const apiKey = params.authorization;
const user = await db.queryRow<User>`
SELECT id, email, role FROM users WHERE api_key = ${apiKey}
`;
if (!user) {
throw APIError.unauthenticated("invalid API key");
}
return {
userID: user.id,
email: user.email,
role: user.role,
};
}
);
```
### Cookie-Based Auth
```typescript
interface AuthParams {
cookie: Header<"Cookie">;
}
export const auth = authHandler<AuthParams, AuthData>(
async (params) => {
const sessionId = parseCookie(params.cookie, "session");
if (!sessionId) {
throw APIError.unauthenticated("no session");
}
const session = await getSession(sessionId);
if (!session || session.expiresAt < new Date()) {
throw APIError.unauthenticated("session expired");
}
return {
userID: session.userID,
email: session.email,
role: session.role,
};
}
);
```
## Testing with Auth
Mock authentication in tests using Vitest:
```typescript
import { describe, it, expect, vi } from "vitest";
import * as auth from "~encore/auth";
import { getProfile } from "./api";
describe("authenticated endpoints", () => {
it("returns profile for authenticated user", async () => {
// Mock getAuthData to return test user
const spy = vi.spyOn(auth, "getAuthData");
spy.mockImplementation(() => ({
userID: "test-user-123",
email: "[email protected]",
role: "user",
}));
const profile = await getProfile();
expect(profile.email).toBe("[email protected]");
spy.mockRestore();
});
});
```
## Guidelines
- Auth handlers must be registered with a Gateway
- Use `getAuthData()` from `~encore/auth` to access auth data
- `getAuthData()` returns `null` in unauthenticated requests
- Auth data propagates automatically in service-to-service calls
- Throw `APIError.unauthenticated()` for invalid credentials
- Keep auth handlers fast - they run on every authenticated request
Related in General
modeling-omnistudio-epc-catalog
IncludedSalesforce Industries CME EPC product-modeling skill for Product2-based catalog creation. Use when creating EPC products, configuring product attributes, building offer bundles with Product Child Items, or reviewing EPC DataPack JSON metadata for product catalog changes. TRIGGER when: user creates or updates Product2 EPC records, AttributeAssignment payloads, AttributeMetadata/AttributeDefaultValues, Offer bundles, or ProductChildItem relationships. DO NOT TRIGGER when: designing OmniScripts/FlexCards/Integration Procedures (use building-omnistudio-omniscript, building-omnistudio-flexcard, or building-omnistudio-integration-procedure), implementing Apex business logic (use generating-apex), or troubleshooting deployment pipelines (use deploying-metadata).
relationship-science-coach
IncludedUse this skill for direct, practical adult relationship coaching: couples conflict, repair, trust, marriage, dating, flirting, attachment patterns, emotional connection, sex, desire differences, eroticism, kink negotiation, affection, love languages, breakups, and long-term passion. Draw on Gottman, EFT and Hold Me Tight, attachment science, modern sex research, Perel, Nagoski, Kerner, Schnarch, Love and Stosny, and flexible love-language tools. Be concrete and low-hedge. Redirect only for imminent danger, abuse, coercive control, minors, non-consent, self-harm, stalking, or medical/legal/psychiatric decisions.
building-sf-integrations
IncludedSalesforce integration architecture and runtime plumbing with 120-point scoring. Use this skill to set up Named Credentials, External Credentials, External Services, REST/SOAP callout patterns, Platform Events, and Change Data Capture. TRIGGER when: user sets up Named Credentials, External Services, REST/SOAP callouts, Platform Events, CDC, or touches .namedCredential-meta.xml files. DO NOT TRIGGER when: Connected App/OAuth config (use configuring-connected-apps), Apex-only logic (use generating-apex), or data import/export (use handling-sf-data).
venue-templates
IncludedAccess comprehensive LaTeX templates, formatting requirements, and submission guidelines for major scientific publication venues (Nature, Science, PLOS, IEEE, ACM), academic conferences (NeurIPS, ICML, CVPR, CHI), research posters, and grant proposals (NSF, NIH, DOE, DARPA). This skill should be used when preparing manuscripts for journal submission, conference papers, research posters, or grant proposals and need venue-specific formatting requirements and templates.
let-fate-decide
IncludedDraws the 12 Houses of the Zodiac Tarot spread to inject entropy into planning when prompts are vague, ambiguous, or casually delegated. Interprets the spread to guide next steps. Use when the user says 'let fate decide', 'YOLO', 'whatever', 'idk', or other nonchalant phrases, makes Yu-Gi-Oh references, or when you are about to arbitrarily pick between multiple reasonable approaches. Prefer over ask-questions-if-underspecified when the user's tone is casual or playful rather than precision-seeking.
net-ops
IncludedCross-platform network troubleshooting (Windows, macOS, Linux) via local or remote shell. Use for: DNS broken, can't resolve hostnames, nslookup/dig works but apps fail, NRPT, WFP, scutil, /etc/resolver, systemd-resolved, /etc/resolv.conf, NetworkManager, VPN DNS leak residue (ProtonVPN/Mullvad/WireGuard/AnyConnect), AV/firewall blocking DNS or DoH, Tailscale DNS interaction, intermittent connectivity, remote diagnostics over SSH.