OAuth の代表的フロー Authorization Code Flow と PKCE についてまとめてみる
「OAuth と OpenID Connect について分かりやすくまとめてみる」で紹介した通り、OAuth のフローを経ると最終的にアクセストークンが発行されます。
前回はごく簡単な流れだけを紹介しましたが、アクセストークンを発行するまでのフローには、いくつか種類が存在します。例えば以下などです。
- Client Credentials Flow
- Resource Owner Password Flow
- Implicit Flow with Form Post
- Authorization Code Flow
今回は、中でも特に重要な Authorization Code Flow について説明したいと思います。
Authorization Code Flow はセキュリティの観点で優れており、一般的な Web アプリケーション、SPA、ネイティブアプリなど多くのユースケースでその利用が推奨されています。
またフローの内容的にも、これを押さえておけば他のフローは比較的容易に理解できると思うので、まずは Authorization Code Flow について理解されることをお勧めします。
前提知識
具体的な説明に入る前に、いくつか前提となる知識を押さえておこうと思います。
OAuth が目指すこと
まずは OAuth が目指していることについて整理しておこうと思います。
Authorization Code Flow は比較的複雑ですが、OAuth が何を目指しているかを把握しておけば、そのフローの理解もしやすいかと思います。
OAuth が目指していることは以下です。
- リソースオーナーが持つ保護リソースへのアクセス権限を、リソースオーナーからの許可を得た上で、クライアントに委譲したい
- リソースオーナーのパスワードをクライアントに教えたくない
- アクセストークンをクライアント以外に教えたくない (できればリソースオーナーやユーザーエージェントにも教えたくない)
先ほど OAuth でアクセストークンを発行するまでのフローには、いくつか種類があると説明しましたが、Authorization Code Flow は基本的にこれらをすべて満たしています。
逆に他のフローはより簡易的なフローである一方、これらの一部を満たすことができておらず、その観点からも Authorization Code Flow が推奨されているのです。
登場人物
次に登場人物を整理しておきます。
- リソースオーナー
- ユーザーのこと
- ユーザーエージェント
- リソースオーナーが利用するブラウザやアプリケーションのこと
- クライアント
- 連携元のアプリケーションのこと
- リソースオーナーからリソースへのアクセス権限を委譲してもらうことになります
- サービスプロバイダー
- 連携先のアプリケーションのこと
- 大きく認可サーバーとリソースサーバーに分けられます
- 認可サーバー
- リソースオーナーとクライアントを仲介し、保護リソースへのアクセス許可に必要な、アクセストークンを発行する場所
- リソースサーバー
- 保護リソース (最終的にアクセスしたい、リソースオーナーの許可なしではアクセスできないリソース) がある場所
以上の前提知識を踏まえた上で、Authorization Code Flow について説明していきたいと思います。
一般的な Web アプリケーションであるか、SPA やネイティブアプリであるかによって、若干その流れが異なるので、分けて説明していきます。
一般的な Web アプリケーションの場合の Authorization Code Flow
一般的な Web アプリケーションの場合は、下図のようなフローでアクセストークンが発行されます。
具体的には次のような流れです (正確には OAuth が定めているのはこの中の 4~14 の部分です)。
- ユーザー → ユーザーエージェントに、保護リソースへのアクセスを希望
- ユーザーエージェント → クライアントに、保護リソースへのアクセスをリクエスト
- クライアント → ユーザーエージェントに、認可サーバーへのリダイレクトを指示
- ユーザーエージェント → 認可サーバーに、認可リクエスト
- 認可サーバー → ユーザーエージェントに、認可画面の表示を指示
- ユーザーエージェント → ユーザーに、認可画面を表示
- ユーザー → ユーザーエージェントに、保護リソースへのアクセスを許可
- ユーザーエージェント → クライアントに、ユーザーの認可を受けて POST リクエスト
- 認可サーバーで、認可コードを発行
- 認可サーバー → ユーザーエージェントに、認可コードをフラグメントに含めつつの /callback エンドポイントへのリダイレクトを指示
- ユーザーエージェント → クライアントに、認可コードを通知
- クライアント → 認可サーバーに、認可コードを含めつつアクセストークンをリクエスト
- 認可サーバーで、アクセストークンを発行
- 認可サーバー → クライアントに、アクセストークンを返却
- クライアント → リソースサーバーに、アクセストークンを含めつつ保護リソースをリクエスト
- リソースサーバーで、アクセストークンを検証
- リソースサーバー → クライアントに、保護リソースを返却
- クライアント → ユーザーエージェントに、保護リソースを返却
- ユーザーエージェント → ユーザーに、リソースを表示
SPA やネイティブアプリの場合の Authorization Code Flow (PKCE を用いるパターン)
基本的な流れは、一般的な Web アプリケーションの場合と同じです。
ただし、SPA やネイティブアプリでは PCKE (ピクシー: Proof Key for Code Exchange) を用いた Authorization Code Flow が推奨されています。そのため、まずは PCKE について説明します。
認可コード横取り攻撃を防ぐ PKCE
PKCE は「認可コード横取り攻撃」を防ぐための仕組みです。
先の図で登場した認可コードですが、ネイティブアプリの場合これが悪意あるユーザーに盗まれてしまう可能性があり、これは認可コード横取り攻撃と呼ばれています (詳しくは「OAuth & OIDC 入門編 by #authlete (動画なので音が出ます。認可コード横取り攻撃の説明は 1:15:48 あたりからです)」をご覧ください)。
そして認可コードが盗まれてしまった場合、その認可コードを認可サーバーに送ることで、悪意あるユーザーはアクセストークンを入手できてしまいます。
PKCE の登場人物
これを防ぐための、PKCE の基本的なアイデアは「認可コードのリクエスト元とアクセストークンのリクエスト元が同一であることを検証」することです。
登場人物は以下の 3 つです。
- code verifier
- ランダムな文字列
- code challenge method
- 以下のどちらか。特殊な事情がなければ基本的に S256 を用いる
- plain: code verifier の値そのものを code challenge として返す処理
- S256: code verifier の SHA-256 ハッシュ値を計算し、Base64URL エンコードして返す処理
- 以下のどちらか。特殊な事情がなければ基本的に S256 を用いる
- code challenge
- code verifier と code challenge method から導かれる値
PKCE の仕組み
これら 3 つの登場人物を用いて、次のようにすることで「認可コードのリクエスト元とアクセストークンのリクエスト元が同一であることを検証」します。
- 認可コードのリクエスト時
- クライアント
- code verifier を生成しつつ、code challenge method から code challenge を算出する
- 認可リクエストに code challenge method と、算出した code challenge を載せる
- 認可サーバー
- 認可コード発行時、code challenge method と code challenge を、認可コードに紐づける形で保存しておく
- クライアント
- アクセストークンのリクエスト時
- クライアント
- アクセストークンのリクエストに code verifier と認可コードを載せる
- 認可サーバー
- 受け取った code verifier と、認可コードを元に取得した保存済みの code challenge method から code challenge を算出し、保存済みの code challenge と比較する
- 両者が一致すれば、先のリクエストと同じクライアントであると考える
- クライアント
PKCE を用いた Authorization Code Flow
以上を踏まえ、PKCE を用いた場合の Authorization Code Flow は下図のようになります。
基本的には先の図と同じですが、黄色い四角で囲んだ 4, 9, 12, 13 が異なります。
それぞれ、次のようになっています。
4 . code challenge と code challenge method を含めつつ、認可リクエスト (GET /oauth/authorize)
9 . 認可コードを発行しつつ、認可コードに紐付ける形で code challenge と code challenge method を保存
12 . code verifier と認可コードを含めつつ、アクセストークンをリクエスト (POST /oauth/token)
13 . code verifier と code challenge method から code challenge を算出、保存していた code_challenge と比較して code verifier を検証、検証後アクセストークンを発行
まとめ
今回は OAuth の代表的フローである「Authorization Code Flow」と SPA やネイティブアプリで Authorization Code Flow を用いる場合に必要になってくる「PKCE」についてまとめてみました。
Authorization Code Flow は OAuth のフローの中でも特に重要なフローです。
この記事が少しでも理解の助けになれば幸いです。
内容に間違いがあれば、コメントでご指摘いただけると嬉しいです。
いいね、フォローもぜひお願いします 🐳🐣🐙
Discussion