OAuthとOpenID Connect(OIDC)の違いを図解:「認可」と「認証」を正しく理解する
はじめに
「Googleでログイン」「GitHubでログイン」を実装するとき、よく「OAuth認証」という言葉が使われます。しかし厳密には、これは間違いです。
OAuth 2.0は「認可(Authorization)」のプロトコルであり、「認証(Authentication)」を行う仕様ではありません。認証を行うのは、OAuth 2.0の上に構築された OpenID Connect(OIDC) です。
この違いを曖昧にしたまま実装すると、セキュリティの脆弱性につながります。本記事では、両者の役割の違いをゼロから丁寧に解説します。
この記事で分かること
- 「認証」と「認可」がなぜ別物なのか
- OAuth 2.0が解決する問題と、できないこと
- OIDCがOAuth 2.0に何を追加したのか
- IDトークン・アクセストークン・リフレッシュトークンの使い分け
- 「OAuthで認証」が危険な理由
- Azure Entra IDでの具体的な使い方
1. 「認証」と「認可」は全く別の概念
1.1 それぞれの定義
| 概念 | 英語 | 意味 | 例 |
|---|---|---|---|
| 認証 | Authentication(AuthN) | 「あなたは誰か」を確認する | ログイン画面でID・パスワードを入力 |
| 認可 | Authorization(AuthZ) | 「何ができるか」を制御する | 管理者のみが設定画面にアクセス可能 |
日常のたとえで言うと:
認証 = 社員証の提示(あなたが誰かを証明)
認可 = 入館できるフロアの制限(何にアクセスできるかを制御)
1.2 なぜ混同されるのか
「Googleでログイン」は一見「Googleに認証してもらっている」ように見えます。しかし内部では:
- OAuth 2.0 でGoogleへのアクセス許可(認可)を取得
- OIDC でユーザーの身元情報(認証)を取得
という2つの処理が行われています。OIDCはOAuth 2.0を内包しているため、見た目上は区別がつきにくいのです。
2. OAuth 2.0 — 「認可」のプロトコル
2.1 OAuth 2.0が解決する問題
OAuth 2.0が登場する前、サードパーティアプリにGoogleやGitHubのデータを渡す方法は「IDとパスワードをアプリに教える」しかありませんでした。
【OAuth以前の問題】
ユーザー → サードパーティアプリ に ID/パスワードを教える
↓
Googleのデータを取得
これでは:
- パスワードを盗まれるリスク
- 全データへのアクセスを許可してしまう
- パスワード変更まで権限を取り消せない
という問題がありました。
OAuth 2.0は「アクセストークン」という限定的な許可証を使うことで、パスワードを渡さずに特定のリソースへのアクセスだけを委任できるようにしました。
2.2 OAuth 2.0の登場人物
(ユーザー)"] Client["📱 クライアント
(サードパーティアプリ)"] AS["🔐 認可サーバー
(Google / Entra ID)"] RS["📁 リソースサーバー
(Google Drive API等)"] RO -->|アクセス許可| AS Client -->|認可リクエスト| AS AS -->|アクセストークン| Client Client -->|アクセストークンでAPIコール| RS RS -->|データ返却| Client
| 役割 | 説明 | 例 |
|---|---|---|
| リソースオーナー | データを持つユーザー本人 | Googleアカウントの所有者 |
| クライアント | アクセスを要求するアプリ | サードパーティの写真編集アプリ |
| 認可サーバー | アクセストークンを発行するサーバー | Google OAuth 2.0サーバー |
| リソースサーバー | 保護されたデータを持つAPIサーバー | Google Drive API |
2.3 OAuth 2.0のフロー(Authorization Code)
scope=drive.readonly AS->>User: ③同意画面表示
「このアプリにドライブの読み取りを許可しますか?」 User->>AS: ④同意 AS->>App: ⑤認可コード返却 App->>AS: ⑥認可コード → アクセストークンに交換 AS->>App: ⑦アクセストークン発行 App->>RS: ⑧アクセストークン付きでAPIコール RS->>App: ⑨データ返却
2.4 OAuthで分かること・分からないこと
ここが重要なポイントです。
| OAuth 2.0 単体 | |
|---|---|
| 分かること | このアクセストークンがドライブへのアクセス権を持っている |
| 分からないこと | このトークンを持っているユーザーは誰か |
OAuth 2.0のアクセストークンは「リソースへのアクセスキー」であり、「ユーザーの身元証明」ではありません。
3. OAuthで「認証」しようとすると危険な理由
3.1 アクセストークンを「ログイン証明」に使う問題
OIDCを使わずにOAuthのアクセストークンやユーザー情報エンドポイントを「ログイン」に使うアプリがあります。これには脆弱性があります。
「Googleでログイン」を悪用 Note over Attacker,TargetApp: 盗んだトークンで
userinfo APIを叩く TargetApp->>Attacker: ⑥被害者としてログイン成功!
OIDCのIDトークンには aud(audience:誰向けのトークンか)クレームがあり、自分のアプリ向けでないIDトークンは拒否できます。アクセストークンにはこの仕組みがありません。
3.2 OAuthを「認証」に使ってはいけない
- アクセストークンは「誰が持っているか」を保証しない
- アクセストークンは別のアプリから横流しされても区別できない
- 認証には必ずOIDCのIDトークンを使うのが正しい実装
4. OpenID Connect(OIDC) — OAuth 2.0に「認証」を追加
4.1 OIDCとは
OIDCはOAuth 2.0の上に認証レイヤーを追加した仕様です(2014年、OpenID Foundation策定)。
OIDC = OAuth 2.0(認可) + 認証レイヤー
具体的には、OAuth 2.0のフローに以下を追加しています:
| 追加要素 | 内容 |
|---|---|
| IDトークン | ユーザーの身元を証明するJWT |
openid スコープ | OIDCフローを開始するスコープ |
| UserInfoエンドポイント | IDトークン以外のユーザー属性を取得するAPI |
| ディスカバリーエンドポイント | IdPの設定情報を自動取得する仕組み |
nonce | リプレイ攻撃を防ぐランダム値 |
4.2 OIDCのフロー(Authorization Code Flow)
(認可サーバー) User->>App: ①ログインボタンをクリック App->>App: ②nonceを生成(セッションに保存) App->>IdP: ③認可リクエスト
scope=openid profile email
nonce=ランダム値 IdP->>User: ④ログイン画面 User->>IdP: ⑤ID/パスワード入力 IdP->>App: ⑥認可コード返却 App->>IdP: ⑦認可コード → トークンに交換 IdP->>App: ⑧IDトークン + アクセストークン
(+ リフレッシュトークン) App->>App: ⑨IDトークンを検証
・署名確認
・aud = 自分のclient_idか確認
・nonce一致確認
・有効期限確認 App->>User: ⑩ログイン完了
openid スコープを追加するだけでOIDCフローになり、IDトークンが発行されます。
4.3 IDトークンの構造
IDトークンはJWT(JSON Web Token)形式で、3つのパートに分かれています。
ヘッダー.ペイロード.署名
(Base64URLエンコード)
ペイロード(デコード後)の例:
| |
| クレーム | 意味 | 用途 |
|---|---|---|
iss | Issuer(発行者) | どのIdPが発行したか確認 |
sub | Subject(ユーザーID) | ユーザーの一意識別子 |
aud | Audience(対象) | 自分のアプリ向けか確認(重要) |
exp | Expiration(有効期限) | トークンが期限切れでないか確認 |
iat | Issued At(発行時刻) | トークンの発行時刻 |
nonce | リプレイ防止値 | リクエスト時のnonceと一致するか確認 |
name, email | ユーザー属性 | UIへの表示など |
5. 3種類のトークンの使い分け
OIDCフローでは最大3種類のトークンが発行されます。それぞれの役割を正しく理解することが重要です。
(JWT)
─────────────
・ユーザーの身元証明
・アプリ内でのみ使用
・有効期限:短め(1時間程度)"] AT["🔑 アクセストークン
(JWT or 不透明)
─────────────
・APIアクセス権
・リソースサーバーに送る
・有効期限:短め(1時間程度)"] RT["🔄 リフレッシュトークン
(不透明)
─────────────
・新しいトークン取得用
・安全な場所に保管
・有効期限:長め(数日〜数週間)"] end ID -->|使用先| U1["アプリ内でのユーザー情報取得
ログインセッション管理"] AT -->|使用先| U2["APIコール時のAuthorizationヘッダー"] RT -->|使用先| U3["アクセストークン期限切れ時の再取得"]
| トークン | 送信先 | 保存場所 | 有効期限 |
|---|---|---|---|
| IDトークン | アプリ内のみ(APIに送らない) | メモリまたはセッション | 短い(〜1時間) |
| アクセストークン | リソースサーバー(API) | メモリまたはセッション | 短い(〜1時間) |
| リフレッシュトークン | 認可サーバーのみ | 安全なストレージ(HTTPOnly Cookie等) | 長い(日〜週単位) |
よくある間違い
- IDトークンをAPIの認証に送る → 誤り(APIにはアクセストークンを使う)
- アクセストークンをユーザーの身元証明に使う → 誤り(認証にはIDトークンを使う)
- リフレッシュトークンをlocalStorageに保存する → 危険(XSS攻撃で盗まれる)
6. OAuthとOIDCの対応関係
6.1 スコープによる動作の違い
OIDCはOAuth 2.0に openid スコープを追加するだけで有効になります。
| リクエストのスコープ | 動作 | 返却されるもの |
|---|---|---|
scope=drive.readonly | OAuth 2.0のみ | アクセストークンのみ |
scope=openid | OIDC(認証のみ) | IDトークン + アクセストークン |
scope=openid profile email | OIDC(属性情報付き) | IDトークン(名前・メール含む) + アクセストークン |
scope=openid profile email drive.readonly | OIDC + OAuth 2.0 | IDトークン + Driveアクセス可能なアクセストークン |
6.2 全体の位置づけ
UserInfoエンドポイント
ディスカバリー
nonce"] end OAuth_core["アクセストークン発行
リフレッシュトークン
認可コードフロー
スコープ管理"] end end
OIDCはOAuth 2.0の機能をすべて内包しています。 OIDCを使えば認証と認可の両方が実現できます。
7. Azure Entra IDでの実装例
7.1 OIDCでMicrosoftアカウントログインを実装する
Entra IDのOIDCエンドポイントへのリクエスト例:
# 認可リクエスト(ブラウザをリダイレクト)
https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/authorize
?client_id=your-client-id
&response_type=code ← Authorization Code Flow
&redirect_uri=https://yourapp.com/callback
&scope=openid profile email ← openidを必ず含める
&state=ランダムな値(CSRF防止)
&nonce=ランダムな値(リプレイ攻撃防止)
7.2 スコープの意味
| スコープ | 取得できる情報 |
|---|---|
openid | OIDCを有効化(subクレーム) |
profile | name, family_name, given_name 等 |
email | email アドレス |
offline_access | リフレッシュトークンの発行 |
User.Read | Microsoft Graph経由でのユーザー情報 |
7.3 MSALを使った実装(JavaScript)
| |
8. まとめ:OAuthとOIDCの違い
| 項目 | OAuth 2.0 | OIDC |
|---|---|---|
| 目的 | 認可(アクセス権の委任) | 認証(ユーザーの身元確認) |
| 発行トークン | アクセストークン | IDトークン + アクセストークン |
| ユーザー情報 | 仕様に含まれない | IDトークン / UserInfoエンドポイント |
| 策定 | IETF(RFC 6749) | OpenID Foundation |
| 策定年 | 2012年 | 2014年 |
| 関係 | 基盤プロトコル | OAuth 2.0の拡張 |
判断基準
- 「ユーザーが誰か」を知りたい → OIDC(IDトークンを使う)
- 「外部サービスのAPIにアクセスしたい」→ OAuth 2.0(アクセストークンを使う)
- 両方必要 → OIDC(OAuth 2.0を内包しているため)
最重要ポイント
- OAuth 2.0だけで「認証」を実装してはいけない
- IDトークンの
audクレームを必ず検証する nonceを使ってリプレイ攻撃を防ぐ- リフレッシュトークンはHTTPOnly Cookieなど安全な場所に保存する