Skip to content

widget/receiver: ingest key による widget↔receiver ペア認証を追加する (#44) - #106

Merged
kosako merged 2 commits into
mainfrom
feat/44-ingest-key
Jul 5, 2026
Merged

widget/receiver: ingest key による widget↔receiver ペア認証を追加する (#44)#106
kosako merged 2 commits into
mainfrom
feat/44-ingest-key

Conversation

@kosako

@kosako kosako commented Jul 5, 2026

Copy link
Copy Markdown
Owner

概要

Issue #44 の本体。公開デプロイで唯一無認証だった受信面(POST /feedback)に、プロジェクトごとの公開キー(ingest key)による軽量なペア認証を追加します。#44 の他項目(SVG stored XSS の CSP 封じ、DoS 上限、payload shape 検証)は #90/#92/#93 で対応済みです。

前提(Issue の設計方針)

ページに埋め込むキーは閲覧者全員に見えるため、秘密による認証ではありません。目的は (a) プロジェクト識別、(b) 無差別 spam の抑止、(c) ローテーションによる失効。レビュアー個人の認証(デモ側ログイン前提の署名付き token)は将来スコープとして README の境界に明記しました。

変更内容

receiver

  • INGEST_KEYS env(カンマ区切り)/ config ingestKeys(配列)。設定時は X-PatchLoop-Ingest-Key ヘッダーの一致(timing-safe)を必須にし、欠落・不一致は 401。未設定は従来通りオープン(ゼロ設定互換)
  • route table の auth kind ingest として宣言(PR-1 のコメントで予告していた拡張)。dispatch が一元適用し、解決した key entry を handler に渡す
  • config の { "key": "...", "projectId": "..." } 形式で key ↔ projectId の紐付け: 紐付き key での投稿は payload の projectId 詐称を 403 で拒否、省略時は補完(store 上のレコードに必ず帰属が付く)
  • CORS の Access-Control-Allow-Headers に key ヘッダーを追加(Issue の設計メモ: 忘れると preflight で widget 送信が全滅)
  • 起動ログに ingest auth: enabled (N keys) / open を出力(キー値は出さない)

widget

  • init option ingestKey を追加し、receiver POST にヘッダーで送信(dist 再ビルド込み)

docs

  • README: Init options / receiver 設定 / 公開デプロイ節(INGEST_KEYS を推奨手順に追加)/ 「現在の境界」の更新

テスト

npm run check 全 105 件パス(新規 2 件: キー必須・誤キー拒否・preflight ヘッダー・/import 非干渉 / projectId 紐付けの一致・詐称 403・補完)。実機スモーク済み: 起動ログ / no key 401 / wrong key 401 / 素キー 201 / 紐付き key + 詐称 projectId 403 / 省略時補完(store で確認)/ preflight Allow-Headers / dist へのヘッダー反映。

Closes #44

🤖 Generated with Claude Code

https://claude.ai/code/session_01LRdDjwxbAAaTMwwnzipkau

公開デプロイで唯一無認証だった受信面(POST /feedback)を保護する。

- receiver: INGEST_KEYS env(カンマ区切り)/ config ingestKeys(配列)を
  追加。設定時は X-PatchLoop-Ingest-Key ヘッダーの一致を必須にし、
  欠落・不一致は 401。route table の auth kind "ingest" として宣言し、
  dispatch で一元適用(timing-safe 比較)
- config の { key, projectId } 形式で key と projectId を紐付けでき、
  紐付き key での投稿は payload の projectId 詐称を 403 で拒否、
  省略時は key の projectId を補完(必ず帰属が付く)
- CORS の Access-Control-Allow-Headers に X-PatchLoop-Ingest-Key を追加
  (忘れると preflight で widget 送信が全滅する — issue の設計メモ)
- widget: init option ingestKey を追加し、receiver POST にヘッダーで送る
- 起動ログに ingest auth の有効/無効を出力。未設定は従来通り誰でも投稿
  できるゼロ設定互換

キーはデモページに埋め込まれる公開キーで、秘密による認証ではない
(プロジェクト識別・無差別 spam 抑止・ローテーション失効が目的)。
レビュアー個人の認証は将来スコープ(README の境界に明記)。

Closes #44

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LRdDjwxbAAaTMwwnzipkau
@kosako

kosako commented Jul 5, 2026

Copy link
Copy Markdown
Owner Author

🔍 レビュー依頼(→ Codex)

  • 観点: Widget→Receiver 取り込みのペア認証と受信データの安全な取り扱い #44 ingest key(widget↔receiver ペア認証)。重点:
    • 認証境界: auth kind "ingest" の dispatch 適用(欠落・不一致 401、timing-safe)、未設定時のゼロ設定互換、他 endpoint(/import 等)への非干渉
    • projectId 紐付け: 詐称 403 / 省略時補完のロジック、validateFeedbackPayload との順序、import 経路に紐付けが効かないことの妥当性
    • CORS: Access-Control-Allow-Headers への key ヘッダー追加漏れ・退行(preflight で widget が弾かれないか)
    • widget 側: init option の配線(DEFAULTS → state.options → postFeedback)、dist ビルドの整合
    • 設定パース: normalizeIngestKeys(env / config、オブジェクト形式、無効エントリの警告)
    • production-rail review lens(フォールバック握り潰し・契約変更の整合・デッドコード)
  • ランク: 🔴 must(merge 前必須)/ 🟡 should(推奨・対応は依頼元判断)/ ⚪ nit(任意)
  • 結果はこの PR にコメントで返します

ingest key は公開キーで unforgeable ではないため、コメントの文言を
「ingest key / project binding と並ぶ弱シグナル」に更新(Codex レビュー 🟡)。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LRdDjwxbAAaTMwwnzipkau
@kosako

kosako commented Jul 5, 2026

Copy link
Copy Markdown
Owner Author

📋 レビュー結果(by Codex GPT-5.5)

判定: ✅ merge 可(must 0 件) — 🟡 should 1 件(下記、対応済み)/ ⚪ nit 0 件

🔴 must

なし

🟡 should

⚪ nit

なし

レビュアーの検証範囲(要旨)

route table / dispatch / rate limit・CORS・auth の順序 / enforceIngestProject の呼び出し順を確認。/importprotected のままで ingest key・project binding が掛からないことを確認。widget と dist の ingestKey 配線・ヘッダー送信の整合、README・config example・テスト追加分を静的確認。テスト実行は依頼元がローカルで代替(npm run check 全 105 件パス + 実機スモーク)。

@kosako
kosako marked this pull request as ready for review July 5, 2026 13:30
@kosako
kosako merged commit 4bdcc0d into main Jul 5, 2026
1 check passed
@kosako
kosako deleted the feat/44-ingest-key branch July 5, 2026 13:30
kosako added a commit that referenced this pull request Jul 5, 2026
#103(inbox ログイン + CORS 制限)・#104(healthz / graceful shutdown /
設定警告)・#106(ingest key)で README.md に入れた receiver 契約の変更を
英語版にミラーする:

- RECEIVER_TOKEN の説明を「閲覧系も保護 + inbox ログイン(HMAC cookie)」に更新
- INGEST_KEYS / ALLOWED_ORIGINS の項目を追加
- healthz・graceful shutdown・invalid setting warning を追記
- Public Deployment (EC2 etc.) 節を新設
- Init options に ingestKey を追加、Current Boundary の Auth 行を更新

Closes #105


Claude-Session: https://claude.ai/code/session_01LRdDjwxbAAaTMwwnzipkau

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Widget→Receiver 取り込みのペア認証と受信データの安全な取り扱い

1 participant