認証API フロー図(エラー処理含む) - nyg1971/business_api GitHub Wiki
認証API フロー図(エラー処理含む)
🎯 全体フロー概要
[フロントエンド] ←→ [Rails API] ←→ [PostgreSQL]
↓
[JWT認証システム]
🔐 1. サインアップフロー
[ユーザー入力]
↓
┌─────────────────────────────────────────┐
│ POST /api/v1/auth/signup │
│ { │
│ "user": { │
│ "email": "[email protected]", │
│ "password": "password123", │
│ "password_confirmation": "...", │
│ "role": "staff" │
│ } │
│ } │
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ AuthController#signup │
│ │
│ 1. user = User.new(user_params) │
│ 2. if user.save │
└─────────────────────────────────────────┘
↓
【分岐】
↓
┌─────────────────────────────────────────┐ ┌─────────────────────────────────────────┐
│ ✅ 成功パス │ │ ❌ 失敗パス │
│ │ │ │
│ 3. token = JsonWebToken.encode(...) │ │ 3. バリデーションエラー │
│ 4. render json: { │ │ 4. render json: { │
│ token: token, │ │ errors: user.errors.full_messages │
│ user: user.as_json(...), │ │ } │
│ expires_at: 24.hours.from_now │ │ 5. status: :unprocessable_entity │
│ } │ │ (HTTP 422) │
│ 5. status: :created (HTTP 201) │ │ │
└─────────────────────────────────────────┘ └─────────────────────────────────────────┘
↓ ↓
┌─────────────────────────────────────────┐ ┌─────────────────────────────────────────┐
│ 📱 フロントエンド受信 │ │ 📱 フロントエンド受信 │
│ │ │ │
│ - JWTトークンをローカルストレージに保存 │ │ - エラーメッセージを表示 │
│ - ユーザー情報を状態管理に保存 │ │ - フォームにエラー表示 │
│ - ダッシュボードへリダイレクト │ │ - ユーザーに修正を促す │
└─────────────────────────────────────────┘ └─────────────────────────────────────────┘
🚨 よくあるサインアップエラー
エラー内容 |
原因 |
HTTPステータス |
レスポンス例 |
Email重複 |
既存ユーザー |
422 |
{"errors": ["Email has already been taken"]} |
パスワード短い |
Devise設定 |
422 |
{"errors": ["Password is too short"]} |
パスワード不一致 |
確認入力ミス |
422 |
{"errors": ["Password confirmation doesn't match"]} |
Email形式不正 |
バリデーション |
422 |
{"errors": ["Email is invalid"]} |
🔑 2. ログインフロー
[ユーザー入力]
↓
┌─────────────────────────────────────────┐
│ POST /api/v1/auth/login │
│ { │
│ "email": "[email protected]", │
│ "password": "password123" │
│ } │
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ AuthController#login │
│ │
│ 1. user = User.find_by(email: ...) │
│ 2. if user&.valid_password?(...) │
└─────────────────────────────────────────┘
↓
【分岐】
↓
┌─────────────────────────────────────────┐ ┌─────────────────────────────────────────┐
│ ✅ 認証成功 │ │ ❌ 認証失敗 │
│ │ │ │
│ 3. token = JsonWebToken.encode(...) │ │ 3. render json: { │
│ 4. render json: { │ │ error: 'Invalid credentials' │
│ token: token, │ │ } │
│ user: user.as_json(...), │ │ 4. status: :unauthorized │
│ expires_at: 24.hours.from_now │ │ (HTTP 401) │
│ } │ │ │
│ 5. status: :ok (HTTP 200) │ │ │
└─────────────────────────────────────────┘ └─────────────────────────────────────────┘
↓ ↓
┌─────────────────────────────────────────┐ ┌─────────────────────────────────────────┐
│ 📱 フロントエンド受信 │ │ 📱 フロントエンド受信 │
│ │ │ │
│ - JWTトークンをローカルストレージに保存 │ │ - エラーメッセージを表示 │
│ - ユーザー情報を状態管理に保存 │ │ - ログインフォームにエラー表示 │
│ - ダッシュボードへリダイレクト │ │ - パスワード欄をクリア │
└─────────────────────────────────────────┘ └─────────────────────────────────────────┘
🚨 ログインエラーパターン
エラー内容 |
原因 |
処理 |
ユーザー未存在 |
User.find_by(email: ...) が nil |
401エラー |
パスワード間違い |
valid_password? が false |
401エラー |
アカウント無効 |
Deviseの確認待ち等 |
401エラー |
🔒 3. 認証必須API呼び出しフロー
[フロントエンド]
↓
┌─────────────────────────────────────────┐
│ GET /api/v1/auth/me │
│ Headers: │
│ Authorization: Bearer eyJhbG... │
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ Base Controller#authenticate_request │
│ (before_action自動実行) │
│ │
│ 1. header = request.headers['Auth...'] │
│ 2. token = header.split(' ').last │
│ 3. decoded = JsonWebToken.decode(token) │
│ 4. @current_user = User.find(...) │
└─────────────────────────────────────────┘
↓
【分岐】
↓
┌─────────────────────────────────────────┐ ┌─────────────────────────────────────────┐
│ ✅ 認証成功 │ │ ❌ 認証失敗 │
│ │ │ │
│ 5. AuthController#me 実行 │ │ 5. rescue JWT::DecodeError │
│ 6. render json: { │ │ 6. render_unauthorized │
│ user: current_user.as_json(...) │ │ 7. render json: { │
│ } │ │ error: 'Unauthorized' │
│ 7. status: :ok (HTTP 200) │ │ } │
│ │ │ 8. status: :unauthorized (HTTP 401) │
└─────────────────────────────────────────┘ └─────────────────────────────────────────┘
↓ ↓
┌─────────────────────────────────────────┐ ┌─────────────────────────────────────────┐
│ 📱 フロントエンド受信 │ │ 📱 フロントエンド受信 │
│ │ │ │
│ - ユーザー情報を表示 │ │ - ログアウト処理 │
│ - ダッシュボード更新 │ │ - ログインページへリダイレクト │
│ - 他のAPI呼び出し続行 │ │ - トークンをローカルストレージから削除 │
└─────────────────────────────────────────┘ └─────────────────────────────────────────┘
🚨 JWT認証エラーパターン
エラー内容 |
原因 |
HTTPステータス |
対処法 |
トークンなし |
Authorizationヘッダー未設定 |
401 |
ログイン画面へ |
トークン形式不正 |
Bearer以外、分割失敗 |
401 |
ログイン画面へ |
トークン期限切れ |
24時間経過 |
401 |
再ログイン要求 |
トークン改ざん |
署名検証失敗 |
401 |
ログイン画面へ |
ユーザー削除済み |
User.find失敗 |
401 |
ログイン画面へ |
🔄 4. 実際の開発フロー例
📱 フロントエンド側の実装例
// サインアップ
async function signup(userData) {
try {
const response = await fetch('/api/v1/auth/signup', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ user: userData })
});
if (response.ok) {
const data = await response.json();
localStorage.setItem('token', data.token);
localStorage.setItem('user', JSON.stringify(data.user));
window.location.href = '/dashboard';
} else {
const errors = await response.json();
displayErrors(errors.errors);
}
} catch (error) {
console.error('Network error:', error);
}
}
// 認証必須API呼び出し
async function fetchUserInfo() {
const token = localStorage.getItem('token');
try {
const response = await fetch('/api/v1/auth/me', {
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
if (response.ok) {
const data = await response.json();
updateUserDisplay(data.user);
} else if (response.status === 401) {
// 認証エラー: ログアウト処理
localStorage.removeItem('token');
localStorage.removeItem('user');
window.location.href = '/login';
}
} catch (error) {
console.error('API error:', error);
}
}
🎯 まとめ:認証フローの要点
✅ 成功パターン
- サインアップ/ログイン → JWTトークン発行
- トークン保存 → フロントエンドのローカルストレージ
- API呼び出し → Authorization: Bearer [token]
- 自動認証 → before_actionで透明な認証
- ユーザー特定 → current_userで即座にアクセス
🚨 エラーハンドリング
- バリデーションエラー → 422でフォーム再表示
- 認証エラー → 401でログイン画面へ
- トークン期限切れ → 自動ログアウト処理
- ネットワークエラー → リトライまたはエラー表示
この認証システムにより、安全で使いやすいAPIが実現できます🚀