npx skills add https://github.com/encoredev/skills --skill encore-authEncore.ts 提供了一个内置的身份验证系统,用于识别 API 调用者并保护端点。
// auth.ts
import { Header, Gateway } from "encore.dev/api";
import { authHandler } from "encore.dev/auth";
// 定义身份验证处理器接收的参数
interface AuthParams {
authorization: Header<"Authorization">;
}
// 定义经过身份验证的请求将有权访问的数据
interface AuthData {
userID: string;
email: string;
role: "admin" | "user";
}
export const auth = authHandler<AuthParams, AuthData>(
async (params) => {
// 验证令牌(使用 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,
};
}
);
// 将身份验证处理器注册到网关
export const gateway = new Gateway({
authHandler: auth,
});
import { api } from "encore.dev/api";
// 受保护的端点 - 需要身份验证
export const getProfile = api(
{ method: "GET", path: "/profile", expose: true, auth: true },
async (): Promise<Profile> => {
// 只有经过身份验证的用户才能到达这里
}
);
// 公共端点 - 不需要身份验证
export const healthCheck = api(
{ method: "GET", path: "/health", expose: true },
async () => ({ status: "ok" })
);
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
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()!; // 当 auth: true 时非空
return {
userID: auth.userID,
email: auth.email,
role: auth.role,
};
}
);
| 场景 | 处理器返回 | 结果 |
|---|---|---|
| 有效凭据 | AuthData 对象 | 请求通过身份验证 |
| 无效凭据 | 抛出 APIError.unauthenticated() | 视为无身份验证 |
| 其他错误 | 抛出其他错误 | 请求中止 |
| 端点配置 | 请求是否具有身份验证 | 结果 |
|---|---|---|
auth: true | 是 | 继续执行,附带身份验证数据 |
auth: true | 否 | 401 未通过身份验证 |
auth: false 或省略 | 是 | 继续执行(身份验证数据可用) |
auth: false 或省略 | 否 | 继续执行(无身份验证数据) |
身份验证数据会自动传播到内部服务调用:
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()!;
// 身份验证会自动传播到此调用
const orderUser = await user.getProfile();
return { order: await getOrder(id), user: orderUser };
}
);
在进行服务间调用时,可以显式覆盖身份验证数据:
import { user } from "~encore/clients";
// 为此特定调用覆盖身份验证数据
const adminUser = await user.getProfile(
{},
{ authData: { userID: "admin-123", email: "admin@example.com", role: "admin" } }
);
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;
}
}
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,
};
}
);
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,
};
}
);
使用 Vitest 模拟测试中的身份验证:
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 () => {
// 模拟 getAuthData 返回测试用户
const spy = vi.spyOn(auth, "getAuthData");
spy.mockImplementation(() => ({
userID: "test-user-123",
email: "test@example.com",
role: "user",
}));
const profile = await getProfile();
expect(profile.email).toBe("test@example.com");
spy.mockRestore();
});
});
~encore/auth 中的 getAuthData() 来访问身份验证数据getAuthData() 返回 nullAPIError.unauthenticated()每周安装数
181
代码仓库
GitHub 星标数
20
首次出现时间
2026年1月21日
安全审计
安装于
codex143
opencode139
gemini-cli137
claude-code121
github-copilot120
cursor112
Encore.ts provides a built-in authentication system for identifying API callers and protecting endpoints.
// 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,
});
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" })
);
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,
};
}
);
| 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 |
| 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) |
Auth data automatically propagates to internal service calls:
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 };
}
);
You can explicitly override auth data when making service-to-service calls:
import { user } from "~encore/clients";
// Override auth data for this specific call
const adminUser = await user.getProfile(
{},
{ authData: { userID: "admin-123", email: "admin@example.com", role: "admin" } }
);
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;
}
}
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,
};
}
);
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,
};
}
);
Mock authentication in tests using Vitest:
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: "test@example.com",
role: "user",
}));
const profile = await getProfile();
expect(profile.email).toBe("test@example.com");
spy.mockRestore();
});
});
getAuthData() from ~encore/auth to access auth datagetAuthData() returns null in unauthenticated requestsAPIError.unauthenticated() for invalid credentialsWeekly Installs
181
Repository
GitHub Stars
20
First Seen
Jan 21, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
codex143
opencode139
gemini-cli137
claude-code121
github-copilot120
cursor112
Linux云主机安全托管指南:从SSH加固到HTTPS部署
31,600 周安装
Docker安全指南:全面容器安全最佳实践、漏洞扫描与合规性要求
177 周安装
iOS开发专家技能:精通Swift 6、SwiftUI与原生应用开发,涵盖架构、性能与App Store合规
177 周安装
describe技能:AI驱动结构化测试用例生成,提升代码质量与评审效率
2 周安装
专业 README 生成器 | 支持 Rust/TypeScript/Python 项目,自动应用最佳实践
2 周安装
Django 6 升级指南:从 Django 5 迁移的完整步骤与重大变更解析
1 周安装
GitLab DAG与并行处理指南:needs与parallel优化CI/CD流水线速度
2 周安装