型付き辺: 出 3 / 入 0
ADR-0096: ADR 起案 UI の起案者を Cloudflare Access 認証済み email で自動入力する (per-user 識別・認証統合)
- Status: Accepted
- Mode: Standard
- Kruchten Type: Existence/Executive
- Scope: platform
- Implementation Status: Done
- 起案者: [email protected]
- 起案日時 (JST): 2026-06-01 12:36
- 承認日時 (JST): 2026-06-21
- Deciders: [email protected] (単独)
コンテキスト
§1.1 背景
2026-10 の Jr 入社で ADR 起案者が複数になることを控え、起案者情報の一貫性確保が必要になった。現在は単一共有 Basic 認証 + 自由テキスト入力で、誰が起案したかが手入力依存になっている。
§1.2 現状 (As-Is)
- 認証は Basic 認証のみ (
src/index.ts:22basicAuth({username: AUTH_USER, password: AUTH_PASSWORD}))。単一共有 credential で per-user の email を持たない。 - 起案者フィールド (
public/chat.html:1691<input id="author">) は自由テキスト +PERSIST_FIELDSによる localStorage 永続。 - worker には認証主体の email を取得する経路が存在しない。
- ADR frontmatter「起案者」と telemetry author が手入力依存で不統一 (同一人物が「代表取締役」「[email protected]」と混在)。
§1.3 課題
手入力依存により起案者の誤記・属性欠落・なりすましリスクが顕在化。Jr 入社で起案者が複数になると、誰が起案したかが ADR・telemetry で追跡不能になる。
§1.4 制約・要件
- Cloudflare Zero Trust Free tier (50 users / 1000 tunnels) 内に収める (追加課金 0 円)。
- CI/headless 経路 (
drp-trigger.ymlの/runs) は人間でない呼び出しのため、認証統合後も動作継続必須。 - ローカル開発で Access を bypass する手順が必要 (Jr 入社時のオンボーディング前提)。
- RQ-085 (Basic 認証 → Cloudflare Access service token) の方向性に整合させる。
- ADR frontmatter は長期保管文書であり、改ざん防止ポリシーと衝突しない author 記録方式を採る。
§1.5 目標 (To-Be)
ログイン中アカウントの email を起案者として自動入力し、ADR frontmatter と telemetry author を per-user で一貫記録する。Non-Goals: Basic 認証の完全廃止 (本 ADR は部分実装、完全廃止は別 ADR)。
決定
chat UI を提供する decision-pipeline worker を Cloudflare Access (Zero Trust) 配下に置き、Access が付与する Cf-Access-Authenticated-User-Email ヘッダ (検証は Cf-Access-Jwt-Assertion の署名検証) を worker が読み取り、/chat ページ配信時に起案者フィールドへ自動入力 (読み取り専用) する。自動入力値を POST /chat/start / /runs の author として送出し、ADR frontmatter「起案者」と telemetry author に一貫記録する。Basic 認証は Cloudflare Access の service token に統合し、CI/headless は service token で認証する。RQ-085 の推奨を部分実装する位置づけ。
JWT 公開鍵は JWKS エンドポイント (https://<team>.cloudflareaccess.com/cdn-cgi/access/certs) から動的取得し、Cache API で TTL 付きキャッシュ + 鍵ローテーション時の自動再取得を実装する。worker でヘッダ値の RFC5322 メール形式バリデーションを必須化し、不正値は /chat 配信を 400 で中断する。
判断基準 (Decision Drivers)
3.1 評価軸
| # | 軸 | 重要度 (係数) | 案件特有の解釈 |
|---|---|---|---|
| 1 | #secure | [Must] (×2.0) | 起案者識別の偽装耐性 (ヘッダ単独信頼でなく JWT 署名検証) |
| 2 | #maintainable | [High] (×1.0) | ADR frontmatter/telemetry の author 一貫性、長期保管耐性 |
| 3 | #operable | [High] (×1.0) | service token 棚卸し、JWKS ローテーション、ローカル bypass、Access 障害復旧 |
| 4 | #suitable | [Medium] (×0.5) | Jr 入社 (2026-10) までに per-user 識別が利用可能になるか |
| 5 | #efficient | [Medium] (×0.5) | 実装人日、追加金銭コスト |
K.O. criterion: Must 軸 (#secure) の score < 3 は不採用。
3.2 評価軸 × 案スコア表
各案 0-5 で評価。加重和 = (Σ score × 係数) / (5 × Σ 係数) で正規化。
| 軸 | 係数 | 採択案 (A: Access 移行) | 案 B: Basic 認証 per-user 化 | 案 C: localStorage email 固定 |
|---|---|---|---|---|
#secure (Must) | ×2.0 | 5 | 2 | 1 |
#maintainable (High) | ×1.0 | 4 | 3 | 1 |
#operable (High) | ×1.0 | 3 | 4 | 5 |
#suitable (Medium) | ×0.5 | 4 | 3 | 4 |
#efficient (Medium) | ×0.5 | 3 | 4 | 5 |
| 加重和 (正規化) | 0.800 | 0.580 | 0.460 | |
| K.O. 通過 (Must ≥3) | ✓ | ❌ (score=2) | ❌ (score=1) |
検討した代替案 (Alternatives Considered)
- 案 A (採択): Cloudflare Access 移行 + JWT 署名検証 — JWT 検証により偽装耐性を確保。RQ-085 と整合。実装 1.5 人日、金銭 0 円。運用追加 (policy 棚卸し・JWKS 管理) は受容可能。
- 案 B: Basic 認証 username を per-user 化 — 共有 credential を個人別に分割。実装 0.5 人日と軽量だが、Basic 認証は credential 漏洩時の偽装耐性が弱く
#secureK.O. を通過しない。RQ-085 の方向性とも逆行。 - 案 C: localStorage に email 固定値を保存 — 0.25 人日以下で実装可能だが認証情報と紐づかないため
#secureK.O. 不通過。Jr 入社までの暫定措置としても、手動設定漏れで空 author が発生する。撤退時の縮退先として位置づける。
コスト試算 (Cost Estimate)
- 実装工数: 約 1.5 人日 (採択案 A)。作業内訳: ① worker の JWT 署名検証 + JWKS 動的取得/キャッシュ実装、② CI の service token 移行、③ ローカル bypass 手順 + Runbook 整備。各項目の人日内訳は Phase A 着手時に実績で確定する (現状は総量の概算・実装未着手のため推定)。
- 金銭コスト: 0 円。Cloudflare Zero Trust Free tier (50 users) 内に収め追加課金なし。
- 運用追加コスト (継続): service token / policy の四半期棚卸し + JWKS ローテーション監視 + service token 期限 30 日前の Slack アラート構築 (R5)。棚卸しは四半期ごと、アラート構築は初期一度。
- 代替案比較: 案 B 約 0.5 人日 / 案 C 約 0.25 人日 (いずれも
#secureK.O. 不通過のため不採択)。
影響 (Consequences)
§5.1 正の影響 (Good)
- per-user 識別で「誰が起案したか」が ADR・telemetry で一貫・明確化。Jr 参加後も自動で正しい属性が付く。
- 手入力 (代表取締役 等) の不統一・誤記を排除。
- RQ-085「Basic 認証 → Cloudflare Access」を前進させ、認証統合の第一歩になる。
§5.2 負の影響 (Bad)
- Access 設定の運用追加 (policy / service token の四半期棚卸し、JWKS キャッシュ管理)。
- CI/headless 経路 (
/runs) は service token 認証への移行が必須。未対応だと CI 起動不能。 - ローカル開発で Access を bypass する手順 (cloudflared access / service token) が要る。Jr オンボーディング前にドキュメント化必須。
- ADR frontmatter と telemetry に email を恒久記録するため、退職者・改姓・ドメイン変更時の Git 履歴残存問題が発生。改ざん防止ポリシーと「忘れられる権利」要求が衝突する可能性。
- 起案者フィールドを読み取り専用にすることで、共同起案・代理起案 (上長が部下の代理で起案) のユースケースが技術的に不可能になる。
§5.3 中立・トレードオフ (Neutral / Trade-offs)
- R1 Access 障害で
/chatアクセス不能 → 起案停止。fallback として Basic 認証経路を移行後 4 週間は併存 (Access の bypass ルール + wrangler.toml で経路分離)。Access 全断時は Cloudflare dashboard で policy 無効化 (推定復旧 15 分)。 - R2 ヘッダ偽装 (Access 非経由で
Cf-Access-Authenticated-User-Email詐称) → worker はCf-Access-Jwt-Assertionを Access 公開鍵で検証、未検証は 403。ヘッダ単独を信頼しない。 - R3 service token アクセス (CI) は email を持たない → author に service principal 名を入れ、
/runsはtrigger_sourceで人間起案と区別。 - R4 JWT 公開鍵ローテーション → JWKS エンドポイントから動的取得 + Cache API で TTL キャッシュ + ローテーション検知時の自動再取得。公式ライブラリ (
@cloudflare/workers-access-authenticator) の採用可否を実装時に評価。 - R5 service token 有効期限切れ → Cloudflare API で期限監視、30 日前に Slack アラート発火 (CI 組み込み)。token ローテーション手順を Runbook 化、担当者不在時の代替者明示。CI 移行と Access 有効化は独立ステップにし、CI 正常動作確認後に Access 有効化。
- R6 ヘッダ値の空・不正 → RFC5322 バリデーション必須化、不正時は 400 で中断、localStorage フォールバック禁止。
- R7 email 識別子の可変性 → ドメイン変更・退職・IdP 移行時に過去 ADR の email と現在の Access email が不整合になる。3〜5 年後の追跡性を維持するため、frontmatter 更新方針を別途決定 (本 ADR では「意図的に変更しない、変更履歴は Access 側で管理」とする)。将来改善候補として JWT の
subclaim 等の不変 ID 併用を記載。 - R8 共同起案ユースケース → 読み取り専用固定は将来の運用ニーズに対する設計制約として明記。発生時は再 ADR で「自動入力 + 編集可 + 変更を audit log 記録」への変更を検討。
撤退条件 (Rollback Plan)
- 導入後 2 週で、Access 起因の起案アクセス障害が月 3 回以上、または author 自動入力の誤り (空・誤メール) が直近 20 起案で 2 件超 → Basic 認証 + 自由入力に戻す (案 C の固定デフォルトへ縮退)。
- CI service token 期限切れで CI 起動不能になった場合は即時手動 token 再発行 + Runbook 改善。
- 判定: [email protected] (プライマリ)。telemetry の author 欠落率・Access エラー率・JWT 検証失敗率で観測。
- Basic 認証 fallback 撤去日: 移行完了から 4 週後の特定カレンダー日付を実装時に確定し、Runbook に記載。撤去判断権者は [email protected]。撤去完了の確認方法は wrangler.toml から Basic 認証経路の routes 削除 + CI で 401 応答確認。撤去日を延期する場合は再 ADR が必要 (先送りにコストを課す)。
Confirmation
- 検証手段:
- telemetry author フィールドの 非空・RFC5322 email 形式準拠率 100% (worker バリデーション + telemetry 集計クエリ)。
Cf-Access-Jwt-Assertion未検証リクエストが 403 になる E2E テスト + 正規 JWT (鍵ローテーション後含む) が 200 になる E2E テストを CI 必須化。- ADR frontmatter「起案者」の email 形式準拠率 ≥ 95% (adr-lint で frontmatter author を RFC5322 検証)。
- service token 有効期限 30 日前の Slack アラート発火テスト (CI で月次模擬実行)。
- ローカル開発手順を別メンバーが Jr 入社前に追試し動作確認 (受け入れ基準)。
- 空 author が telemetry に書き込まれた件数の alert 閾値監視 (>0 で即時通知)。
- 実行頻度:
- (1)(6) 全 run ごと (リアルタイム)
- (2) 全 PR の CI
- (3) 導入後 4 週時点で計測、以降月次
- (4) 月次
- (5) 2026-09 末まで (Jr 入社前) に 1 回
- 違反時対応:
- (1)(3)(6) 違反 → §6 撤退条件発火、Basic 認証 + 自由入力に縮退検討
- (2) 違反 → 該当 PR マージブロック
- (4) 違反 → service token 即時再発行 + Runbook 改善
- (5) 違反 → 手順書改訂 + 再追試
参照 (References)
- 関連 ADR: RQ-085 (認証・シークレット統合 / 本 ADR が部分実装)、ADR-0019 / ADR-0066 (decision-pipeline worker アーキ)、ADR-0082 (TelemetryRecord author フィールド)
- 関連 PR/Issue: -
- 外部資料: Cloudflare Access JWT Validation (
https://<team>.cloudflareaccess.com/cdn-cgi/access/certs)、@cloudflare/workers-access-authenticator(採用可否は実装時評価)