Updated: 10/18/2025, 5:40:23 PM
10/18/2025, 5:40:23 PM
認証システムの実装で最もつまずきやすいのが、「誰が」「いつ」「何を」するのかが曖昧になることです。
よくある失敗例:
❌ 「トークンを保存する」← どこに?いつ?
❌ 「トークンを検証する」← 誰が?どのタイミングで?
❌ 「ログアウトする」← クライアント側?サーバー側?両方?
これらの疑問を解消するのがシーケンス図です。
シーケンス図の利点:
認証システムは状態遷移の塊です:
未認証 → 認証中 → 認証済み → セッション期限切れ → 再認証 → ログアウト
この状態遷移を頭の中だけで管理しようとすると、必ず実装時に混乱します。
実装前にシーケンス図を描くべき理由:
設計の抜け漏れを防ぐ
チーム内で認識を統一
実装の優先順位が明確になる
認証システムを設計するとき、私が最も重視しているのは:
Tip
💡 重要な気づき: これらを独立したフローとして設計すると実装が格段にシンプルになる
5つの独立したフロー:
なぜ独立させるのか?
❌ 悪い設計:全部を一つの巨大な関数で処理
→ if文の嵐、状態管理が複雑、テストが困難
✅ 良い設計:各フローを独立したエンドポイント・関数に
→ 単一責任、テストしやすい、変更に強い
API設計例:
POST /auth/signup/send-code → サインアップ開始
POST /auth/signup/verify-code → サインアップ完了
POST /auth/login → ログイン
POST /auth/refresh → トークン更新(ログイン中)
POST /auth/logout → ログアウト
DELETE /auth/account → 退会
各エンドポイントが独立しているため、実装・テスト・デバッグが容易になります。
採用方式:Instagram方式(メール確認後に即ログイン)
mermaid
設計のポイント:
client_idの役割
目的:リフレッシュトークンの不正利用を防ぐ
シナリオ:
攻撃者がリフレッシュトークンを盗んだ
→ でもclient_idが分からない
→ トークン更新できない
client_idの種類
javascript
Redisの保存構造(仮登録)
Key: signup:{email}
Value: {
"password_hash": "$2a$10$...",
"code": "123456",
"client_id": "web-app-v1",
"created_at": "2025-10-19T10:00:00Z"
}
TTL: 900秒 (15分)
Redisの保存構造(本登録後)
Key: refresh_token:{uuid}
Value: {
"user_id": 123,
"client_id": "web-app-v1",
"created_at": "2025-10-19T10:00:00Z",
"ip_address": "192.168.1.1", // オプション
"device_info": "iPhone 15 Pro" // オプション
}
TTL: 2592000秒 (30日)
既存ユーザーが認証情報を使って入る流れ
mermaid
設計のポイント:
client_idの保存場所(クライアント側)
typescript
エラーメッセージの工夫
❌ 悪い例:
"メールアドレスが存在しません"
"パスワードが間違っています"
✅ 良い例:
"メールアドレスまたはパスワードが間違っています"
レート制限の実装
mermaid
最も重要なフロー:トークン期限切れの自動処理とセキュリティ検証
mermaid
client_id検証の重要性:
攻撃シナリオの防止
攻撃者の行動:
1. 何らかの方法でリフレッシュトークンを盗む
(例:ネットワーク盗聴、マルウェア、フィッシング)
2. 自分のデバイスでトークンリフレッシュを試みる
→ client_id が "attacker-device-v1" になる
3. サーバー側で検証
stored_client_id: "web-app-v1"
request_client_id: "attacker-device-v1"
→ 不一致!
4. トークンを即座に無効化
→ 攻撃失敗
→ 正規ユーザーは次回ログイン時に気づく
セキュリティレイヤーの多層化
第1層:リフレッシュトークン(UUID)
→ ランダムで推測不可能
第2層:client_id
→ トークンとセットで必要
→ デバイス/プラットフォーム固有
第3層:Redis TTL
→ 30日で自動失効
第4層:トークンローテーション
→ 使用後は即座に無効化
実装のポイント
go
クライアント側の実装例
typescript
セッションを明示的に終了する
mermaid
設計のポイント:
ログアウト時のclient_id検証
理由:
攻撃者がリフレッシュトークンだけ盗んでも
client_idがないとログアウトもできない
→ 正規ユーザーのセッションを勝手に切れない
全デバイスログアウト(オプション)
mermaid
より効率的な実装(推奨):
Redis構造を工夫:
user:{user_id}:sessions = Set of {uuid}:{client_id}
ログイン時:
SADD user:123:sessions "uuid-1:web-app-v1"
全ログアウト時:
SMEMBERS user:123:sessions
→ 各UUIDに対してDEL refresh_token:{uuid}
→ DEL user:123:sessions
アカウントを完全に削除する
mermaid
設計のポイント:
セッション管理の改善版
従来:全トークンをスキャン
KEYS refresh_token:* → 重い!
改善:ユーザーごとにセッションリスト管理
user:{user_id}:sessions → 軽い!
構造:
user:123:sessions = Set {
"uuid-1:web-app-v1",
"uuid-2:ios-app-v1",
"uuid-3:android-app-v1"
}
client_idも含めたセッション管理
go
シーケンス図の重要性
5つの独立したフロー
client_idによるセキュリティ強化
実装の実践的なポイント
次回は 「トークンの保存場所とセキュリティ」 です。
特に「HttpOnly Cookieはセキュアだけどモバイルで使えない」という現実的な問題と、client_idを含めた実践的なトークン管理について詳しく解説します。