encore-auth について
Encore Authenticationは、Encore.tsのAPIに認証を実装するためのスキルです。認証ハンドラー、エンドポイント保護、認証データの参照、サービス間伝播、テスト方法を扱います。
主な機能
- 01認証ハンドラーの設定
- 02エンドポイントの認証制御
- 03リクエスト認証データへのアクセス
- 04サービス呼び出し間での認証伝播
- 05JWT、APIキー、Cookieのパターン
- 06Vitestによる認証テスト
活用シーン
- 01APIエンドポイントへの認証要求
- 02内部サービス呼び出しでのID伝播
- 03JWT、APIキー、Cookieの検証
- 04保護されたエンドポイントの動作テスト
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ encoredev/ skills --skill encore-authインストール後、Claude Code で/encore-authを入力して読み込まれたか確認します。
詳細
Encore 認証
手順
Encore.ts は、API 呼び出し元を識別し、エンドポイントを保護するための組み込みの認証システムを提供します。
1. 認証ハンドラーを作成する
// 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. エンドポイントを保護する
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. エンドポイントで認証データにアクセスする
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,
};
}
);
認証ハンドラーの動作
| シナリオ | ハンドラーの戻り値 | 結果 |
|---|---|---|
| 有効な認証情報 | AuthData オブジェクト | リクエストが認証済み |
| 無効な認証情報 | APIError.unauthenticated() をスロー | 認証なしとして扱われる |
| その他のエラー | その他のエラーをスロー | リクエストが中止される |
エンドポイントでの認証
| エンドポイント設定 | リクエストに認証がある | 結果 |
|---|---|---|
auth: true | はい | 認証データとともに続行 |
auth: true | いいえ | 401 Unauthenticated |
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()!;
// Auth is automatically propagated to this call
const orderUser = await user.getProfile();
return { order: await getOrder(id), user: orderUser };
}
);
認証データのオーバーライド
サービス間呼び出しを行う際に、認証データを明示的にオーバーライドできます:
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" } }
);
一般的な認証パターン
JWT トークンの検証
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 キー認証
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 ベースの認証
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 () => {
// 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();
});
});
このモックは ~encore/clients を通じて行われる呼び出しには適用されません。テストから別のサービスの auth: true エンドポイントを呼び出すには、2 番目の引数として { authData: ... } を渡します(上記の「認証データのオーバーライド」を参照)。
ガイドライン
- 認証ハンドラーは Gateway に登録する必要があります
- 認証データにアクセスするには
~encore/authのgetAuthData()を使用します getAuthData()は未認証のリクエストではnullを返します- 認証データはサービス間呼び出しで自動的に伝播します
- 無効な認証情報には
APIError.unauthenticated()をスローします - 認証ハンドラーは高速に保ちます - 認証されたすべてのリクエストで実行されます
コメント
Markdown 対応