encore-auth

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Encore Authentication

Encore 认证

Instructions

说明

Encore.ts provides a built-in authentication system for identifying API callers and protecting endpoints.
Encore.ts 提供了一个内置的认证系统,用于识别API调用者并保护端点。

1. Create an Auth Handler

1. 创建认证处理器

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,
});
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

2. 保护端点

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" })
);
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

3. 在端点中访问认证数据

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,
    };
  }
);
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

认证处理器行为

ScenarioHandler ReturnsResult
Valid credentials
AuthData
object
Request authenticated
Invalid credentialsThrows
APIError.unauthenticated()
Treated as no auth
Other errorThrows other errorRequest aborted
场景处理器返回值结果
有效凭据
AuthData
对象
请求已认证
无效凭据抛出
APIError.unauthenticated()
视为未认证
其他错误抛出其他错误请求中止

Auth with Endpoints

端点认证配置

Endpoint ConfigRequest Has AuthResult
auth: true
YesProceeds with auth data
auth: true
No401 Unauthenticated
auth: false
or omitted
YesProceeds (auth data available)
auth: false
or omitted
NoProceeds (no auth data)
端点配置请求是否带认证结果
auth: true
携带认证数据继续执行
auth: true
返回401 未认证
auth: false
或未设置
继续执行(可获取认证数据)
auth: false
或未设置
继续执行(无认证数据)

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 };
  }
);
认证数据会自动传播到内部服务调用:
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 };
  }
);

Common Auth Patterns

常见认证模式

JWT Token Validation

JWT 令牌验证

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;
  }
}
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

API 密钥认证

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,
    };
  }
);
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

基于Cookie的认证

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,
    };
  }
);
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,
    };
  }
);

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
  • 认证处理器必须注册到Gateway
  • 使用
    ~encore/auth
    中的
    getAuthData()
    来访问认证数据
  • 在未认证的请求中,
    getAuthData()
    返回
    null
  • 认证数据会在服务间调用中自动传播
  • 对于无效凭据,抛出
    APIError.unauthenticated()
  • 保持认证处理器快速执行 - 它们会在每个认证请求中运行