Proof Key for Code Exchange (RFC 7636)

1. イントロダクション

RFC 7636Proof Key for Code Exchange (PKCE, 「ピクシー」と発音します) は、 認可コード横取り攻撃(authorization code interception attack) への対策に関する仕様です。

authorization code interception attack

2015 年 9 月に出版された同仕様は、認可コードフローの各種リクエストに関し、いくつかの追加パラメーターを定義しています

  1. 認可リクエスト: code_challenge パラメーターと code_challenge_method パラメーターを追加
  2. トークンリクエスト: code_verifier パラメーターを追加

この仕様により認可サーバーは、悪意のある(正当なコードベリファイアを持たない)クライアントアプリからのトークンリクエストを拒否できるようになります。

2. PKCE 認可リクエスト

2.1 リクエストパラメーター

PKCE に対応したクライアントアプリは、認可リクエストに code_challenge パラメーターを追加し、その値としてコードチャレンジを含めます。

code_challenge_method パラメーターはオプションです。ただし特別な理由がない限りは、コードチャレンジメソッドとして後述する S256 の利用が推奨されています。そのため結果的には、S256 を値として指定するために、code_challenge_method パラメーターも追加することになります。

2.2 コードチャレンジ

クライアントアプリは、コードベリファイアの値にコードチャレンジメソッドの計算ロジックを適用して、コードチャレンジの値を導出します。

2.3 コードベリファイア

クライアントアプリが生成しなくてはならないコードベリファイアの値は、[A-Z] / [a-z] / [0-9] / "-" / "." / "_" / "~" からなるランダムな文字列であり、最低43文字、最大128文字の長さが必要となります。

pkce authorization request

2.4 コードチャレンジメソッド

コードチャレンジメソッドの値としては plain および S256 が定義されています。それぞれの計算ロジックは下記の通りです。

Method Logic
plain code_challenge = code_verifier
S256 code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))

plain では、インプットは何ら変換されません。コードベリファイアの値が、そのままコードチャレンジの値となります。

S256 では、SHA-256 のハッシュ値を BASE64-URL エンコードした値を用います。例えば、code_verifier の値が dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk である場合、code_challenge の値は E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM となります。

S256 を用いる場合、クライアントアプリは認可リクエストの中に code_challenge_method=S256 を含める必要があります。 code_challenge_method パラメーターがない場合、認可サーバーは plain が使われていると判断し、処理します。

3. PKCE 認可レスポンス

認可サーバーは認可コードを生成した後、その値と併せて、認可リクエストに含まれている code_challenge の値と code_challenge_method の値を、データベース上に保存します。

認可サーバーはこれらの値を、その後クライアントアプリから送られてくるトークンリクエストの検証に用います。

認可エンドポイントから返されるレスポンス自体は、これまでと同様であり、PKCE 特有のパラメーター等はありません。

pkce authorization response

4. PKCE トークンリクエスト

認可サーバーから認可コードを受け取った後、クライアントアプリはトークンリクエストを生成します。トークンリクエストには、認可コードに加え、コードチャレンジの値の元となったコードベリファイアの値を、 code_verifier パラメーターの値として含める必要があります。

pkce_token_request

5. PKCE トークンレスポンス

5.1 コードベリファイアの有無を確認する

PKCE をサポートする認可サーバーは、トークンリクエストに正当なコードベリファイアが含まれているのかを確認します。

この確認は、 grant_type が authorization_code であり、トークンリクエスト中の認可コードがコードチャレンジと紐づいている場合に限ります。

これらの条件がそろっていても、トークンリクエスト中にコードベリファイアが含まれていない場合、そのリクエストは悪意のあるクライアントアプリからのリクエストと認識し、認可サーバーはエラーを返します。

5.2 コードベリファイアを検証する

認可サーバーは 2 つのコードチャレンジを照合し、トークンリクエストの正当性を検証します。

コードチャレンジの片方は、認可リクエストに含まれていた(認可サーバーがデータベースに保存しておいた)値です。もう片方は、認可リクエストで指定された方法(トークンチャレンジメソッド)を用いて、トークンリクエスト中のコードベリファイアから計算される値です。

もしこれら 2 つのコードチャレンジが同一の場合、そのトークンリクエストを送信してきたクライアントアプリは、先の認可リクエストの送信元だったクライアントアプリと同一であると、認可サーバーは判断します。一致しない場合、認可サーバーは、そのトークンリクエストが悪意のあるクライアントアプリから来たものと判定します。

5.3 トークンを発行する

トークンリクエストが正規の場合、認可サーバーは通常通りトークンを発行します。

pkce_token_response

6. PKCE を試す

6.1 準備する

6.1.1 サインアップする

Authlete のアカウントをお持ちでない場合は、まず初めにサインアップしてください。

6.1.2 サービス API キーとクライアント ID

認可リクエストを実行するためには、サービス API キーと クライアント ID が必要となります。これらはサインアップと同時に発行されています(動作確認用の認可サーバーとクライアントアプリがひとつずつ自動的に生成されています)。

実際の値については、アカウント登録時に送信されるメールの記載や、管理者コンソールおよびクライアントコンソールからご確認ください。

6.1.3 サービスとクライアントの設定

認可サーバー(管理者コンソールサービス)及びクライアントアプリ (クライアントコンソールアプリ) の設定は下記を参考にしてください。

Table. Settings of Service in Service Owner Console

カテゴリ パラメーター
認可 サポートする認可種別 少なくとも AUTHORIZATION_CODE にチェックを入れる
認可 サポートする応答種別 少なくとも CODE にチェック入れる
認可 ダイレクト認可エンドポイントの有効化 有効
認可 ダイレクトトークンエンドポイントの有効化 有効

Table. Settings of Client in Client Developer Console

カテゴリ パラメーター
基本情報 クライアントタイプ PUBLIC を選択する
認可 認可種別 少なくとも AUTHORIZATION_CODE にチェックを入れる
認可 応答種別 少なくとも CODE にチェック入れる
認可 リダイレクト URI https://api.authlete.com/api/mock/redirection/service-api-key

6.2. 認可リクエスト・レスポンス

6.2.1. 認可エンドポイントにアクセスする

下記の URL にブラウザからアクセスしてください。その際、service-api-keyclient-id は自身のものに置き換えてください。

https://api.authlete.com/api/auth/authorization/direct/service-api-key
  ?client_id=client-id
  &response_type=code
  &code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
  &code_challenge_method=S256

上記にある code_challengecode_challenge_method が、これまで説明してきた PKCE 関連のパラメーターです。コードチャレンジメソッドとして S256 を指定し、コードチャレンジとして、後述するコードベリファイアから計算した値 E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw を含めています。

6.2.2 認可リクエスト

認可エンドポイントにおいて、認可ページが表示されます。その中にあるログインフォームに、ここでは動作確認として、ログイン ID としてサービスの API キー、パスワードとして同じく API シークレットを入力し、認可ボタンをクリックしてください。なお、API シークレットの値の確認方法についてはクイックスタートをご参照ください。

login form in authorization page

6.2.3 認可レスポンス

認可エンドポイントから返されるレスポンスは、ブラウザを介して、クライアントアプリのリダイレクトエンドポイントに送られます。送信先(リダイレクト URI)は、サインアップ時に自動生成された動作確認用のクライアントアプリの情報として、すでに登録されています。実際の送信先は https://api.authlete.com/api/mock/redirection/service-api-key になります。

このリダイレクトエンドポイントは、動作確認用に Authlete が用意した実装です。この実装では、認可レスポンスとして受け取った各種パラメーターの値を表示します。たとえば、認可コードを含む認可レスポンスを受け取った場合、リダイレクトエンドポイントはその値を下記のように表示します。

authorization_code

加えて、認可エンドポイントから ID トークンが発行されていた(認可レスポンスに ID トークンを含んでいた)場合、ID トークンの内容を表示します。

また、認可コードフローのような、トークンリクエストを行うフローの場合、トークンリクエストを送信するためのフォームが表示されます。

6.3 トークンリクエスト

6.3.1 トークンリクエストフォーム

直前のセクションでも述べましたが、認可レスポンス中に認可コードが含まれている場合、トークンリクエストを送信するためのフォームが表示されます。

そのフォームには、コードベリファイアを入力する欄があります。ここでは、dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk と入力して下さい。

token_request_form

コードベリファイア付きのトークンリクエストを送信するために、「送信」ボタンをクリックしてください。なお、認可コードは 10 分で失効するように設定されているため、10 分以内にボタンをクリックしてください。

6.3.2. トークンレスポンス

認可コードと入力したコードベリファイアが正しい場合、JSON 形式でアクセストークンが返却されます。

{
    "access_token": "KPLXrl_wJSHqU708R9kp3bNRGi0LgKUdh0kh-CQhx9g",
    "refresh_token": "YLRJXfratV4yq0_65seCT0bF6YxxgU5jKBUvhOZPrb4",
    "scope": null,
    "token_type": "Bearer",
    "expires_in": 86400
}

おめでとうございます!これで、PKCE に対応した認可コードフローでアクセストークンの発行が完了しました。

6.4. PKCE Configuration

Authlete では、クライアントに対し、PKCE の利用、およびコードチャレンジメソッド S256 の指定を強制するよう設定可能です。詳細は以下の記事をご参照ください。