OAuth 2.0 (Authorization Code Flow / Implicit Flow)とOpenID Connectについて調べた
概要
OAuth 2.0のAuthorization Code FlowとImplicit Flowについて自分なりに整理した。あと、OpenID Connect(主にImplicit Client)も調べた。
いつもなんとなく調べて、ざっくり理解するも、時間が経つと忘れるので、アウトプットしておく。とはいえ、仕様や用語とかの理解が甘くて自信はない。ベースはオライリーの本で勉強して、OAuth 2.0の仕様もちょこちょこみてた。
用語
用語の理解があいまいな気がするけど、この記事内では統一しておきたい…
リソースサーバ
GoogleやFacebookなどが提供しているAPIサーバ
認可サーバ(Authorization Server)
GoogleやFacebookなどが提供している認可用のサーバ
リソースオーナー(Resource Owner)
GoogleやFacebookにリソースおいてるユーザ。API認可を必要とするアプリのユーザ。
クライアント(Client)
OAuthクライアント。Authorization Code Flowならサーバで動くアプリ、Implicit Flowならブラウザで動くアプリという認識。
ユーザエージェント (User-Agent)
多分Authorization Code FlowとImplicit Flowならブラウザってことになるのかな。
OAuthについて3行
OAuth 2.0 Authorization Code Flow
Authorization Code Flowだけではないが、OAuthエンドポイントとAPI間はHTTPS通信を行う。
以下でフローのステップを説明している。この説明は、リソースオーナーがAPIプロバイダにサインインしていることを前提としてる。
1. リソースオーナーに実行内容を知らせて、認可を要求
- OAuth認可ページを表示(またはウインドウを開く)
- リソースオーナーが認可
- (ユーザエージェントが)OAuth認可エンドポイント(認可サーバ)にリクエスト
リクエストには以下のようなクエリパラメータが必要
- client_id: クライアントID
- redirect_uri: 認可後、リダイレクトされるクライアントのURL
- scope: アクセス要求するデータの種類
- response_type: レスポンスの種類。codeを指定。
- state: ローカルステート。CSRF対策用の一意の値。
2. 認可コードをアクセストークンに交換
- 認可サーバがユーザエージェントをredirect_uriにリダイレクト
- クライアントが認可サーバにアクセストークン取得をPOSTリクエスト
- レスポンスでアクセストークンが返ってくる
認可サーバによってリダイレクトされるURLに、以下のクエリパラメータが付与されてる。
- code: 認可コード
- state: 認可リクエストのときに送ったローカルステート
クライアントから認可サーバへのPOSTリクエストには以下のパラメータが必要。
- client_id: クライアントID
- client_secret: クライアントシークレット
- code: 認可コード
- redirect_uri:
- grant_type: グラントタイプ。authorization_codeを指定。
- アクセストークンに期限がある場合は、expires_inやrefresh_tokenも必要
レスポンスではaccess_tokenに加えて、token_type、expires_in、refresh_tokenなども返ってくる。
3. API呼び出し
- クライアントがリソースサーバのAPIにリクエスト
- レスポンスでクライアントがリソースを取得する
リクエスト時に、HTTPのAuthorizationヘッダでアクセストークンを送付する。
OAuth 2.0 Implicit Flow
こっちはメインのウインドウで画面遷移をせずに認可を得られるのが特徴だと思った。
以下でフローのステップを説明している。
1. リソースオーナーに実行内容を知らせて、認可を要求
- OAuth認可用ウインドウを開く(iframeでもいいかも)
- リソースオーナーが認可
- (ユーザエージェントが)OAuth認可エンドポイント(認可サーバ)にリクエスト
認可がクライアントで簡潔するので、子ウインドウやiframeを使うのが特徴。
リクエスト時のクエリパラメータは以下である。Authorization Code Flowとはresponse_typeの指定が異なる。
- client_id: クライアントID
- redirect_uri: 認可後、リダイレクトされるクライアントのURL
- scope: アクセス要求するデータの種類
- response_type: レスポンスの種類。tokenを指定。
- state: ローカルステート。CSRF対策用の一意の値。
2. URLからアクセストークンを解析
- 認可サーバがユーザエージェント(認可用ウインドウ)をredirect_uriにリダイレクト
- クライアントがハッシュフラグメントのアクセストークンを解析
Implicit Flowでは認可サーバによるリダイレクトのURLに、ハッシュフラグメントとしてaccess_token、token_type、expires_in、stateなどが付加されている。
URLから解析したアクセストークンは親ウインドウに渡される。親ウインドウへの送付には、グローバル関数やwindow.postMessageを使う。親ウインドウでCSRF対策を行うべき。
3. API呼び出し
- クライアントがリソースサーバのAPIにリクエスト
- レスポンスでクライアントがリソースを取得する
リクエスト時に、HTTPのAuthorizationヘッダでアクセストークンを送付する。Authorization Code Flowと同じである。
また、Authorization Code Flowと違ってリフレッシュトークンがないので、アクセストークンを更新することはできない。アクセストークンを取得するには毎回上記のステップふむ必要がある。
OAuth 2.0を利用した認証
OAuthは認可の技術だけど、認証でも使われている。簡単に言うと、プロフィールのAPIあったとして、そのAPIの認可を得たら認証もできる、といった感じだと思う。
一見大丈夫そうだけど、リスクがあるとのこと。それは、OAuth 2.0 Implicit Flowを使った場合。詳しく説明してある記事があったので、そちらを参照するといい。僕もその記事で勉強しました。
OAuth 2.0 Implicit Flowをユーザー認証に利用する際のリスクと対策方法について #idcon
Implicit Flowだとアクセストークン置き換え攻撃ができるというリスクがある。例えば、誰かのアクセストークンを使えば、その誰かとして認証できるということ。URLのハッシュとしてアクセストークンが送られてきて、それを使って単純にJSでリクエストしてるから、まあそうだよねといった感じ。
このリスクを回避するために、OpenID Connect使うといいよとも上記の記事に書いてある。
OpenID Connectについて3行
- OAuth 2.0を利用した認証技術
- IDトークンというアクセストークンとは別のトークンがある
- 実装方法として、Basic ClientとImplicit Client、その2つをあわせたHybrid Flowがある
OpenID Connect Implicit Client
OAuth 2.0 Implicit Flowを利用した認証。Implicit Flowを利用しているだけあって、それと似たようなステップになってる。
1. ユーザ認可の取得
- 認証用ウインドウを開く(iframeでもいいかも)
- (ユーザエージェントが)OAuth認可エンドポイント(認可サーバ)に認証リクエスト
- 認可サーバーがエンドユーザを認証
- エンドユーザが承認/認可
認証リクエストはImplicit Flowの認可リクエストと似たようなもの。以下のようなクエリパラメータが必要。scopeとresponse_typeの指定とnonceがある点で認可リクエストとは異なる。
- client_id: クライアントID
- redirect_uri: 認可後、リダイレクトされるクライアントのURL
- scope: アクセス要求するデータの種類。openidを含む必要がある。
- response_type: レスポンスの種類。id_tokenおよびtokenを空白で区切りで指定。
- state: ローカルステート。CSRF対策用の一意の値。
- nonce: クライアントのセッションとIDトークンを紐付ける文字列。
エンドユーザとはOAuth 2.0の説明で出てきたリソースオーナーと同じ。エンドユーザの認証は、OAuth 2.0の説明ではサインイン済みとしてスキップした。実際には、認可リクエストでも認証は必要である。
2. URLからIDトークンとアクセストークンを解析
- 認可サーバがユーザエージェント(認証用ウインドウ)をredirect_uriにリダイレクト
- クライアントがハッシュフラグメントのアクセストークンを解析
こちらもOAuth 2.0 Implicit Flow同様に、リクエストの結果がURLのハッシュ部分に返される。以下のような内容になる。
- access_token
- token_type
- id_token
- state
- expires_in
3. IDトークンの検証
- 解析したIDトークンをCheck ID Endpointに送って検証
- 検証をパスしたら認証とする
Check ID EndpointとはIDトークンの検証サーバーみたいなもの。検証にCheck ID Endpoint使う決まりはないみたい。このIDトークンの検証があるので、アクセストークン置き換えによるなりすましリスクを防げるようだ。
検証後、IDトークンやアクセストークンは親ウインドウに渡しておく必要がある。
さいごに
OAuth 2.0のAuthorization Code Flow、Implicit Flowは共にシンプルなプロトコルということが実感できた。OpenID ConnectはOAuth 2.0をベースとしてるだけあって、OAuth 2.0をざっくり理解しておけばなんとかなりそう。
勉強してるうちに仕様書を読めるようになってきたので、まとめるのが面倒になった。かっちり書いたら仕様書に近づくだけなので、ゆるく書いたつもり。致命的な間違いがなければいいな…