雰囲気で使わないOAuth
2021-03-11

OAuth is 何

OAuth2.0の標準仕様であるRFC6749を見る。

OAuth 2.0 は, サードパーティーアプリケーションによるHTTPサービスへの限定的なアクセスを可能にする認可フレームワークである. サードパーティーアプリケーションによるアクセス権の取得には, リソースオーナーとHTTPサービスの間で同意のためのインタラクションを伴う場合もあるが, サードパーティーアプリケーション自身が自らの権限においてアクセスを許可する場合もある. 本仕様書はRFC 5849に記載されているOAuth 1.0 プロトコルを廃止し, その代替となるものである.

OAuthを理解するために必要な用語がいくつかある。

  • サードパーティーアプリケーション(クライアント)
  • HTTPサービスへの限定的なアクセス
  • 認可フレームワーク
  • リソースオーナー
  • 認可サーバー
  • リソースサーバー

をイメージしやすいように具体例を交えて理解していく。

前提として、ユーザーの許可によってGoogleDriveにある画像をダウンロードし、加工するアプリを例に出す。

サードパーティアプリケーション

これは画像の編集アプリを指す。開発者にとっては作成中のアプリであることが多い。「サードパーティ」というのは、後述するHTTPサービスから見た場合に「第三者」であるから。

HTTPサービスへの限定的なアクセス

HTTPサービスはGoogleDriveのこと。限定的なアクセスというのは、GoogleDriveにあるデータにどんな操作でもできるというわけではないということ。例えば、このアプリでは、GoogleDriveへのアップロードなどは許可されていないとする。その意味で、限定的なアクセスになるということ。

補足ですが、Driveにある画像は、Driveのものではなく、後述するリソースオーナーの所有であるという考え方をします。なので、サードパーティアプリに対しての許可は基本的にはリソースオーナーの許諾がないと行えないという流れになっている。

認可フレームワーク

OAuthのこと。アクセストークンの発行に関してのルールであり、ここでいうと、そのルールに従ってGoogleのOAuthが行われる。

リソースオーナー

リソースオーナーはユーザーのこと。スマートフォンなどの場合は、その本体がリソースオーナー(の物)であるとして、その本体経由であれば自動的に許諾扱いになるものもある。

認可サーバー

認可サーバーはアクセストークンを発行するためのサーバーのこと。GoogleのAuth が該当します。

リソースサーバー

リソースサーバーはWebAPIに代表されるもの。リクエスト -> 欲しい情報が来る(今回でいうところの画像データ) 認可サーバとリソースサーバーは兼務されていることも多い。

クライアントタイプ

上記ではクライアントはサードパーティアプリケーションと同一視して扱ったが、OAuthとして「クライアントタイプ」という用語があるのでそちらを別途補足する。 OAuthでは、クライアントの認証情報であるクライアントID,クライアントシークレットをセキュアに保存できるかどうかでクライアントタイプを定義している。 セキュアに保存できる場合は「コンフィデンシャルクライアント」と呼ぶ。セキュアに保存できるケースとしては、サーバーサイドで保存できる場合である。それに対して、SPAによるフロントエンドでのみ行う場合や、ネイティブアプリケーションの場合は「パブリッククライアント」と呼ぶ。パブリッククライアントの場合、認証自体はOAuthで行わず、OpenID Connectなどの規格を用いて行うのが一般的。

OAuthにおけるトークンについて

アクセストークン

アクセストークンには、基本的には2つの情報が紐付いている

  • 誰のどんなリソースに対してどんな事ができるのか
  • 期限

OAuthのアクセストークンはBearerトークンなので、クライアントが誰なのか?については関知しない。 アクセストークンを所有している == その内容でできることは許可される という扱いになる。

スコープ

  • 誰のどんなリソースに対してどんな事ができるのか という部分の話。 スコープによって、どんなことができるかが分類される。 アクセストークンにどんなスコープが付けられるかは、クライアントから認可サーバーへのアクセス権リクエストの際に行われる。

ここのスコープは基本的には最小限にするべき。例えば必要ではないのにいきなり位置情報を要求してくるアプリなどがあるが、そういった場合はリソースオーナーは不審がって許可しないだろう。

有効期限

期限の話。 アクセストークンには期限があり、期限を過ぎたトークンでのリクエストは拒否される。 アクセストークンの有効期限は、後述するリフレッシュトークンの有効期限に比べて短くするのが通常。基本的にはexpire_inという項目に記載がある。

リフレッシュトークン

リフレッシュトークンは、クライアントから認可サーバーにアクセストークンの再発行を要求するために使われます。 リフレッシュトークンは必須要件ではないが、大体の一般的に普及しているOAuthにはあると思う。

認可コード

認可コードは、リソースオーナーが同意した証として発行される。この証を用いて、アクセストークンを要求する。認可コードが利用できるのは一度だけ。また、流出の際の危険が大きいので、基本的には非常に短い時間で設定される。

エンドポイントについて

OAuthのフローとして、最も代表的になグラントタイプである認可コードグラントでは、以下のエンドポイントが存在する。Googleログインとかをイメージすると理解しやすいかも。

認可エンドポイント

認可サーバーによって提供されるエンドポイント。役割は認可コードの発行。 クライアントが初回にアクセス権をリクエストした際には、この認可エンドポイントに認可コードの発行をリクエストします。 リソースオーナーはユーザー名とパスワードなどで認証を行い、特定の操作権限の移譲についての許諾を求められます。 同意により、認可コードが発行される。

トークンエンドポイント

トークンエンドポイントは、認可サーバーによって提供されるエンドポイントのこと。認可コードを受け取ったクライアントは、認可コードと必要な情報を持ってトークンエンドポイントにリクエストを投げます。それによって、アクセストークンが発行されます。必要な情報というのはクライアントIDとクライアントシークレットなど。

リダイレクトエンドポイント

リダイレクトエンドポイントはクライアントが用意するエンドポイント。リダイレクトエンドポイントは、認可サーバーから認可コード(他のグラントタイプではトークンの受け渡し。)を受け取るために行われる。認可サーバーは認可コード送信の同意が行われた段階で、302を返してリダイレクトエンドポイントにブラウザをリダイレクトさせます。その際に、クエリパラメータとして認可コードの値が渡されます。

###クライアントの登録について

トークンエンドポイントの話で少し出てきたが、クライアントの開発者はリソースを提供する組織(例で行くとGoogle)に対してクライアント情報を登録し、クライアントID,クライアントシークレットの発行を受ける必要がある。GoogleだとGoogle Developer Consoleでアプリを登録する。リダイレクトURIもここで登録したりする。

グラントタイプについて

グラントは「付与」という意味。標準仕様では4つのグラントタイプが定義されている。それぞれに特徴があるのでケースによって使い分けると良い。ここでは認可コードグラントにフォーカスする。

認可コードグラント

一番大事だと思われる。

認可コードグラントはコンフィデンシャルクライアントに最適化されたグラント。現状ではPKCE(Proof Key for Code Exchange by OAuth Public Clients)を使用することで、パブリッククライアントでも推奨されるグラントタイプである。 リソースオーナー、クライアント、認可サーバの三者が登場し、4つのグラントタイプの中で最も複雑である。 3-legged OAuthという呼び方となる。

シーケンス

1.認可コードの取得 クライアントアプリで、Googleから画像を取得を行う。「googleから画像を読み込む」みたいなボタンを押すイメージ。

2.クライアントはリダイレクトを行い、リソースオーナーを認可エンドポイントに誘導する。この時、認可エンドポイントにはリクエストに必要な情報がリクエストパラメーターに付与された状態。

具体的にはこんな風なリクエストが飛んでいる

GET リクエスト
/authorize
  ?response_type=code
  &client_id=hogehoge
  &state=fuga
  &scope=write
  &redirect_url=https%3A%2F%2Fcallback

response_type

値として、[code]が設定されている。認可サーバはここを見て、認可コードの発行を求められていることを判定しています。

client_id

クライアントの事前登録時に発行されたIDを入れています。ここで「どのクライアントか」を判断します。

state

クライアントが生成したランダムな値。stateをユーザーセッションと紐付けて管理することで、CSRFを防ぐ。

scope

クライアントの要求するスコープ。例ではwriteとして書き込み権限を要求している。

redirect_uri

クライアントの事前登録時に設定したリダイレクトURLが設定される。

  1. 認可サーバーはリソースオーナーに本人認証を求める。email passwordでgoogleにログインする。(ここの認証方法はemail & passwordである必要はない)既にログインしている場合は、このフェーズはスキップされることもある。
  2. ログインした後、認可サーバーはリクエストにあるscopeのリクエスト内容を評しする。「〇〇がxxに対して△△することを許可しますか?」のような画面が出る。ここで、リソースオーナーは同意か、拒否ができる。同意した場合は、認可サーバーから認可コードが発行される。
  3. 認証した場合、認可サーバーがステータスコード302のレスポンスを返す。このレスポンスを、「認可レスポンス」と呼称する。このときのリダイレクトURLは2のリダイレクトURLになる。付与されるパラメータとして、codeとして認可コードの値、stateとして生成されたランダム値が付与される。 stateは最初のリクエストの際にランダム生成され、認可サーバーに渡されます。その後、認可サーバーから再度同じ値がクライアントに返される。クライアントはこの時、「最初のランダム生成の値」と「返ってきた値」が同一かどうかを確認する。これによって、CSRFを防ぐことができる。
  4. 認可コードを受け取った後、認可コードを付与してクライアントからトークンエンドポイントに対してリクエストを投げる。これを「トークンリクエスト」と呼ぶ。トークンリクエストでクライアントの認証が行われる。
POST /token

Authorization: Basic hogehoge
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=hogefuga
&redirect_url=https%3A%2F%2Fcallback

ヘッダーにベーシック認証を行っているが、bodyにクライアントIDとシークレットを含めて渡してもいい。

grant_type

認可コードグラントなので、authorization_codeとなる。

code

認可コードが入ってる

redirect_url

リダイレクトURL

client_id, client_secret

事前登録の際に発行された値。この値で確認を行い、認可コードに紐付いたコードに対応したアクセストークンを生成する。

7.クライアントID,Secretの検証が問題なければアクセストークン、有効期限などが返ってくる。このレスポンスのことを「トークンレスポンス」と呼ぶ。 8.AuthorizationヘッダーにBearerという文字列とともにアクセストークンの値を設定し、リソースサーバーに対してリクエストを投げる。リソースサーバーは、リクエストが適切かどうかをチェックし、問題なければリソースがレスポンスとして返ってくる。

インプリシットグラント

2019年に非推奨になったみたい。認可コードグラント + PKCEの組み合わせが推奨されている。

クライアントサイドがバックエンドと分離したSPAなどでは、アクセストークンがブラウザで見えてしまうので、情報漏えいのリスクが高くなる。

認可コードグラント+ PKCEの組み合わせによる認可コード横取り攻撃についてはまた次回。