はじめに
ちょっと認証基盤を調べる機会があり、全くわからなかったのでAIと対話しながら記事を作ってみました。
私の脳みそを補完するようになっているかもなので、これはご了承くださいませ。
OAuth 2.0, JWT, OpenID Connect, SSO を完全理解
📚 認証基盤・共通基盤の概要
認証基盤とは、ユーザー認証と認可を一元管理し、複数のアプリケーション間でセキュアに情報やアクセス権を共有するための基盤です。
🏢 わかりやすい例え
空港のセキュリティゲートをイメージしてください。
- 認証基盤 = 空港全体のセキュリティシステム
- 認証 = パスポートであなたが本人であることを確認
- 認可 = チケットを確認して「このゲート使用可能」「VIP ラウンジ利用可能」などを判定
複数のアプリケーション間でのシームレスな認証・認可とは、異なる空港やテナントでも同じパスポート(認証情報)とチケット(アクセス権)で移動できる、みたいなイメージです。
🎯 主な目的
- ユーザー認証の一元化
- 複数アプリケーション間のシームレスな認証
- 安全な認可情報の管理
- ユーザーデータの保護
核となるプロトコル
- OAuth 2.0: 認可プロトコル(アクセス権の委譲)
- OpenID Connect (OIDC): 認証層(ユーザーの身元確認)
- SAML 2.0: エンタープライズ認証・認可プロトコル
🔐 認証(Authentication)と認可(Authorization)の違い
セキュリティの基礎となる「認証」と「認可」は、異なる概念です:
- 認証(Authentication):あなたが本人であることを確認するプロセス。「あなたは誰ですか?」という質問に答える。
- 認可(Authorization):認証されたユーザーが特定のリソースやアクションにアクセスする権限を持っているか確認するプロセス。「あなたは何ができますか?」という質問に答える。
📍 飲食店での例えで理解する
シナリオ:高級レストランでコース料理を注文
-
認証:「山田太郎さんですね?」と確認し、会員カード(身分証明書)を確認する
- 実装例:ID/パスワード検証、本人確認
- 質問:「あなたは本当にこのシステムのユーザーですか?」
-
認可:「山田太郎さんは今月の利用額が○○円でプレミアム会員です。Aコース(通常)とPコース(プレミアム)のどちらが注文できますか?」
- 実装例:ユーザーのグループ・権限・スコープの確認
- 質問:「このユーザーには何のアクセス権限がありますか?」
➜ つまり:会員カード(有効なTOKEN)があっても、すべてのメニュー(機能)が使用できるわけではない
🔄 OAuth 2.0 の認証・認可フロー(Web API版)
OAuth 2.0 で特に重要なポイント:OAuth 2.0 の「認可サーバー」は実際には「認証」と「認可」の両方を実行します。これが混乱を招く理由です。
📊 フロー詳細:Uber Eats × Google ログインの例
| ステップ | 担当 | 実際の処理内容 | DBアクセス | 説明 |
|---|---|---|---|---|
| 1-3 | クライアント + ユーザー | Uber Eats がユーザーを Google にリダイレクト | — | ユーザーが「Google でログイン」をクリック |
| 4-8 | 認証サーバー 🔐 | 認証:ID/PW 検証 + 認可:権限判定 | ✓ DB 確認 | Google のサーバーがユーザーの ID/PW を確認し、ユーザー情報と権限をデータベースから確認 |
| 9 | 認証サーバー 🔐 | TOKEN 発行 (user_123 を埋め込み) | — | ユーザー ID を含むアクセストークンを生成して返却 |
| 10-12 | クライアント + リソースサーバー | Uber Eats がアクセストークンを使ってユーザー情報を取得 | ✓ DB 確認 | Uber Eats のサーバーが TOKEN を検証し、ユーザーが本当に権限を持っているか確認 |
🎯 重要なポイント:TOKEN はどこに保存されている?
❓ よくある質問:「認証サーバーで TOKEN を発行しているのに、複数のサーバーで確認できるのはなぜ?」
✅ 答え:実は同じサーバーです!OAuth 2.0 用語の混乱が起きやすいポイント。
-
Google の「認証サーバー」
- ユーザーの ID/PW を受け取って検証(認証)
- ユーザーの権限・スコープを確認(認可)
- アクセストークン(TOKEN)を生成・発行
- 保存場所:Google のデータベースに TOKEN ハッシュなどを記録
-
Uber Eats の「リソースサーバー」
- Google から受け取った TOKEN を検証
- 保存場所:TOKEN を保存していない。Google の認証サーバーに問い合わせるか、JWT ハッシュで検証
- TOKEN が有効なら、ユーザー情報を取得する権限があると判断
つまり:
- token が「有効」かどうかは Google の「認証サーバー」が管理
- token が「何の権限を持っているか」は Google の「認証サーバー」が決定
- Uber Eats は TOKEN をもらって「本当に有効か?」を Google に確認するだけ
🎫 TOKEN の詳細構造(Opaque Token vs JWT)
TOKEN の実装方式には大きく2つのパターンがあります。
🔐 Opaque Token(不透明なトークン)
ランダムな文字列で、中身が何かわからないタイプのトークンです。多くの開発者が直感的にイメージしやすい実装パターンです。
Token: "7x9k2m_secure_token_abc123xyz" ← ただのランダムな文字列
検証フロー
- リソースサーバーが TOKEN を受け取る
- 認証サーバーに問い合わせ:「このtokenは本物?」
- 認証サーバーのDB検索
- DB から user_id を取得
- 検証完了
認証サーバーのDB例
| TOKEN | user_id | expires | |
|---|---|---|---|
7x9k2m_secure_token_abc123xyz |
user_123 | taichi@example.com | 2026-03-09 11:00 |
5m2k9x_another_token_def456ghi |
user_456 | hanako@example.com | 2026-03-09 12:30 |
✅ メリット
- 情報が隠蔽されているのでセキュアに見える
- サーバーサイドで完全に制御可能
- 即座に TOKEN をリボーク(無効化)可能
❌ デメリット
- 毎回 DB 検索が必要 → スケーラビリティに課題がある
- ネットワーク遅延が増加
- 複数のマイクロサービスがある場合、全て認証サーバーに問い合わせ → ボトルネック
- 認証サーバーが落ちたら全て使えない(単一障害点)
📝 JWT Token(署名付きトークン)
ユーザー情報を含む署名済みのトークン。自己検証可能です。
JWT の構造(3つの部分に分かれている)
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiMTIzIiwiZW1haWwiOiJ0YWljaGlAZXhhbXBsZS5jb20iLCJzY29wZSI6InByb2ZpbGUgZW1haWwifQ.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
📋 ヘッダー(Header)
{
"alg": "HS256",
"typ": "JWT"
}
📦 ペイロード(Payload)← ユーザー情報
{
"user_id": "user_123",
"email": "taichi@example.com",
"name": "Taichi Yamada",
"scope": "profile email",
"role": "user",
"iat": 1678000000,
"exp": 1678003600
}
🔐 署名(Signature)の役割 — 改ざん防止メカニズム
秘密鍵で署名(認証サーバーだけが持つ秘密鍵)
HMACSHA256(
base64(header) + "." + base64(payload),
secret_key // ← Google だけが持つ秘密鍵
)
= "TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ"
リソースサーバーの検証(公開鍵で署名を検証)
jwt.verify(
token,
google_public_key // ← 世界中に公開されている公開鍵
)
✅ 署名が有効 → TOKEN はGoogle本物
❌ 署名が無効 → TOKEN は改ざんされた
改ざん検出の仕組み — なぜ改ざんを検出できるのか
| シナリオ | ペイロード | 署名 | 検証結果 |
|---|---|---|---|
| ✅ 正規の TOKEN | user_123 |
TJVA95... |
✅ 有効 |
| ❌ 改ざん試行 | user_999 |
TJVA95...(元のまま) |
❌ 改ざんを検出 |
✅ メリット
- DB 検索不要 → 署名検証だけで有効性確認
- マイクロサービス間でも独立で検証可能
- スケーラビリティが優れている
- ステートレス(サーバーが状態を保つ必要がない)
- 改ざん検出できる
❌ デメリット
- 一度発行されたら期限まで無効化できない
- TOKEN サイズがやや大きい(Opaque比)
- ペイロードが Base64 なので BASE64 デコードで見える(秘密情報は入れてはいけない)
🛡️ TOKEN に何を入れてOK、何がNG?
TOKEN はブラウザやクライアントに保存されるため、セキュリティリスクを考慮して情報を選定する必要があります。判断基準は「この情報が外部に漏れても問題ないか」です。
✅ TOKEN に含めてOK(公開情報)
- user_id:ユーザーの一意識別子。ユーザーが誰かを特定するため必須。
- email:メールアドレス。ユーザーが公開範囲で同意した情報として扱える。
- name:ユーザー名。プロフィール情報として公開可能。
- scope / role:権限情報。何ができるかはシステムが判定するので公開OK。
- phone:電話番号(ユーザーが同意した場合のみ)。
- iat / exp:TOKEN の発行日時・有効期限。検証に必要。
❌ TOKEN に含めてはいけない(秘密情報)
- password:パスワード。絶対に TOKEN に含めてはいけない。
- password_hash:ハッシュ化されたパスワード。ハッシュであっても入れてはダメ。
- credit_card:クレジットカード番号。TOKEN がバレたら金銭被害。
- api_key / secret:API キーや秘密鍵。TOKEN がバレたら他者が不正使用。
- ssn / individual_number:マイナンバーなど個人識別情報。法的に保護される情報。
- secret_answer:秘密の質問の答え。セッションハイジャック対策が無効化。
⚠️ TOKEN に含める判断フロー
Q1:この情報がバレても問題ないか?
- YES → TOKEN に含める候補
- NO → 絶対に入れない
Q2:ユーザーが同意しているか?
- YES → TOKEN に含めて OK
- NO / 不明 → 検討が必要
Q3:HTTPS で保護されているか?
- YES → 相対的に安全
- NO → 傍受可能 = 秘密情報は入れられない
💡 実装例:これからのあなたのコード
// ✅ GOOD:公開情報のみ
const token = jwt.sign(
{
user_id: 'user_123',
email: 'taichi@example.com',
name: 'Taichi Yamada',
scope: 'profile email',
role: 'user'
},
secret_key,
{ expiresIn: '1h' }
);
// ❌ BAD:秘密情報を含める
const bad_token = jwt.sign(
{
user_id: 'user_123',
email: 'taichi@example.com',
password_hash: 'xxx...', // ❌ ダメ
api_key: 'sk-abc123...', // ❌ ダメ
credit_card: '1234-5678' // ❌ 違法
},
secret_key
);
💾 HTTPS + HttpOnly Cookie 保存が最強
TOKEN を JavaScript からアクセスできない HttpOnly Cookie に保存すれば:
- ✅ XSS 攻撃で盗まれない
- ✅ ブラウザが自動で送信
- ✅ HTTPS で暗号化
- ✅ SameSite フラグで CSRF 攻撃に対応
まとめ:TOKEN の選択基準
| 基準 | Opaque Token | JWT |
|---|---|---|
| スケーラビリティ | 低(毎回DB検索) | 高(署名検証のみ) |
| マイクロサービス | 向かない | ✅ 推奨 |
| リボーク(無効化) | ✅ 即座に可能 | ❌ ブラックリスト必要 |
| TOKEN サイズ | 小さい | やや大きい |
| シンプルさ | ✅ わかりやすい | 複雑 |
| 最新トレンド | レガシー | ✅ 業界標準 |
🆔 OpenID Connect (OIDC)
OpenID Connect は OAuth 2.0 の上に認証層を追加したプロトコルです。OAuth 2.0(認可)+ Id Token(認証)の組み合わせ。
🪪 身分証明書のアナロジー
- OAuth 2.0 だけの場合:「あなたのメール送信権を貸す」ってだけ → 受け取った側は「この権限の所有者が誰かわからない」
- OIDC の場合:「あなたが Taro(ユーザーID)であることを証明し、メール送信権も与える」 → 受け取った側は「Taro さんだ、権限も確認できた」と双方判明
OIDC フロー
1. ユーザーが Relying Party(Webアプリケーションやモバイルアプリ)にアクセス
↓
2. Relying Party → OpenID Provider にリダイレクト
↓
3. OpenID Provider = Google側で認証・認可
↓
4. ID Token (ユーザー認証証明) + Access Token (API利用権) を返却
↓
5. Relying Party がユーザー情報エンドポイントを呼び出し
↓
6. ユーザー詳細情報(プロフィール)を取得
↓
7. Relying Party でセッション作成 → ユーザーログイン完了
返されるトークン
| トークン | 用途 | 内容 |
|---|---|---|
| ID Token | ユーザー認証 | JWT形式、ユーザー情報を含む |
| Access Token | API へのアクセス | リソースサーバーへのアクセス権 |
| Refresh Token | トークン更新 | 新しいアクセストークン取得 |
OIDC の利点
✅ OIDC を使うべき場合
- ユーザー身元確認が必要
- ユーザープロフィール情報が必要
- SSO を実装したい
- モダンな認証フロー(SPA、モバイルアプリ)
🚪 Single Sign-On (SSO)
SSO は、一度のログインで複数のアプリケーション間でシームレスにアクセスできる仕組みです。
🏢 オフィスビルの鍵カードシステム
大きなオフィスビルを思い浮かべてください。
- SSO なし: 各フロア(営業部、企画部、財務部)ごとに別々のパスワード・鍵が必要 → 朝から何度もセキュリティチェック
- SSO あり: 朝、メインゲートで顔認証1回 → その後はすべてのフロアに自動でアクセス可能な鍵カードが配られる
これが SSO の威力です。企業内の Google Workspace、Microsoft 365 が良い例。一度ログインすれば、Gmail、Google ドライブ、カレンダーも全部使えますね。
SSO の仕組み(複数サービス間でのシーケンス)
1. ユーザーが Slack にアクセス
2. Slack が SSO サーバーに「このユーザーは認証済み?」と確認
3. SSO サーバーがログイン画面を表示
4. ユーザーがログイン
5. SSO サーバーが Slack に「user_123 は認証済み」と通知
6. Slack がセッション作成
7. ユーザーが GitHub にアクセス
8. GitHub が SSO サーバーに「このユーザーは認証済み?」と確認
9. SSO サーバーが「はい、user_123 は既に認証済み」と通知
10. GitHub がセッション作成 → つまり、ログイン画面を見ることなく即座にアクセス可能!
SSO の利点
✅ SSO を使うべき場合
- 複数のサービスを連携させたい
- エンタープライズ環境
- 一元的なUserセッション管理
- 社員がログイン情報を覚える負担軽減
🔒 セキュリティの脅威と対策
認証・認可への脅威と対策
| 脅威 | 対策 |
|---|---|
| ブルートフォース攻撃(辞書攻撃) | レート制限、アカウントロック、MFA |
| フィッシング | HTTPS必須、教育 |
| 認証情報盗聴 | HTTPS必須、HttpOnly Cookie |
| Token 盗難 | HTTPS、HttpOnly Cookie、SameSite |
| Token 改ざん | 署名検証、有効期限チェック |
| Token リプレイ | タイムスタンプチェック、ノンス |
| 中間者攻撃(MITM) | HTTPS必須 |
| CSRF攻撃 | CSRF Token、SameSite フラグ |
| XSS 攻撃 | XSS対策、CSP |
📊 プロトコル比較表
| 機能 | OAuth 2.0 | OpenID Connect | SAML 2.0 |
|---|---|---|---|
| 主な用途 | API認可 | 認証+認可 | エンタープライズSSO |
| 標準化 | ✅ RFC 6749 | ✅ Web Standards | ✅ OASIS |
| ユーザー情報 | ❌ 別途取得 | ✅ ID Token含む | ✅ Assertion含む |
| トークン形式 | Bearer Token, JWT | JWT (ID Token) | XML Signed/Encrypted |
| 署名 | ✅ JWT時 | ✅ 必須 | ✅ 必須 |
| モバイル対応 | ✅ 優秀 | ✅ 優秀 | ❌ 不得意 |
| 複雑度 | シンプル | 中程度 | 複雑 |
| 学習難度 | 簡単 | 中程度 | 難しい |
| 実装サンプル | Google, Twitter | Google, Microsoft | Okta, AD |
| 推奨用途 | SPA, モバイル | モダンWebアプリ | 大企業 SSO |
🎯 使い分けガイド
OAuth 2.0 を選ぶべき場合
- API への認可(権限委譲)が必要
- シンプルな実装が必要
- 複数の認証方式をサポート
- 例:Google フォト、Twitter 連携
OpenID Connect を選ぶべき場合
- ユーザー認証 + 認可の両方が必要
- モダンな SPA / モバイルアプリ
- 標準準拠の実装が必要
- 例:Google ログイン(Gmail等複数サービス連携)
SAML を選ぶべき場合
- 大企業・エンタープライズ環境
- 既に SAML インフラが存在
- XML ベースの要件
- 例:オフィス内でのActive Directory連携
🎓 実装時の注意点
1. HTTPS は必須
- TOKEN が傍受されると全て盗まれる
- 必ず HTTPS 使用
2. TOKEN の有効期限を設定
- 短い有効期限(15分~1時間)
- Refresh Token で更新
3. HTTPOnly Cookie 使用
- JavaScript からアクセスできない
- XSS 攻撃対策
4. CSRF Token / SameSite フラグ
- CSRF 攻撃対策
5. エラーメッセージは一般的に
- ユーザー情報を漏らさない
- ❌ "ユーザー foo のパスワードが間違っています"
- ✅ "ユーザー名またはパスワードが間違っています"
📚 参考資料
- RFC 6749 - The OAuth 2.0 Authorization Framework
- OpenID Connect Core 1.0
- SAML 2.0
- OWASP Authentication
✅ まとめ
| 概念 | 説明 |
|---|---|
| 認証 | 「あなた誰?」 = ユーザーの身元確認 |
| 認可 | 「何ができる?」 = ユーザーの権限確認 |
| TOKEN | 「身分証明書」 = Opaque / JWT の2種類 |
| OAuth 2.0 | 認可プロトコル = API アクセス権の委譲 |
| OIDC | 認証+認可 = OAuth 2.0 + ユーザー情報 |
| SSO | 複数サービス間 = 一度のログインで全アクセス |
| セキュリティ | HTTPS + HttpOnly + 署名検証 + 有効期限 |