よくある勘違い
ついにOIDCのフローを理解し、実装完了...!
OIDCはOAuth 2.0を認証に使用できるように拡張したもの
つまり、
- OIDCで受け取ったアクセストークンを検証
- 検証OKならアクセストークンでUserInfoエンドポイントからユーザー情報を取得
- ユーザー情報をもとにロールベースの認可
これでOAuth 2.0準拠の完璧なシステムが完成している...! ふつくしい...!!
こんなことになってませんか?(筆者はなってました)
「OIDCはOAuth 2.0を認証に使用できるように拡張したもの」
これは正しいのですが、受け取ったアクセストークンで UserInfo の属性だけを見て機能可否を決めても、それはあなたのアプリの独自の認可であり、OAuth 2.0の認可にはなっていません。
用語を整理しながら、詳細を解説します
用語の整理
OAuth 2.0
- 認可プロトコル
- 超ざっくりいうと、OAuth 2.0 は、リソースオーナーの同意に基づき、認可サーバーがアクセストークンを発行し、リソースサーバーがそれを検証・強制することで、クライアントに保護リソースへの限定的アクセスを委任するための枠組み
フロー概要
https://tex2e.github.io/rfc-translater/html/rfc6749.html
から引用
+--------+ +---------------+
| |--(A)- Authorization Request ->| Resource |
| | | Owner |
| |<-(B)-- Authorization Grant ---| |
| | +---------------+
| |
| | +---------------+
| |--(C)-- Authorization Grant -->| Authorization |
| Client | | Server |
| |<-(D)----- Access Token -------| |
| | +---------------+
| |
| | +---------------+
| |--(E)----- Access Token ------>| Resource |
| | | Server |
| |<-(F)--- Protected Resource ---| |
+--------+ +---------------+
Figure 1: Abstract Protocol Flow
図1に示されている抽象的なOAuth 2.0フローは、4つの役割間の相互作用を説明しており、以下のステップが含まれています。
(A)クライアントはリソース所有者に認可を要求します。許可要求は、リソース所有者に直接(図に示すように)行うことができますが、仲介サーバーとして許可サーバーを介して間接的に行うこともできます。
(B)クライアントは、この仕様で定義されている4つの許可タイプの1つを使用して、または拡張許可タイプを使用して表される、リソース所有者の許可を表す資格情報である許可許可を受け取ります。許可付与タイプは、クライアントが許可を要求するために使用する方法と、許可サーバーがサポートするタイプによって異なります。
(C)クライアントは、許可サーバーで認証し、許可付与を提示することにより、アクセストークンを要求します。
(D)認可サーバーはクライアントを認証し、認可付与を検証し、有効な場合はアクセストークンを発行します。
(E)クライアントは保護されたリソースをリソースサーバーに要求し、アクセストークンを提示して認証します。
(F)リソースサーバーはアクセストークンを検証し、有効な場合はリクエストを処理します
プロトコル概要
- 認証プロトコル
- OAuth2.0をベースに作られている
- 認証成功すると、OpenID Providerが、Relaying Partyに対して、エンドユーザーの属性情報を提供する仕組み
OIDCのプロトコルの概要は以下です
OpenID Connect プロトコルは, おおまかに言って以下のステップに従う.
- RP (Client) が OpenID Provider (OP) にリクエストを送る.
- OP は End-User を認証し, 認可を受ける.
- OP は ID Token および (通常は) Access Token を返す.
- RP は Access Token を添えて UserInfo Endpoint にリクエストを送る.
- UserInfo Endpoint は End-User の Claim を返す.
これら一連のステップを以下に図示する.
+--------+ +--------+ | | | | | |---------(1) AuthN Request-------->| | | | | | | | +--------+ | | | | | | | | | | | End- |<--(2) AuthN & AuthZ-->| | | | | User | | | | RP | | | | OP | | | +--------+ | | | | | | | |<--------(3) AuthN Response--------| | | | | | | |---------(4) UserInfo Request----->| | | | | | | |<--------(5) UserInfo Response-----| | | | | | +--------+ +--------+
https://openid-foundation-japan.github.io/openid-connect-core-1_0.ja.html
から引用
ザックリ解説
Q. 何がOAuth 2.0になってないのか?
A. 大きく分けて2点
- OIDCのUserInfoエンドポイントから得られた属性情報に基づいて認可しているだけで、あなたの欲しい情報そのものの(ユーザー情報以外)、リソース所有者、または認可サーバに許可を要求しているわけでない
- OAuth 2.0におけるリソースサーバーがアクセストークンを検証していない
詳細な解説
結局のところ、OIDCで発行されるアクセストークンでUserInfoから取得した属性情報で認可しても、それはOAuth 2.0ではないということです
また、OIDCで発行されるアクセストークンでは、対象となるリソースサーバーが決まっていることが多く、「自分が取得したい情報があるリソースサーバ」が対象になっているか確認する必要があります
そもそもOIDCがOAuth 2.0の拡張ってどういうことだっけ?
これは、大まかに以下2点に対して言っていると思われます
- トークン取得までのフローが一緒
- OIDCがOAuth 2.0のフローをそのまま採用した形
- OAuth 2.0とは違い、IDトークンも発行するようにした
以下のYoutubeの動画がわかりやすいです
引用: https://www.youtube.com/watch?v=PKPj_MmLq5E&t=1550s
(引用した動画はOAuth & OIDCがスーパー分かりやすい神動画です。詳しくはこの動画を見てください)
勘違いの要因 ―OIDCとOAuth 2.0のアクセストークン―
このような勘違いが起きる要因はOIDCとOAuth 2.0両方で「アクセストークン」という同じ名前のトークンが払い出されることではないでしょうか?
正確に言うと、OIDCで払い出されたアクセストークンが、必ずしもあなたのリソースサーバーに対するアクセストークンではないということです(IdPによってはあなたのリソースサーバーに対するOAuth2.0に準拠したアクセストークンになっていることがあります)。ユーザーの属性情報をユーザー情報が存在しているリソースサーバーから取得するという意味でOAuth 2.0のアクセストークンになっているのです
そして、OAuth 2.0に基づいたアクセストークンで認可するなら、ユーザーの属性情報は取得せずに認可でき、ユーザー情報に依存した認可はそのアプリの独自認可ということです
まとめ
- OIDCはOAuth 2.0の拡張だが、「認証プロトコル」として拡張しただけ
- OIDCで払い出されるアクセストークンはユーザー情報があるリソースサーバへのアクセストークンであり、あなたのシステムがアクセスするリソースサーバのアクセストークンとは限らない(scope等 次第)
- UserInfoで属性を見て認可するのはアプリ固有の認可であって、OAuth 2.0準拠の認可ではない
おわりに
OIDCとOAuth 2.0むずすぎッ!