@xenkai-inc/cf-access
v0.1.0
Published
Cloudflare Access JWT 検証共通モジュール(T0328/T0329 で共有)
Readme
@xenkai-inc/cf-access
Cloudflare Access JWT 検証共通モジュール。
llm.xenkai.jp(T0328)と将来の Xenkai SDK(T0329 等)で再利用する。
v0 unstable: SemVer に従うが、v1.0.0 まではマイナーバージョンで破壊的変更が入る可能性があります。
インストール
npm install @xenkai-inc/cf-access認証不要(npmjs public package)。
機能
- Cloudflare Access JWT の RS256 署名検証
- JWKS のメモリキャッシュ(TTL 1h、key rotation 対応)
- クレーム検証: aud / iss / exp / nbf(60秒 clock drift 許容)
- 型付きエラー(
AccessJwtError.code)で 401/403/500 の切り分けが可能 VerifiedUserEmailbranded type による型安全なメール取得
使い方
extractVerifiedEmail — JWT 検証とメール取得を一括で行う(推奨)
import { extractVerifiedEmail, AccessJwtError } from '@xenkai-inc/cf-access';
// teamDomain と aud は必ず呼び出し側の env から渡すこと(ハードコード禁止)
try {
const email = await extractVerifiedEmail(
request.headers.get('Cf-Access-Jwt-Assertion') ?? '',
env.CF_ACCESS_TEAM_DOMAIN, // 例: "xenkai"
env.CF_ACCESS_AUD, // Cloudflare Zero Trust Dashboard の AUD tag
);
// email は VerifiedUserEmail(JWT 検証済み branded string)
console.log(email); // "[email protected]"
} catch (err) {
if (err instanceof AccessJwtError) {
// err.code で 401/403/500 を切り分け
}
}シグネチャ:
async function extractVerifiedEmail(
jwt: string,
teamDomain: string,
aud: string,
): Promise<VerifiedUserEmail>verifyAccessJwt + extractEmail — ペイロード全体が必要な場合
import { verifyAccessJwt, extractEmail, AccessJwtError } from '@xenkai-inc/cf-access';
try {
// JWT を検証して AccessJwtPayload を返す
const payload = await verifyAccessJwt(
request.headers.get('Cf-Access-Jwt-Assertion') ?? '',
env.CF_ACCESS_TEAM_DOMAIN,
env.CF_ACCESS_AUD,
);
// payload から email を取得(verifyAccessJwt で存在が保証されている)
const email = extractEmail(payload);
// payload には aud / iss / exp / iat / sub / policy_id 等が含まれる
console.log(payload.policy_id);
} catch (err) {
if (err instanceof AccessJwtError) {
switch (err.code) {
case 'JWT_MISSING':
case 'JWT_EXPIRED':
case 'JWT_NOT_YET_VALID':
return new Response('Unauthorized', { status: 401 });
case 'JWT_INVALID_SIGNATURE':
case 'JWT_INVALID_AUD':
case 'JWT_INVALID_ISS':
case 'JWT_MALFORMED':
case 'JWT_EMAIL_MISSING':
return new Response('Forbidden', { status: 403 });
case 'JWKS_FETCH_FAILED':
return new Response('Internal Server Error', { status: 500 });
}
}
throw err;
}env の設定
teamDomain と aud は必ず呼び出し側の環境変数から渡してください。ソースコードへのハードコードは禁止です。
| 変数名 | 内容 | 取得場所 |
|--------|------|----------|
| CF_ACCESS_TEAM_DOMAIN | Cloudflare Access team domain(例: xenkai) | Zero Trust → Settings → Custom Pages |
| CF_ACCESS_AUD | Application AUD tag | Zero Trust → Access → Applications → 対象アプリ |
エラーコード
| コード | HTTP ステータス | 原因 |
|--------|-----------------|------|
| JWT_MISSING | 401 | JWT が未指定または空 |
| JWT_EXPIRED | 401 | JWT 有効期限切れ(clock drift 60秒超) |
| JWT_NOT_YET_VALID | 401 | nbf が未来(clock drift 60秒超) |
| JWT_INVALID_SIGNATURE | 403 | 署名検証失敗 |
| JWT_INVALID_AUD | 403 | aud 不一致 |
| JWT_INVALID_ISS | 403 | iss 不一致 |
| JWT_MALFORMED | 403 | JWT 形式不正 |
| JWT_EMAIL_MISSING | 403 | payload に email クレームなし |
| JWKS_FETCH_FAILED | 500 | JWKS フェッチ失敗 |
開発
# テスト実行
npm test -w packages/cf-access
# 型チェック
npm run typecheck -w packages/cf-access
# ビルド
npm run build -w packages/cf-access