1. はじめに
前回の記事「OAuth 2.0とは何か?「誰に何を許可するか」という発想」では、OAuth 2.0が「認証」ではなく「認可」の仕組みであることを整理しました。「Googleでログイン」のようなボタンでは、実際には認可の枠組みだけでは足りない部分があります。それを補うのが、今回扱うOpenID Connect(OIDC)です。
2. この記事はこんな方におすすめ
- OAuth 2.0とOpenID Connectの違いを説明できない方
- 「IDトークン」と「アクセストークン」の役割の違いが気になる方
- 「OAuthでログインする」という言い回しに、もやっとしたことがある方
3. 内容
OAuth 2.0だけでは、ログインの何が足りないのか
前回整理した通り、OAuth 2.0が発行するのはアクセストークンです。これは「クライアントが、リソースサーバー上の何にアクセスしてよいか」を表す許可証です。
OAuth 2.0の標準仕様(RFC 6749)は、アクセストークンの中身の形式や、そこに含めるべき情報を定めていません。つまり、クライアントがアクセストークンを取得できたとしても、それだけでは「利用者の認証結果を、標準化された方法でクライアントへ伝え、検証する」という部分が満たされません。「誰が、いつ認証されたか」をクライアントが確認したい場合、OAuth 2.0単体では共通のやり方が用意されていない、というのが正確なところです。
OpenID Connectは、OAuth 2.0の上に立つ「認証の層」
OpenID Connectは、2014年にOpenID Foundationが策定した仕様で、OAuth 2.0の認可の仕組みの上に、標準化された認証の仕組みを追加します。OAuth 2.0が「認可のフレームワーク」であるのに対し、OIDCは「OAuth 2.0の上に構築されたシンプルなアイデンティティ層」と位置づけられています。
具体的には、OIDCは次の2つを新たに導入します。
- IDトークン:認証が行われたことと、その利用者に関する情報を表す、署名付きのJWTです
-
openidスコープ:OIDCによる認証を要求することを示すスコープです。前回に続き認可コードフローで見ると、クライアントが認可リクエストにこのスコープを含めて要求すると、認可コードの交換時にアクセストークンとあわせてIDトークンも受け取れるようになります
OAuth 2.0の認可コードグラントの流れをそのまま使いながら、認証結果を伝えるIDトークンと、それをクライアントが検証するためのルールを追加したものと考えると分かりやすいでしょう。
IDトークンの中身
IDトークンは、JWTという形式で発行される、署名付きのデータです。中には、認証に関する情報が「クレーム」として含まれています。代表的なクレームは次の通りです。
-
iss(issuer):このIDトークンを発行した認可サーバーを示すURLです -
sub(subject):発行元の中で利用者を識別する、別の利用者へ再割り当てされない識別子です。クライアントは、このsubを発行元を表すissと組み合わせて利用者を識別します。異なる発行元同士では、同じsubの値が使われる可能性があるためです -
aud(audience):このIDトークンが、どのクライアント(client_id)向けに発行されたかを示します -
exp/iat:IDトークンの有効期限と、発行時刻です。iatはあくまでトークンの発行時刻であり、利用者が認証された時刻ではありません。利用者が認証された時刻を表すのはauth_timeという別のクレームで、max_ageが要求された場合などを除き、常に含まれるとは限りません -
nonce:認証リクエストとIDトークンを対応付け、リプレイ攻撃を軽減するための値です。認可コードフローでは仕様上は送信が任意ですが、リクエストで送信した場合、クライアントは返ってきたIDトークンに同じ値が含まれることを必ず検証する必要があります
クライアントは、信頼するプロバイダーの鍵を使った署名検証に加え、発行元(iss)・受取先(aud)・有効期限(exp)などを検証し、受け入れてよいIDトークンかを確認します。さらに、nonceを送信した場合は、今回の認証リクエストに対応する値と一致することを確認します。
アクセストークンとIDトークンは役割が違う
ここで、これまでのシリーズとも関わる重要な注意点があります。アクセストークンとIDトークンは、使う相手と目的が異なります。
-
アクセストークン:クライアントが、リソースサーバーへのアクセスに使うものです。OIDCでは、認可サーバー自身が提供するUserInfoエンドポイントという、利用者のプロフィール情報(
profileスコープなら氏名、emailスコープならメールアドレスなど、許可された範囲で)を返すリソースサーバーへのアクセスにも使われます - IDトークン:クライアント自身が受け取り、検証し、認証が行われたこと自体の証明として解釈するためのものです
つまり、「アクセストークンで利用者情報を取得すること」自体は、UserInfoエンドポイントを通じて正規に行われる、ごく普通の使い方です。ただし1点、見落とされがちな注意点があります。UserInfoエンドポイントから返ってきたプロフィール情報のsubは、先ほど検証したIDトークンのsubと完全に一致することを確認しなければなりません。OIDC Coreの該当箇所(§5.3.2 Successful UserInfo Response)では、一致しない場合、そのUserInfoレスポンスの情報を利用してはならないとされています。この確認をせずにUserInfoの内容をそのまま信頼してしまうと、別の利用者の情報を取り違えて使ってしまうリスクがあります。
逆方向の取り違えにも注意が必要です。IDトークンを、APIへのアクセスに使うアクセストークンの代用品として扱ってはいけません。アクセストークンはあくまで「何かにアクセスしてよい」という許可証であり、それを取得できたからといって、標準化された方法で本人確認が行われたことにはなりません。「誰が認証されたか」を確認したいときはIDトークンを、「何かにアクセスする」ときはアクセストークンを、という役割分担を守ることが、OIDCを正しく使う上での前提になります。
4. まとめ
OpenID Connectは、OAuth 2.0の認可の枠組みに、標準化された認証の仕組みを追加する仕様です。クライアントはIDトークンの署名や発行元、受取先、有効期限などを検証し、プロバイダーが伝える認証結果を確認します。
プロフィールを受け取るだけでなく、誰がどのアプリに向けて発行した認証結果なのかまで確かめるところに、OAuth 2.0へ認証の層を加える意味があるんだなと感じました。