OpenID Connect

読み:おーぷんあいでぃーこねくと / 英語表記:OpenID Connect

OAuth 2.0の上に、利用者の認証の結果をアプリケーションへ安全に伝える仕組みを加えた標準仕様である。ログインの結果は、発行者が署名したIDトークンとして受け渡される。

イマシルちゃんの解説

ほかのサービスのアカウントを使って、別のアプリにログインできることがあるんだ。
その裏でよく使われているのが、OpenID Connectという標準規格だよ。
権限を任せるためのOAuth 2.0を土台にして、「誰がログインしたか」を伝える部分を足したものなの。
利用者がアカウントを管理する事業者の画面でログインすると、アプリはその結果を表すIDトークンというデータを受け取るんだ。
そこには利用者の識別子や、どのアプリ向けにいつ発行されたかが書かれているよ。
アプリは署名や宛先を確かめてから、その人としてログインさせるの。
アプリごとにパスワードを預けなくても、本人確認を信頼できる相手に任せられるのが便利なんだ。

より深い解説

定義と位置付け

OpenID Connectは、OpenID Foundationが定めた仕様群である。中心となるOpenID Connect Core 1.0は、OAuth 2.0の手順をそのまま使いながら、ログインした利用者の情報を伝える方法を加える。OAuth 2.0は権限の委譲のための枠組みであり、「誰がログインしたか」をアプリケーションへ伝える形式は定めていない。その不足を埋めるのがこの仕様である。

利用者の本人確認を担当し、その結果を発行する側をOpenIDプロバイダ(OP)と呼ぶ。OAuth 2.0の認可サーバに、利用者を認証して結果を発行する機能を加えたものに当たる。結果を受け取ってログインに使うアプリケーションをリライングパーティ(RP)と呼ぶ。こちらはOAuth 2.0のクライアントに当たる。

OpenID Connectの呼び方OAuth 2.0での役割例
エンドユーザーリソースオーナーログインする利用者
OP認可サーバアカウントを管理する事業者のログイン基盤
RPクライアント外部のアカウントでのログインを受け付けるWebアプリケーション

利用者のパスワードを受け取って照合するのはOPだけである。RPはパスワードを扱わず、OPが発行した結果を検証して利用者を受け入れる。複数のRPが同じOPを信頼すれば、シングルサインオンを実現できる。

OAuthとの役割分担

OAuth 2.0とOpenID Connectは、同じ通信の流れの中で別の問いに答える。

観点OAuth 2.0OpenID Connect
答える問いこのアプリに何を許したか誰が、いつ、どの方法でログインしたか
中心となるトークンアクセストークンIDトークン
トークンの宛先APIを提供するリソースサーバログインを受け付けるRP自身
トークンの形式規定なし規定された署名付きの形式

アクセストークンはAPIに見せるためのもので、RPが中身を読んで利用者を判断することは想定されていない。逆に、IDトークンをAPIへの権限の証明として送るのも用途違いである。二つのトークンを取り違えないことが、安全な実装の前提になる。

OAuth 2.0だけでログインを作ると、別のアプリケーション向けに発行されたアクセストークンを持ち込まれる危険がある。IDトークンには「どのRP向けに発行したか」が書かれているため、RPは自分宛てでないものを拒否できる。

ログインの流れ

Webアプリケーションで基本となる認可コードフローを例にする。全体はOAuth 2.0の認可コードグラントと同じで、違いは要求に付ける値と、トークンエンドポイントから受け取るものにある。

  1. 利用者がRPで「外部アカウントでログイン」を選ぶ。
  2. RPはブラウザをOPの認可エンドポイントへリダイレクトする。要求のスコープには必ずopenidを含め、推測できない値を nonce パラメータに付ける。
  3. OPは利用者にログインさせ、必要なら情報の提供について同意を得る。
  4. OPはブラウザをRPのリダイレクトURIへ戻し、認可コードを渡す。
  5. RPのサーバは、認可コードをOPのトークンエンドポイントへ送る。
  6. OPはアクセストークンに加えて、IDトークンを返す。
  7. RPはIDトークンを検証し、問題がなければ利用者のログインセッションを作る。
  8. 必要に応じて、アクセストークンを使ってOPから利用者の属性を取得する。

手順2の要求は次のようになる。値はすべて説明用である。

GET /authorize?response_type=code
  &scope=openid%20profile%20email
  &client_id=photo-print
  &redirect_uri=https://print.example/callback
  &state=af0ifj...
  &nonce=n-0S6_W...
Host: id.example.jp

スコープにこの値がなければ、OpenID Connectの要求として扱われない。prompt=login を付けると、OPに利用者の再ログインを求められる。max_age を付けると、最後のログインからの経過時間が指定を超えた場合に再ログインさせられる。このときIDトークンには、ログインした時刻を示す auth_time が入る。

署名付きトークンの構造

IDトークンはJWT(JSON Web Token)の形式で表す。この形式は、ヘッダー・ペイロード・署名の三つをbase64urlで符号化し、ピリオドでつないだ文字列である。ヘッダーには署名アルゴリズムと鍵の識別子を、ペイロードには利用者や発行に関する情報(クレーム)を入れる。符号化は暗号化ではないため、署名だけの場合は誰でも中身を読める。

ペイロードの例を示す。値は説明用である。

{
  "iss": "https://id.example.jp",
  "sub": "u-7f3a91c2",
  "aud": "photo-print",
  "exp": 1767226200,
  "iat": 1767225600,
  "nonce": "n-0S6_W...",
  "auth_time": 1767225590
}

主なクレームの意味は次のとおりである。最初の五つはどのフローでも必須である。

クレーム意味検証での使い方
iss発行者(OP)の識別子信頼しているOPと完全に一致するか
subOPの中で利用者を識別する値RPの利用者と対応付ける
audこのトークンを受け取るRP自分のクライアントIDを含むか
exp有効期限現在時刻が期限前か
iat発行時刻古すぎる発行を拒否する目安にする
nonce認可要求に付けた値自分が送った値と一致するか
auth_time利用者がログインした時刻再ログインを求めた条件を満たすか

時刻は1970年1月1日0時(UTC)からの秒数で表す。この例の iat は2026年1月1日9時(日本時間)に当たり、exp との差は次のとおりである。

有効期間 = 1,767,226,200 − 1,767,225,600 = 600秒 = 10分

利用者を識別する値は、そのOPの中で一意で、ほかの利用者に再割り当てされない。長さは最大255文字のASCII文字と定められている。一意性はOPごとに保証されるので、RPは発行者の識別子との組で利用者を識別する。メールアドレスは変更や再利用があり得るため、利用者を識別する鍵には向かない。

受け取った側の検証手順

RPは、IDトークンを受け取っただけでログインさせてはならない。OpenID Connect Core 1.0の3.1.3.7節は、認可コードフローでの検証手順を定めている。主な確認は次の順に行う。

  1. iss が、事前に知っているOPの識別子と完全に一致するか確かめる。
  2. aud に自分のクライアントIDが含まれているか確かめる。信頼していないほかの宛先が含まれていれば拒否する。
  3. 署名を、OPが公開している鍵で検証する。
  4. 現在時刻が exp より前であることを確かめる。
  5. 認可要求で nonce を送ったなら、トークン内の値が送った値と一致するか確かめる。

トークンエンドポイントからTLSで直接受け取ったIDトークンでは、TLSによるサーバの検証を署名の検証の代わりにできると定められている。ブラウザ経由で受け取る場合は、必ず署名を検証する。IDトークンにはJWSによる署名が必須であり、署名なし(alg が none)が許されるのは限られた条件だけである。

iat の許容範囲はRPごとに決める。サーバの時計がずれていると、正しいトークンを期限切れと判断したり、発行時刻が未来だと判断したりすることがある。RPのサーバもNTPなどで時刻を合わせておく。

使い回しを防ぐ値

nonceは、RPのログインセッションとIDトークンを結び付け、リプレイ攻撃を防ぐための値である。RPは推測できない乱数を作って認可要求に付け、OPはその値をIDトークンにそのまま入れる。RPは、戻ってきたIDトークンの値が自分の送った値と一致するかを確かめる。

一致しないIDトークンは、別のログインのために発行されたものである。攻撃者が以前に入手したIDトークンを差し込んでも、今回のログインで送った値と一致しないため拒否できる。値には推測されないだけの十分な乱雑さが必要とされている。

実装の例として、乱数をHttpOnly属性付きのCookieに保存し、そのハッシュ値を認可要求に付ける方法が示されている。戻ってきたIDトークンの値と、Cookieから計算したハッシュ値を比べればよい。

この値は、IDトークンを認可エンドポイントから直接受け取るフローでは必須、認可コードフローでは任意である。state はOAuth 2.0のパラメータで、認可応答が自分の要求に対するものかを確かめる。目的は近いが、この値はIDトークンの中に入って署名で保護される点が異なる。

利用者の属性を取得する

IDトークンに入っていない氏名やメールアドレスなどは、OPのUserInfoエンドポイントから取得できる。このエンドポイントはOAuth 2.0で保護されたAPIであり、RPはトークンエンドポイントで受け取ったアクセストークンをAuthorizationヘッダーに Bearer 方式で付けて要求する。応答はJSONで返る。

取得できる情報の範囲は、認可要求のスコープで指定する。

スコープ主に得られる情報
profile氏名、ニックネーム、プロフィールの基本情報
emailメールアドレスと、それが確認済みかどうか
address住所
phone電話番号と、それが確認済みかどうか

UserInfoの応答に含まれる sub は、IDトークンの sub と完全一致することを確かめなければならない。一致しない場合は、その応答を使ってはならない。別の利用者向けのトークンにすり替えられ、他人の情報を受け取る攻撃を防ぐためである。

フローの種類

OpenID Connectには三つのフローがあり、認可要求の response_type で選ぶ。

フローresponse_typeの値IDトークンの受け取り方
認可コードフローcodeトークンエンドポイントから受け取る
インプリシットフローid_token、id_token token認可エンドポイントからブラウザ経由で受け取る
ハイブリッドフローcode id_token など両方のエンドポイントから受け取る

ブラウザ経由でトークンを受け取る方式は、URLからの漏えいや差し替えの危険が大きい。RFC 9700は、認可応答でアクセストークンを受け取る方式を避け、認可コードを使うよう求めている。新しく作るWebアプリケーションやスマートフォンのアプリでは、認可コードフローにPKCEを組み合わせるのが基本になる。

設定情報と署名鍵の公開

RPがOPを使うには、エンドポイントのURLや署名の検証に使う公開鍵を知る必要がある。OpenID Connect Discovery 1.0では、OPの識別子に /.well-known/openid-configuration を付けたURLで、これらの設定情報をJSONとして公開する。

項目名内容
issuerOPの識別子。IDトークンの iss と一致する
authorization_endpoint認可エンドポイントのURL
token_endpointトークンエンドポイントのURL
userinfo_endpointUserInfoエンドポイントのURL
jwks_uri署名の検証に使う公開鍵の集合(JWK Set)を置くURL

OPは署名鍵を定期的に交換することがある。JWTのヘッダーにある鍵の識別子(kid)を見て、公開鍵の集合から対応する鍵を選ぶ。RPが古い鍵を保存したまま更新しないと、鍵の交換後に検証が失敗する。

SAMLとの比較

企業のシングルサインオンでは、SAMLも広く使われる。どちらもログイン結果を発行者の署名付きで別のサービスへ伝える点は共通である。

観点OpenID ConnectSAML
結果を表す形式JWT(JSON)XMLのアサーション
土台OAuth 2.0独自のプロトコルとバインディング
APIの権限アクセストークンで同時に得られる主な目的に含まれない
多い用途Webアプリケーション、スマートフォンのアプリ企業向けのWebアプリケーション

JSONとHTTPのAPIで扱いやすいため、スマートフォンのアプリやAPIと組み合わせる場面ではOpenID Connectが選ばれやすい。

設計と運用

RPを設計するときは、OPを信頼する範囲と、検証をどこで行うかを決める。

最後の点は誤解しやすい。RPでログアウトしても、OPにログインしたままなら、次回は利用者の操作なしにログインできることがある。逆にOPでログアウトしても、RPのセッションが自動的に終わるとは限らない。両者を連動させるには、ログアウトのための別の仕様を使う。

障害の切り分け

ログインの失敗は、OPとの通信の段階か、IDトークンの検証の段階かで分けて調べる。

現象考えられる原因
issの不一致で拒否される設定したOPの識別子と表記が異なる(末尾のスラッシュの有無など)
audの不一致で拒否される別のクライアントIDで登録した設定を使っている
署名の検証だけが急に失敗するOPの鍵の交換に、保存した公開鍵が追従していない
期限切れや発行時刻の異常で拒否されるRPのサーバの時刻がずれている
nonceの不一致で拒否されるログイン開始時の値を保存したCookieやセッションが失われている
属性の取得に失敗するアクセストークンの期限切れ、必要なスコープを要求していない

署名や時刻の問題は、OPの画面では正常にログインできているのに、RPに戻った後だけ失敗する形で現れる。どの段階まで進んだかをRPのログで確かめると、原因を絞り込める。

午後での問われ方

参考資料

この用語が出てくる記事

↑