TL

OpenID Connect とフェデレーション認証の信頼モデル

ソーシャルログインの裏側がわかる。OAuth の上に OIDC が ID トークンをどう載せ、JWKS で鍵を配り、IdP と RP の信頼境界をどう引くのかを原理から理解できる。

応用OpenID ConnectOAuthJWTフェデレーション認証シングルサインオン最終更新: 2026-06-21
TL;DR要点だけ先に
  • 1.OAuth は「認可(アクセス権の委譲)」のための枠組みで、それだけでは「認証(誰がログインしたか)」を安全には表せない。OIDC は OAuth の上に ID トークン(署名付き JWT)を1枚足して認証を標準化した薄い層である。
  • 2.ID トークンの真正性は IdP の秘密鍵による署名で担保し、RP はディスカバリ(.well-known)経由で取得した JWKS の公開鍵で検証する。鍵は kid で選び、ローテーションに追従する。
  • 3.信頼境界の本質は「RP は IdP の署名と iss/aud/exp/nonce を必ず検証する」こと。aud や nonce の検証を省くと、別アプリ向けトークンの使い回しやリプレイで認証がすり抜ける。

OIDC が解く問題:OAuth は「認可」であって「認証」ではない

「Google でログイン」のようなソーシャルログインの裏で動くのが OpenID Connect(OIDC)です。ここでまず押さえるべきは、土台である OAuth 2.0 が**認可(authorization)**の枠組みであって、認証(authentication)の枠組みではない、という点です。OAuth が発行するアクセストークンは「このアプリは当該ユーザーの何々の API を呼んでよい」という権限の委譲証であり、「いま誰がログインしたか」を表すものではありません。両者の違いは認証と認可の違いの通りで、ここを混同すると設計を誤ります。

OAuth だけでログインを実装しようとすると、有名な落とし穴に落ちます。アクセストークンはリソースサーバーに提示するための文字列にすぎず、どのアプリ宛てに発行されたかが受け手に伝わりません。攻撃者が別アプリ向けに正規取得したアクセストークンを使い回し、/userinfo 的なエンドポイントに投げて得たユーザー ID で「ログイン成功」と判定してしまうと、なりすましが成立します(いわゆる confused deputy 問題)。OIDC はこれを、宛先(aud)を含み IdP が署名した ID トークンを1枚足すことで根本から塞ぎます。

OIDC は OAuth の「薄い認証層」

OIDC は OAuth を置き換えるものではなく、その上に乗る薄い層です。具体的には (1) openid という固定スコープ、(2) ID トークンという署名付き JWT、(3) 鍵やエンドポイントを自動発見するディスカバリ/JWKS、(4) UserInfo エンドポイントの4点を標準化しただけです。OAuth の認可コードフローはそのまま使い、応答に ID トークンが1枚増える、と捉えると全体像がつかめます。

主要な登場人物と用語の対応

OIDC では OAuth のロール名がそのまま使われますが、認証文脈の別名も併記されます。最初にこの対応を固定しておくと混乱しません。

OIDC の呼称OAuth での呼称役割
OP / IdP(OpenID Provider)認可サーバー本人確認し、ID トークンとアクセストークンを発行する。鍵を保有する
RP(Relying Party)クライアントIdP を信頼してログインを受け入れるアプリ。トークンを検証する側
エンドユーザーリソースオーナーログインする本人
ID トークン(OAuth に該当なし)「誰が・いつ・どの RP に対して」認証されたかを表す署名付き JWT
アクセストークンアクセストークンAPI(UserInfo 含む)を呼ぶための権限委譲証。RP が中身を解釈する義務はない

ここで決定的に重要なのは、ID トークンの受け手は RP、アクセストークンの受け手はリソースサーバーだという宛先の違いです。RP が検証すべきは ID トークンであって、アクセストークンを「復号して中を見る」ものではありません(アクセストークンは IdP にとって不透明な文字列でよい)。この役割分担が、後述する aud 検証の根拠になります。

ID トークンの中身:署名付き JWT としての構造

ID トークンの実体は JWS(JSON Web Signature)形式の JWT です。ヘッダ.ペイロード.署名 の3つを base64url で連結した文字列で、ヘッダとペイロードは暗号化されておらず誰でもデコードして読めます。秘密は守られず、守られるのは**真正性(誰が発行し、改竄されていないか)**だけです。これはデジタル署名が機密性ではなく完全性と認証を担うのと同じ性質です。

# ID トークン(JWT)の3部構成
ヘッダ   : {"alg":"RS256","kid":"a1b2","typ":"JWT"}   # 署名アルゴリズムと鍵ID
ペイロード: {                                          # クレーム(主張)の集合
  "iss": "https://idp.example.com",   # 発行者。誰が署名したか
  "sub": "248289761001",              # ユーザーの一意かつ不変な識別子
  "aud": "rp-client-id-123",          # 宛先。このトークンを使ってよい RP
  "exp": 1718960000,                  # 有効期限(UNIX 秒)
  "iat": 1718956400,                  # 発行時刻
  "nonce": "n-0S6_WzA2Mj",            # リプレイ対策。認可要求の値と一致必須
  "auth_time": 1718956390             # 実際に本人確認した時刻
}
署名     : RS256( base64url(ヘッダ) + "." + base64url(ペイロード), IdPの秘密鍵 )

各クレームには明確な役割があります。iss は「どの IdP が署名したか」、sub は「その IdP の中で一意かつ不変なユーザー ID」です。ここで注意すべきは、sub はあくまで iss の名前空間内でのみ一意だという点で、アプリのユーザーキーは常に (iss, sub) の組で持つのが原則です。email を主キーに使うのは、IdP 側でメールが変更・再割り当てされうるため危険です。aud がこのトークンを使ってよい RP の識別子、exp が失効時刻、nonce がリプレイ対策の使い捨て値です。

`alg: none` と鍵取り違えは典型的な検証バグ

JWT 検証の二大事故が、(1) ヘッダの algnone のトークンを「署名なし」として受け入れてしまうダウングレード、(2) RS256(非対称)を期待すべき所で HS256(対称)を受け入れ、公開鍵を HMAC 鍵として誤用させられる鍵取り違えです。後者は、攻撃者が公開された IdP 公開鍵を共通鍵として HMAC 署名し、検証側がアルゴリズムを固定していないと通ってしまいます。RP は期待する alg を明示的に固定し、ライブラリ任せにしないのが鉄則です。

鍵配布の自動化:ディスカバリと JWKS

ID トークンの署名を RP が検証するには、IdP の公開鍵が要ります。これを手作業で配ると鍵ローテーションのたびに破綻するため、OIDC は2段の自動発見を標準化しています。

第一段がディスカバリです。RP は https://<issuer>/.well-known/openid-configuration を GET し、そこに書かれた各エンドポイント(認可・トークン・UserInfo)と、署名鍵の置き場所である jwks_uri を得ます。第二段が **JWKS(JSON Web Key Set)**で、jwks_uri を GET すると現在有効な公開鍵の集合が返ります。

# 1) ディスカバリ:エンドポイントと鍵の場所を発見
GET https://idp.example.com/.well-known/openid-configuration
{
  "issuer": "https://idp.example.com",
  "authorization_endpoint": "https://idp.example.com/authorize",
  "token_endpoint": "https://idp.example.com/token",
  "userinfo_endpoint": "https://idp.example.com/userinfo",
  "jwks_uri": "https://idp.example.com/keys",          # ← 公開鍵の置き場
  "id_token_signing_alg_values_supported": ["RS256"]
}

# 2) JWKS:現在有効な公開鍵の集合(複数並ぶ)
GET https://idp.example.com/keys
{ "keys": [
  { "kty":"RSA", "kid":"a1b2", "use":"sig", "n":"...", "e":"AQAB" },
  { "kty":"RSA", "kid":"c3d4", "use":"sig", "n":"...", "e":"AQAB" }   # 新旧併存
]}

検証の実際は単純です。RP は受け取った ID トークンのヘッダの kid を読み、JWKS の中から同じ kid を持つ公開鍵を選び、その鍵で署名を検証します。鍵が複数並ぶのはローテーションを無停止で行うためで、IdP は「新鍵を JWKS に追加 → しばらく新旧併存 → 旧鍵で署名したトークンが全て失効してから旧鍵を削除」という手順を踏みます。RP 側は JWKS を短時間キャッシュしつつ、未知の kid が来たら再取得する実装が定石です。これはX.509 のチェーン検証が信頼の起点をルート CA に置くのと違い、信頼の起点が「issuer から HTTPS で取得した JWKS」に置かれている点が対照的です。

信頼の連鎖は TLS から始まる

JWKS の真正性は、jwks_uri への HTTPS 接続そのもの(サーバー証明書の検証)に依存します。つまり OIDC の信頼の根は、最終的に Web PKI に接地しています。issuer 値の検証を省いたり、HTTP で JWKS を取りに行ったりすると、鍵のすり替えで署名検証ごと無効化されるため、ここは妥協できません。基盤の考え方は公開鍵基盤(PKI)を参照。

RP がやるべき検証:信頼境界はここに引かれる

OIDC のセキュリティは、ほぼ全面的に RP 側の ID トークン検証に懸かっています。IdP がどれだけ厳密でも、RP が検証を省けば信頼境界は崩れます。最低限、次の検証はすべて必須です。

検証項目何を確認するか省くと起きること
署名JWKS の公開鍵で署名が正しく、alg が期待通りか改竄・偽造トークンを受理してしまう
iss想定した IdP の発行者文字列と完全一致するか別 IdP(攻撃者の IdP)のトークンを信じる
aud自分(RP)のクライアント ID が含まれるか別アプリ宛てトークンの使い回しを許す
exp / iat現在時刻が有効期限内で、発行が未来でないか失効済みトークンの再利用を許す
nonce自分が認可要求で送った値と一致するか過去トークンのリプレイを許す

とりわけ audnonce が、OAuth 単体では塞げなかった穴を埋める要です。aud 検証は「このトークンはこの RP のために発行された」ことを保証し、冒頭の confused deputy(別アプリ向けトークンの流用)を阻止します。nonce は認可要求時に RP が生成した使い捨て乱数で、IdP がそれを ID トークンにそのまま埋め戻すため、**RP は「自分が今回の要求で送った nonce と一致するか」**を確認することで、盗んだ古いトークンの再送(リプレイ)を弾けます。これは多要素認証が別要素で本人性を補強するのとは別軸の、トークンの鮮度と宛先を担保する仕組みです。

アクセストークンでログイン判定をしてはいけない

最も重大なアンチパターンが、ID トークンを使わず「アクセストークンで UserInfo を引けたらログイン成功」とする実装です。アクセストークンには宛先(aud)の概念が RP に見える形で含まれず、別 RP 向けのトークンでも UserInfo は引けてしまうため、なりすましが成立します。ログインの判定は必ず署名検証済みの ID トークンの sub で行う——これが OIDC を導入する最大の理由そのものです。

UserInfo エンドポイントと、ID トークンとの使い分け

ID トークンには認証の事実(誰が・いつ)を載せ、メールや氏名などのプロフィール属性UserInfo エンドポイントから別途取得するのが OIDC の流儀です。RP は受け取ったアクセストークンを Authorization: Bearer で UserInfo に提示し、IdP がそのトークンに紐づくユーザーの属性を JSON で返します。

GET https://idp.example.com/userinfo
Authorization: Bearer <access_token>
->
{ "sub":"248289761001", "name":"Taro Yamada",
  "email":"taro@example.com", "email_verified": true }

両者を分ける理由は、ID トークンを軽量に保ち、要求スコープに応じて属性開示を可変にするためです。ただし UserInfo 応答の sub は、ID トークンの sub と必ず一致することを確認しなければなりません。一致確認を怠ると、別ユーザーのアクセストークンで引いたプロフィールを取り違える危険があります。属性の取得経路(ID トークン直載せか UserInfo か)は IdP 実装で異なるため、RP は両対応にしておくのが安全です。

フェデレーション設計:信頼を一点に集約する構図

最後に、なぜこの構造が「フェデレーション認証」と呼ばれるかを設計の視点で整理します。フェデレーションとは、複数の RP が共通の IdP に本人確認を委譲し、認証情報(ID トークン)を相互運用可能な形で受け渡す構図を指します。各 RP は自前で資格情報(パスワード)を保持せず、本人確認の責務を IdP に一点集約します。

この集約には明確な利益と代償があります。利益は、パスワード保管箇所が IdP の1点に減り、各 RP の攻撃面が縮小すること、そして利用者が一度のログインで複数 RP に入れるシングルサインオンが成立することです。代償は、IdP が単一障害点かつ単一の侵害点になることです。IdP の署名鍵が漏れれば、その IdP を信頼する全 RP のログインが偽造可能になります。だからこそ署名鍵のローテーション(JWKS)と HSM 等での鍵保護が、フェデレーション全体の安全性を左右します。

試験・面接で問われる急所

(1) OAuth は認可、OIDC は認証——この一文を即答できること。(2) ID トークンは「署名付き JWT」で、機密ではなく真正性を守る。(3) 鍵はディスカバリ(.well-known)→ JWKS → kid 選択で取得・選択する。(4) RP 必須検証は「署名・iss・aud・exp・nonce」。(5) ログイン判定はアクセストークンではなく ID トークンの sub で行う。この5点が押さえられていれば原理は理解できています。

まとめると、OIDC は OAuth の認可基盤の上に「署名付き ID トークン+鍵の自動配布+RP 側の厳密検証」を薄く重ねることで、認証を標準化したプロトコルです。信頼境界は「RP が IdP の署名と iss/aud/exp/nonce を検証する」一点に集約され、そこを正しく実装することがフェデレーション全体の安全性を決めます。関連して、パスワード自体を排す方向のFIDO2 / WebAuthnと組み合わせると、IdP 側の本人確認をさらに堅牢にできます。

セキュリティ Article

OpenID Connect とフェデレーション認証の信頼モデルを実務で読む

TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。

解決すること

OpenID Connect

比較で見る軸

難易度: advanced / カテゴリ: セキュリティ / タグ数: 6

導入後に効く点

ID トークンの真正性は IdP の秘密鍵による署名で担保し、RP はディスカバリ(.well-known)経由で取得した JWKS の公開鍵で検証する。鍵は kid で選び、ローテーションに追従する。

先に潰すリスク

用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。

数字・仕様の読み方
難易度
advanced
カテゴリ
セキュリティ
タグ数
6

判断チェックリスト

  • 自社の用途が「OpenID Connect / OAuth」に近いか確認する。
  • 強みである「OAuth は「認可(アクセス権の委譲)」のための枠組みで、それだけでは「認証(誰がログインしたか)」を安全には表せない。OIDC は OAuth の上に ID トークン(署名付き JWT)を1枚足して認証を標準化した薄い層である。」が本当に評価軸になるか確認する。
  • 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
  • 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
  • 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
  • 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。

次に確認する観点

OpenID ConnectOAuthJWTフェデレーション認証OpenID ConnectOAuthJWT
参考: 公式情報