feat(#194): 認可のセッションキー方式化とバックアップ用メール認証 (BE) - #210
Conversation
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
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. Comment |
|
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 |
87ce807 to
35e67d5
Compare
- 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 に記載。
fcc7aaf to
80241fe
Compare
概要
issue #194 の実装。認可を「
trip_ids配列 JWT」から「session_id+ DB (user_trip_access)」方式に移行し、Firebase Auth を "バックアップ・追加機能" として統合。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 化app/middleware/legacy_cookie_migration.py): 独立モジュールとして分離。リリース 2-3 ヶ月後に 1 行削除で剥がせる設計POST /auth/link): ID トークン検証 + 匿名 user と認証済 user のマージロジックdocs/requirements.mdを実態に合わせて全面更新フロントエンド
lib/firebase.tsにgetFirebaseAuthを追加(FCM 用に既に導入済みの Firebase App を流用)atoms/auth.ts:authUserAtom(User | null | undefined、初期化前/未認証/認証済を区別)hooks/useAuth.ts:useAuthStateSyncを App 直下で呼び、onAuthStateChangedで atom を更新。マウント時にマジックリンクを検出したらsignInWithEmailLink→POST /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 を "必須" ではなく "オプション(バックアップ)" に変更:
認可判定を常に user 単位に統一 (匿名 user + 認証済 user):
firebase_uidを埋めて昇格 or 既存 user へマージ旧 Cookie 移行ミドルウェアの分離:
auth.pyには旧形式のロジックを持たせないmain.pyのadd_middleware1 行 + ファイル一式の削除で完結"同期" セクションの UI トーン:
意図的にスコープ外(別 issue)
signOut()関数のみ提供。UI 露出は次のイテレーションTest plan
pnpm dotenvx run -- sh -c 'cd server && uv run pytest'(142 passed、新規 22 追加)pnpm run type-check,pnpm run lint:check,pnpm run format:check/で「同期する」ボタン → メール受信 → 同じデバイスの同じブラウザでリンクをタップ → 認証完了、SyncSection が非表示になる関連