一つのシステムに認証を所有させる
ログインフォームはリクエストを集めます。認証システムはそのリクエストが認証を確立するかを決め、アプリケーションの残りが信頼するセッションを発行します。この責任を一か所に保つことで、各ルートがログイン済みユーザーの独自解釈を発明するのを防ぎます。
supastack は認証とセッション管理に Better Auth を組み合わせます。アプリケーションコードは保護された境界でセッションを解決し、ワークスペースに関連付けます。インターフェースは有用なアクセス状態を表示できますが、クライアント状態が操作が認可された証拠になってはいけません。
このルールは製品が出荷するすべての面で成立しなければなりません。Web・モバイル・デスクトップのクライアントは同じ認証決定を消費すべきで、3 回実装すべきではありません — 「ログイン済み」の 3 つの解釈がアカウントバグの発生源です。
ログイン方法を意図的に選ぶ
すべての方法は、摩擦と攻撃対象面・外部依存のトレードオフです。パスワードにはリセットフローとレート制限が必要です。マジックリンクとワンタイムコードはメール到達性に依存します。OAuth は Google、GitHub、Apple などのプロバイダーに認証を委譲し — それらの障害・同意画面・コールバックルールを引き継ぎます。
supastack はこれらを Better Auth を通じて構成します — メール OTP、パスワード、マジックリンク、OAuth、利用可能な二要素認証とパスキー — を単一の認証決定の背後に置きます。対象ユーザーが実際に必要とする最小のセットを出荷してください。追加の方法ごとに、別のコールバック経路・別の失敗モード・ローンチ前に検証する別のフローが増えます。認証ガイドが配線をカバーしています。
一人が複数の方法でログインできる場合、方法同士をどう結ぶかを決めます。同じアドレスに解決される Google アカウントとメールマジックリンクは、2 つではなく 1 つの認証に着地すべきです。アカウント連携ルールは認証決定の一部であり、間違えると重複ユーザーが生まれ、数ヶ月後に不可解に消えたデータとして表面化します。
重要な認証情報はセッション
ログイン後、すべてのリクエストが運ぶのはパスワードではなくセッションです。その有効期限・保存・無効化ルールが実際のセキュリティ姿勢を定義します。ログアウト後も生き残るセッションや、削除されたワークスペースメンバーが使い続けられるセッションは、どんなに磨かれたログイン画面でも直せない認証の失敗です。
ライフサイクルは最初に決めます:セッションの寿命、アクティブなセッションをユーザーが確認・失効できるか、パスワード変更・メール変更・ワークスペース削除がそれらに何をするか。セッション管理がキットのモジュールとして存在するのは、まさにこれらの決定が先送りしやすく後付けが高くつくからです。
セッションを要求されたレコードに関連付ける
ログイン済みであることはすべてのレコードへのアクセスを許しません。サーバーでユーザーのワークスペースを解決し、そのスコープを読み取りと変更に含めます。タスク識別子や復旧リンクは作業への参照であり、それを取得する許可ではありません — ワークスペース境界の記事がその境界の側面を詳しくカバーしています。
スコープは固定の順序で解決します:セッションを認証し、メンバーシップを読み込み、次にそのワークスペースコンテキストで読み書きする — 逆は決してしません。ミドルウェアは匿名訪問者をリダイレクトできますが、操作を認可することはできません。チェックはレコードに触れるサーバーアクションかルートハンドラーに属します。
アカウントには始まりだけでなくライフサイクルがある
プロビジョニングはその境界の最初のテストです。ユーザーが登録するとき、続いて作られるワークスペースと所有権レコードは 2 回以上作成しても安全でなければなりません — ライフサイクルフックはプロバイダー・再試行・サポートツールによってリプレイされ得ます。重複イベントは重複組織ではなく、何もしない操作であるべきです。
ライフサイクルの終わりにも同じ注意が必要です。メール変更・パスワードリセット・メンバー削除・アカウント削除・データエクスポートは、それぞれ独自の悪用ケースを持つ小さなフローです — 2 回使えるリセットリンク、チームメイトのレコードを漏らすエクスポート。supastack が GDPR エクスポートとセッション管理を同梱するのはこのためです。何の上に作るにせよ、地味なフローに予算を取ってください。
終わりを含めてライフサイクルをテストする
成功したログインテストは有用ですが不完全です。無効な認証情報、期限切れまたは再利用された確認コード、匿名リクエスト、ワークスペース間アクセスを実行します。ログアウトがセッションを無効化し、戻ってきたクライアントが以前認可された画面で保護された作業を続けられないことを検証します。
ハンドラーテストとデプロイ受け入れを区別します。インメモリアダプターは、Postgres 永続化やプロバイダーコールバックを証明せずに実際の認証ハンドラーの意味論を確立できます。ログインフロー全体が機能すると主張する前に、設定されたオリジン、コールバックパス、セッション復元、ログアウトをデプロイ済み依存関係で検証してください。
- 無効な認証情報と、期限切れまたは再利用された確認コードが拒否される。
- 匿名およびワークスペース間リクエストが所有データを受け取らない。
- ログアウトがセッションを無効化する — 古いクライアントは動き続けられない。
- ライフサイクルフックがリプレイされてもプロビジョニング済みレコードが冪等のまま。
- 実プロバイダーのコールバックが設定されたオリジンで動作する — 想定ではなく検証済み。
認証についてよくある質問
以下の質問は、ログインフォームが完成し、システムが動き続けなければならなくなってから表面化します。
- ミドルウェアでルート保護は十分ですか? — ミドルウェアは便利なリダイレクトであり、認可層ではありません。各サーバーアクションとルートは自身でセッションとワークスペースを解決します。ページレベルのガードは画面を隠すだけで、操作を認可しません。
- 認証は自作すべきですか、ライブラリを使うべきですか? — セッションの意味論は SaaS が差別化する場所であることは稀です。明確なモジュール境界の背後にある Better Auth のような保守されたライブラリは、重要な決定 — スコープ、ライフサイクル — を自分のコードに残しながら、テスト済みのプリミティブを与えます。
- セッションとワークスペースはどう関係しますか? — セッションは人を特定し、ワークスペースはその人が触れてよいレコードを特定します。すべてのリクエストでサーバーでメンバーシップを解決し、クライアントが送るワークスペース ID は決して信頼しないでください。
- B2B 顧客は認証に何を期待しますか? — ロール付きチームワークスペース、OAuth ログイン、セッションの可視化と失効、監査証跡です。二要素認証とパスキーは管理ロールでますます期待されています。
- 本番で認証を壊すものは? — たいていは端です:ローカルにしか存在しないコールバック URL、2 回プロビジョニングするフック、メンバーシップより長生きするセッション。始まりだけでなくフローの終わりをテストしてください。