サーバーで所有権を導出する
レコード ID は何かを識別しますが、誰が読めるかを証明しません。supastack はユーザー所有のレコードを workspaceId でスコープします。サーバーは認証済みユーザーのワークスペースを解決し、レコードの読み取りや変更時にそのスコープを含めます。
ブラウザから送信されたワークスペース ID を所有権の証明として扱わないでください。ブラウザはアクションを要求できますが、信頼されたサーバーコンテキストが誰のものかを確立しなければなりません。このルールは、タスク状態、保存されたプロバイダー設定、使用量レコード、すべての製品固有リソースに対して成立すべきです。レイアウトとコンポーネントは何を描画するかを絞れますが、描画は認可ではありません — チェックはレコードが実際に読み書きされる場所に属します。
この記事の他のすべてはそのルールから導かれます:所有権は導出されるものであり、要求されるものではありません。クライアント送信の workspaceId がどこかで信頼された瞬間、他のすべてのチェックは装飾になります。認証の記事がこの境界の上流でセッション自体がどう確立されるかをカバーしています。
メンバーシップも境界の一部
スコープはレコードがどのワークスペースに属するかに答え、メンバーシップはこのユーザーがそのワークスペースに属するかに答えます。両方をサーバーで解決します。supastack は個人用と組織用のワークスペースを owner・admin・member ロールでモデル化しています — ワークスペースガイドがそのモデルを文書化しています。
ロールは 2 つ目の軸を加えます:メンバーは admin だけが変更できるものを読めます。2 つのチェック — 「このワークスペースに属するか」と「この操作を実行できるか」— を区別したままにすれば、降格や削除も他のすべてと同じ境界で効果を持ちます。招待と削除は管理者の後付けではなく、同じライフサイクルの一部です。
メンバーシップ変更は即座に効果を持たなければなりません。削除されたメンバーの次のリクエストは、キャッシュされたトークンがたまたま期限切れになるときではなく、境界で失敗すべきです — これも、チェックが以前発行された認証情報の内部ではなくレコードが読まれる場所に置かれる理由です。
アクセスを許可せずに復旧を有用にする
タスク URL はユーザーがリフレッシュ後に戻れるようにします。作業への参照を保持するからこそ有用です。認証済み API は、状態や結果を返す前に、要求されたタスクが現在のワークスペースに属することを依然としてチェックしなければなりません。
同じ原則が送信復旧に適用されます。冪等性キーは繰り返されたリクエストの識別と重複作業の回避に役立ちます。セッションでもアクセストークンでもありません。保存された送信の再試行には依然として認証、ワークスペース所有権、適切な権限が必要です。
便利な経路こそ、分離が静かに侵食される場所です。正当なユーザーが作業を再開するのを助けるリンク — リフレッシュ後や完了メールから — は他のリクエストと同じ認証チェックを通らなければなりません。リクエストから結果へのパイプラインはまさにこの原則でタスクスコープの冪等性キーを使っています。
失敗すべきリクエストをテストする
所有者がレコードを取得できるテストはハッピーパスを証明します。分離は証明しません。同じレコードを要求する二番目のワークスペース、匿名リクエスト、別の所有者を名乗る書き込みと組み合わせてください。
永続化された結果と同じくらい外部の振る舞いをアサートします。拒否されたリクエストは別のワークスペースのデータを変更すべきではなく、エラーレスポンスは秘密や生のデータベース詳細を漏らすべきではありません。これらのチェックは実装が変わっても境界のレビューを保ちます。
- 2 つ目のワークスペースが同じレコード ID を要求 — 拒否。
- 匿名リクエストが同じエンドポイントに到達 — 拒否。
- ワークスペースから削除されたメンバーが再試行 — 拒否。
- 書き込みが別の所有者やワークスペースを指定 — 拒否。何も変更されない。
- エラーレスポンスは汎用的なまま — 秘密もスキーマ詳細もなし。
ルールを製品に持ち込む
新しい製品テーブルを追加するとき、画面を作る前にその所有権を決めます。永続化モデルに workspaceId を含め、既存のサーバー境界を通じてワークスペースを解決し、リストと詳細の両方の操作でそのコンテキストを使います。
バックグラウンドワーカーも同じ注意が必要です。キューされたタスクは正しいレコードを読み込むのに十分な信頼コンテキストを運び、プロバイダー認証情報はサーバーに留まるべきです。ベア ID で行を読み込むスケジュールジョブも境界の内側にあります。一貫した所有権ルールは、ルートごとの例外の集まりより維持が簡単です。
API 面も同じ境界に属します。ワークスペース API キーと署名付き送信 webhook はワークスペースのスコープを携えます — キーはテナントをまたいで読む手段になってはならず、webhook ペイロードは隣人のレコードを運んではなりません。API モジュールガイドがその面の形をカバーしています。
ワークスペース分離についてよくある質問
マルチテナント分離はどのプロジェクトでも同じ一握りの質問を生じさせます。早めに決着しておく価値があるのはこれらです。
- アプリケーションレベルのスコープで十分ですか、それとも行レベルセキュリティが必要ですか? — すべての読み書きでのサーバー強制スコープは、ほとんどの SaaS の脅威モデルをカバーします。データベース RLS は多層防御を加えますが、境界チェックの代替にはなりません — ワーカーと API キーにも同じルールが必要です。
- ワークスペース間の共有はどうあるべきですか? — スコープを弱めるのではなく、明示的にモデル化します — 招待、メンバーシップ行として。「リンクを持つ全員」は事故ではなく、独自のチェックを持つ設計された機能であるべきです。
- マルチテナントアプリで最初に壊れるものは? — 便利な経路です:復旧リンク、管理ツール、ベア ID でレコードを読むバックグラウンドワーカー。通常のスコープを通らずにレコードに触れるすべてのコードパスを監査してください。
- 既存テーブルにスコープをどう追加しますか? —
workspaceIdを追加し、バックフィルし、読み書きで強制してから UI に触れます。境界が想定ではなく証明されるよう、ネガティブテストを先に出荷してください。 - ページが保護されていればサーバーアクションにチェックは要りますか? — 要ります。ページガードは画面を隠しますが、操作を認可しません。すべてのアクションは独立してセッションとワークスペースを解決します。