Skip to content

feat(#194): 認可のセッションキー方式化とバックアップ用メール認証 (BE) - #210

Draft
kuu13580 wants to merge 6 commits into
feature/issue194_session-id-dbfrom
feature/issue194_session-id
Draft

feat(#194): 認可のセッションキー方式化とバックアップ用メール認証 (BE)#210
kuu13580 wants to merge 6 commits into
feature/issue194_session-id-dbfrom
feature/issue194_session-id

Conversation

@kuu13580

@kuu13580 kuu13580 commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

概要

issue #194 の実装。認可を「trip_ids 配列 JWT」から「session_id + DB (user_trip_access)」方式に移行し、Firebase Auth を "バックアップ・追加機能" として統合。

⚠️ このPRは #212 (DB スキーマ追加) の上に積んでいます。base branch を feature/issue194_session-id-db に設定済み。#212 が develop にマージされると、GitHub 側で自動的に本 PR の base が develop に更新されます。

Draft の理由: 実機検証が未完了 (下記 Test plan の unchecked 項目)。コード上は BE / FE ともに ready で、Firebase コンソール側での Email link 有効化と authorized domains 設定が済めば動作確認できる状態。

やったこと(本 PR 分)

バックエンド

  • 認可ロジック差し替え (app/auth.py): JWT payload を trip_ids 配列 → session_id のみに変更。認可判定は user_trip_access を JOIN で参照。並列付与は ON CONFLICT DO NOTHING で idempotent 化
  • 旧 Cookie 移行ミドルウェア (app/middleware/legacy_cookie_migration.py): 独立モジュールとして分離。リリース 2-3 ヶ月後に 1 行削除で剥がせる設計
  • Firebase Auth 統合エンドポイント (POST /auth/link): ID トークン検証 + 匿名 user と認証済 user のマージロジック
  • page/block 認可を JOIN 1 発化: 2 クエリ → 1 クエリ (最頻出エンドポイント)
  • docs/requirements.md を実態に合わせて全面更新

フロントエンド

  • lib/firebase.tsgetFirebaseAuth を追加(FCM 用に既に導入済みの Firebase App を流用)
  • atoms/auth.ts: authUserAtom (User | null | undefined、初期化前/未認証/認証済を区別)
  • hooks/useAuth.ts: useAuthStateSync を App 直下で呼び、onAuthStateChanged で atom を更新。マウント時にマジックリンクを検出したら signInWithEmailLinkPOST /auth/link で session と firebase_uid を紐付け
  • dialogs/SyncSignInDialog.tsx: メールアドレス入力 → sendSignInLinkToEmail。email は localStorage に保存し、戻ってきた時に使う
  • components/SyncSection.tsx: 未認証時のみ表示される旅程一覧上のバナー的セクション(ポジティブ訴求、iOS Cookie 制約は明示しない)
  • HomePage.tsx: 「最近見た旅程一覧」直下に SyncSection を配置

DB スキーマ (models, migration, docs/db_schema.md) は #212 に分離。

設計上の意思決定

Firebase Auth を "必須" ではなく "オプション(バックアップ)" に変更:

  • Grill-me で以下 2 点が判明したため:
    1. Firebase Hosting rewrites で same-origin 化する案は WebSocket 非対応 で Phase 2 と衝突
    2. Cookie の Max-Age を長くしても iOS Safari の ITP で 7 日パージ されうる (Cloud Run + Firebase Hosting の IP prefix 不一致)
  • そのため Cookie 紛失を救う根本手段として "メール認証 (オプション)" を採用
  • 編集自体はログイン不要 (URL 知ってる人 = 誰でも編集可) を維持

認可判定を常に user 単位に統一 (匿名 user + 認証済 user):

  • 認証状態でモード切り替えするより一系統の方が実装がシンプル
  • 匿名 user は Cookie 発行時に自動作成、認証時に firebase_uid を埋めて昇格 or 既存 user へマージ

旧 Cookie 移行ミドルウェアの分離:

  • 本体 auth.py には旧形式のロジックを持たせない
  • 削除は main.pyadd_middleware 1 行 + ファイル一式の削除で完結

"同期" セクションの UI トーン:

  • モーダルではなく 旅程一覧ページの常設セクション として配置。押しつけがましくならないよう
  • CTA 文言は「同期する」("登録"感を避ける)
  • iOS Cookie 制約は明示せず、複数デバイス同期・機種変更対応のポジティブ訴求のみ

意図的にスコープ外(別 issue)

  • 旅程一覧のアーカイブ機能 — 別 issue で対応
  • 認証なしでのデバイス間引き継ぎ(引き継ぎコード方式) — 別 issue で対応(メール認証したくないユーザー向け)
  • サインアウト UI — 今回は signOut() 関数のみ提供。UI 露出は次のイテレーション

Test plan

  • pnpm dotenvx run -- sh -c 'cd server && uv run pytest' (142 passed、新規 22 追加)
  • ruff format / check / mypy
  • frontend: pnpm run type-check, pnpm run lint:check, pnpm run format:check
  • Firebase コンソールで Email link 認証を有効化、authorized domains に本番/ステージング URL を追加
  • staging 環境で旧 Cookie を持つブラウザからアクセス → 新 Cookie に切り替わることを実機確認
  • / で「同期する」ボタン → メール受信 → 同じデバイスの同じブラウザでリンクをタップ → 認証完了、SyncSection が非表示になる
  • PWA (ホーム画面追加) 経由でマジックリンクをタップした時の deeplink 戻り挙動 (iOS 16.4+ / Android)
  • 旅程削除後の旧 Cookie アクセスで FK 違反 500 が発生しないこと(レビュー指摘の Critical 対応済み)

関連

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 404a8c86-2891-4c9d-bddd-2c1b2356ff37

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jul 30, 2026

Copy link
Copy Markdown

Visit the preview URL for this PR (updated for commit 80241fe):

https://tabi-share-8ef6b--pr210-feature-issue194-ses-x3pfpgck.web.app

(expires Sat, 08 Aug 2026 02:42:36 GMT)

🔥 via Firebase Hosting GitHub Action 🌎

Sign: 9f2a87ede127df7673322845e34cf22c1372d720

@kuu13580
kuu13580 changed the base branch from develop to feature/issue194_session-id-db August 1, 2026 01:09
@kuu13580
kuu13580 force-pushed the feature/issue194_session-id-db branch from 87ce807 to 35e67d5 Compare August 1, 2026 02:39
- auth.py を Cookie session_id + user_trip_access 参照方式に書き換え
- grant_trip_access は匿名 user/session の自動発行と ON CONFLICT
  DO NOTHING による idempotent な付与に対応
- 認可判定 (require_*_access) を async + DB クエリ化
- routers/trips.py の呼び出し側を await + db 引数追加に対応
- tests を新方式に合わせて更新 (JWT payload / DB 状態の検証)

挙動変更: trip 削除時に user_trip_access が CASCADE 削除されるため、
削除後の GET は 404 ではなく 403 を返す。
- LegacyCookieMigrationMiddleware を app/middleware/ 下に配置
- 旧形式 (trip_ids 配列 payload) の JWT を検出したら匿名 user + session を
  作成し、trip_ids を user_trip_access に ON CONFLICT DO NOTHING で移行、
  新形式 JWT を Set-Cookie
- 本体 auth.py には旧形式のロジックを持たせない (剥がしやすさ優先)。
  リリース 2-3 ヶ月後、main.py の add_middleware 1 行とファイル一式を
  削除するだけで撤去できる
- middleware は db_connection.AsyncSessionLocal を属性 lookup で参照。
  conftest でこの属性をテスト用 factory に差し替える
- 統合テストを追加 (tests/test_legacy_cookie_migration.py)
匿名 user (Cookie 経由) を Firebase 認証済み user に紐付ける API を実装。
"バックアップ・追加機能" 位置づけで、編集の必須要件ではない。

- パターン 1: 同 firebase_uid の user が未存在 → 匿名 user の firebase_uid を
  埋めて認証済に昇格
- パターン 2: 同 firebase_uid の user が既存 → 匿名 user の user_trip_access を
  ON CONFLICT DO NOTHING で統合し、session を振り替え、匿名 user を削除。
  archived フラグは既存側を優先 (ON CONFLICT の性質)
- パターン 3: 再認証は idempotent (何もしない)
- verify_id_token 失敗 / session 無しは 401
- 5 つの統合テスト (verify_id_token は monkeypatch)
require_page_access / require_block_access は "trip_id 解決 → 認可判定" を
別々の SELECT で 2 回投げていたが、EXISTS を絡めた JOIN 1 発に統合。
ページ・ブロック CRUD (ドラッグ&ドロップ等) の DB 往復数を認可分だけ削減する。
- 認証・共有機能: 「編集は Firebase Auth 必須」を撤回、URL を知っている
  人は誰でも閲覧・編集可能に更新。メール認証はバックアップ・追加機能として
  明記
- 技術スタック: 実態に合わせて MySQL → PostgreSQL, Railway → Cloud Run
- iOS Safari ITP による Cookie 7 日パージを技術制約に記載
- Phase 2 に FCM 通知、Phase 3 に一覧アーカイブを追加
- URL 設計・セキュリティ要件・データ構造・開発フェーズ・コスト管理も
  現状の実装に整合
- lib/firebase.ts に getFirebaseAuth を追加 (FCM 用の Firebase App を流用)
- atoms/auth.ts: authUserAtom (User | null | undefined、初期化前/未認証/認証済)
- hooks/useAuth.ts: useAuthStateSync / sendMagicLink /
  completeMagicLinkSignInIfPresent / signOut
- App 直下で useAuthStateSync を呼び、マウント時にマジックリンクを検出したら
  signInWithEmailLink → POST /auth/link で session と firebase_uid を紐付け
- SyncSignInDialog: メールアドレス入力 → sendSignInLinkToEmail
  (email は localStorage に保存、戻り時に signInWithEmailLink で使用)
- SyncSection: 未認証時のみ表示される旅程一覧上のバナー的セクション
  (ポジティブ訴求のみ、iOS Cookie 制約は明示しない)
- HomePage の「最近見た旅程一覧」直下に配置

編集は従来通り URL 知ってる人 = 誰でも可。メール認証は "バックアップ・
追加機能" として位置付ける。実機検証項目は PR に記載。
@kuu13580
kuu13580 force-pushed the feature/issue194_session-id branch from fcc7aaf to 80241fe Compare August 1, 2026 02:41
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.

1 participant