読者です 読者をやめる 読者になる 読者になる

理系学生日記

おまえはいつまで学生気分なのか

忍者TOOLS

OAuth 2.0の認可権限無効化の仕様

OAuth 2.0 のプロバイダを作るんだったら、もしかして認可権限の取消画面も作らないといけないのかしら〜〜〜〜(ほげ〜〜〜〜)と思って調べてたら、その revoke の仕様も RFC で仕様化されていることを知りました。 これにより、必ずしも取消画面を作らなくて良いということにはなりませんが、API として公開することは容易そうなので心をなでおろしています。

OAuth 2.0 (RFC 6749) では認可サーバ側での認可権限の無効化について触れられていますが、RFC 7009 で触れられているのは、クライアントからの権限の無効化です。 Twitter における「アプリ連携」メニューでは、個々のアプリケーションに対して「許可を取り消す」というボタンが存在していますが、あれを仕様化したものと理解すれば良い。

トークンの無効化という文脈には以下の 2 つがあります。

  • リフレッシュトークンの無効化
  • アクセストークンの無効化

このうち、絶対に実装せよ(MUST) とされているのはリフレッシュトークンの無効化です。アクセストークンと比較して、リフレッシュトークンの有効期限は一般に長い (あるいは無期限) ので、これは当然といえば当然ですね。アクセストークン無効化したって、リフレッシュトークンが有効なままなら、新しいアクセストークンの再取得が可能ですし。

一方でアクセストークンの無効化は、アクセストークンを JWT で実装するといったケースでは非常に困難なものになります。このようなケースでは、アクセストークンの有効期限を短くして対応することになるでしょう。

Token Revocation

トークンの無効化に際して、認可サーバは「Token Revocation EndPoint」を実装する必要があります。これは一種の無効化 API のエンドポイントと考えてもらったら良さそうです。 クライアントは、認可サーバに以下のようなリクエストを送ります。

POST /revoke HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW

token=45ghiukldjahdnhzdauz&token_type_hint=refresh_token

Token Revocation Endpoint の仕様の概要としては、

  1. HTTPS で提供する (TLS のバージョンはもちろん新しいやつをつかいましょう)
  2. POST メソッドを受け付ける。パラメータは後述。
  3. クライアント認証は実施。(RFC 6749 参照)

POST メソッドのパラメータは以下のようなものになります。

パラメータ名 REQUIRED/OPTIONAL 概要
token REQUIRED 無効化したいトークン
token_type_hint OPTIOAL ※後述

このうち、token_type_hint は、リフレッシュトークンを無効化するのか、アクセストークンを無効化するのかのヒントを示しています。仕様上は access_tokenrefresh_token が定義されていることもあり、基本的にはこの 2 値を使うことになるでしょう。 認可サーバ側はリクエストを受け取り、トークンを無効化したら、HTTP レスポンス 200 を返却します。この後、クライアント側は無効化したトークンを使ってはいけません(使ってもエラーになりますが)。

おもしろいのは、無効なトークンに対する無効化要求に対しても、認可サーバは HTTP ステータス 200 を返却する点です。 たしかに、このケースでクライアントに 4XX や 5XX を返却しても、クライアントは何ができることはありませんね。

リフレッシュトークンの無効化は、紐づくアクセストークンの無効化を要求するか

悩ましいのは、リフレッシュトークンを無効化したとき、そのリフレッシュトークンから作成されたアクセストークンを無効化しないといけないかどうかですが、RFC 的には「アクセストークンの無効化をサポートしているのであれば、リフレッシュトークンの無効化でアクセストークンも無効化すべきでしょ (SHOULD)」という立場です。 JWT とかでアクセストークンを構成する場合はアクセストークンの無効化自体が困難なケースが多いので、そういう場合は、アクセストークンは「Expire するのに任せる」というのが良いかなと思います。

Chromeのautofillによりテキストフィールドが謎の黄色になってしまう

Chrome で input フィールドに履歴から入力した後、テキストボックスの色が悲しい黄色になる。 この挙動に困った人もいると思います。

この挙動については、かなしいですが非バグとされていて、データが autofill されたことをユーザが分かるようにっていう WebKit からの設計思想だってことになっているようです。

We inherit this coloring behavior from WebKit and I believe it’s by design. It allows the user to understand the data has been prefilled.

そういうわけで、何人もの人がこの挙動に困っていて、どうもこの修正のスタンダードな方法は -webkit-box-shaddow で背景色を上書きするっていう方法のようです。

/* Change the white to any color ;) */
input:-webkit-autofill {
    -webkit-box-shadow: 0 0 0px 1000px white inset;
}

inset で、テキストボックスの内側に「影」として色付けをするっていう方法。たしかにうまくいきますね。

その他参考

PoEAA: Class Table Inheritance

Class Table Inheritance は、Rool-Leaf Mapping とも呼ばれるもので、階層構造を持つオブジェクトモデルと DB 上のエンティティとの関係について、オブジェクトモデルのクラス毎にテーブルを持たせるというデザインパターンです。

オブジェクトモデルとエンティティという対応においては分かりやすいパターンです。 ドメインモデル上で表現されるデータがテーブル上で散らばってしまっているので、その抽出が複雑になります。

長所

  1. 各テーブル上のカラムはそのテーブルに必要な情報を保持する(不要な情報を保持しない)ので、スペースに無駄がない
  2. ドメインモデルとデータベースとの関係が分かりやすい

短所

  1. DB からオブジェクトを作成する際、複数のテーブルを参照する必要があり、その参照は複雑になる
  2. 継承ツリー上でフィールドを親クラスに移動させるような設計変更が、テーブル構造に影響を与える
  3. 継承ツリーの上位にあたるテーブルは頻繁にアクセスされるため、ボトルネックになりやすい

特定ユーザのGithub上のリポジトリをごそっとローカルにcloneする

Github 上にある特定 user のリポジトリをぜんぶローカルに持ってきたいなぁというときのワンライナー。 nablarch のところは適当なユーザに書き換えれば OK。ghq ではなく単に git を使いたければ、ghq getgit clone に変えれば良いです。

$ curl 'https://api.github.com/users/nablarch/repos?per_page=100' | jq -r '.[]["clone_url"]' | xargs -n1 -P4 ghq get

Github API

Github には API があって、参照だけなら HTTP リクエストを送るだけで情報を取得できるようになってます。特定ユーザのリポジトリ一覧を得る API はこちら。

デフォルトでは 30 件しか取得できなくてページングさせる必要がありますが、per_page パラメータを使うと 1 ページあたり 100 件まで取得できるようになります。

jq

jq に関しては特筆すべきことはないんですが、ダブルクオテーションを削除するために -r (--raw-output) を指定しています。

xargs

こちらも特段特別なことはしてないのですが、ghq get は引数が リポジトリ 1 つなので、-n 1 を指定しています。 また、clone 処理をはやめに終わらせるために -P 4 を指定して並列度を上げてます。このへんは CPU によって調節すれば良いでしょう。

PoEAA: Single Table Inheritance

Patterns of Enterprise Application Architecture (Addison-Wesley Signature Series (Fowler))

Patterns of Enterprise Application Architecture (Addison-Wesley Signature Series (Fowler))

Single Table Inheritance は、オブジェクトモデルにおいて表現された継承関係を DB にマッピングするためのデザインパターンの 1 つで、 継承関係にある各クラスのフィールドを 1 つのテーブルに集約させるという設計になります。他のパターンとしては、後日に読む

  • Class Table Inheritance
  • Concrete Table Inheritance

があります。

クラスを DB に永続化するとき、対象クラスに存在するフィールドはそのまま DB に反映されますが、存在しないフィールド値は、対応する DB 上のカラムに null で格納されます。 一方、DB 上のデータをオブジェクトにマッピングするときは、対象レコードが継承ツリー上のどのクラスを表現しているのかを、別カラムで表現しておく必要があります。

パターンの長所

  1. 1 テーブルのみを対象としておけば良くシンプル
  2. データを取得するときに JOIN が必要ない
  3. 特定のフィールドを継承ツリー上のどのクラスに移動したとしても、テーブル設計に影響を与えない

パターンの短所

  1. 特定のクラスでしか使われないカラムによって DB 容量が消費されてしまう
  2. 1 テーブルにデータが集中するため、パフォーマンス劣化に繋がる

OAuth 2.0 authorization code grant 実装ポイント

OAuth 2.0 で Authorization code grant を実装しようとする場合の設計/実装ポイントをまとめてみました。 RFC 6749 を読みながら書いていますが、「普通に作ったらこんなことしねーだろ」というポイントとかはガンガン除外しています。 なので、本気で OAuth 2.0 を理解したり作ったりしようとする人は、RFC 6749 を読みましょう。

共通

あたりまえといえばあたりまえですが、認可サーバは発行するいかなる識別子に関しても、そのサイズに関する情報を提供すべき(SHOULD)なので、そのあたりは仕様書に書いておきましょう。

クライアント登録

クライアント登録は OAuth 2.0 の範疇ではありませんが、OAuth 2.0 の前提はクライアントが認可サーバに登録されていることです。2.4. Unregistered Clients に記述がありますが、未登録のクライアントに対する扱いは OAuth 2.0 の対象外になっています。 クライアント開発者に対する要件(SHALL)として以下が要求されている(2)ため、これを登録しておくのは認可サーバの機能として認識しておくべきでしょう。 - クライアントタイプの指定 - リダイレクト URI の登録 - 認可サーバが要求するその他の情報

クライアントタイプの指定はクライアント開発者への要件であるのと関連して、認可サーバはクライアントタイプを憶測に頼らないという要件が明記されています(SHOULD NOT)。 リダイレクト URI については、Auth 2.0 の仕様書は複数個の登録が可能なのですが、OAuth 2.0 の実装によって違いがあります。

クライアント認証

認可サーバは登録済のクライアントに対し、認可サーバ毎に一意のクライアント識別子を発行します。なお、この識別子の長さについては仕様で定められていませんので、OAuth 2.0 を提供する場合は、仕様書上に書いておく必要があります。

クライアント認証に関しては、どのような方式の認証に対応しても良いことになっていますが、後述する Basic 認証のサポートは必須 (MUST) です。これは TLS で保護されなければなりません(MUST)。なお、クライアント識別子のみでクライアント認証を行ってはいけません (MUST NOT)。 また、パブリッククライアントに関してはクライアント認証をすることは良い(MAY)のですが、クライアントを識別する目的でその認証に信を置いてはいけないとなっています。これは、クライアント認証用のクレデンシャルが漏れ得ることが、パブリッククライアントの定義になっているからですね。 他の認証方式を採る場合は、クライアント識別子と認証方式のマッピングを保持しておかない(MUST)といけないので、Basic 認証以外を採用しようとする場合は、クライアント用のマスタテーブルなんかに認証方式のカラムが必要でしょう。それを元に、認証方式を切り替える実装になります。

クライアント認証でサポートが必須な Basic 認証に関しては、username に相当するものがクライアント識別子、password に相当するものがクライアント用パスワードになります。 他に、HTTP BODY でこれらの情報を送る方法もサポートはして良い(MAY)ですが、それは Basic 認証が使用できないようなクライアントに限られるべき (SHOULD) で、基本的には非推奨です (NOT RECOMMENDED) です。 また、パスワードで認証されるが故に、ブルートフォースアタックへの対策を採ることが必須(MUST)です。

認可エンドポイント

リソースオーナーに対して認可を得るために認可サーバ上に設けるエンドポイントです。もちろん TLS で保護されます(MUST)。

認可サーバは、最初にリソースオーナーの認証を行わなければなりません。が、もちろん OAuth は認可の仕様なので、認証仕様の記載はありません。認可サーバ上で Form 認証を行ったり、OpenID Connect を利用するなんてことが多いんじゃないかと思います。 このエンドポイントは GET のアクセスが必須 (MUST) で、同様に POST のサポートはしても良い (MAY) とされています。 パラメータの値が無い場合はパラメータ自体が無いものと捉えなければならず(MUST)、認識できないようなパラメータは無視する必要があります(MUST)。また、リクエスト/レスポンスのパラメータは重複してはなりません(MUST)。

認可リクエスト

認可リクエストは以下のような形式で、GET のサポートは MUST です。

パラメータ REQUIRED/OPTIONAL 備考
response_type REQUIRED codetokenの二択ですが、Authorization code の場合は code になる
client_id REQUIRED クライアント登録時に発行されるクライアント識別子
redirect_uri OPTIONAL リソースオーナー認証後に user-agent をリダイレクトさせる先
scope OPTIONAL アクセスの要求範囲を示します
state RECOMMENDED CSRF 対策のためクライアント側で設定される
  • response_typecode の場合、その後の OAuth の認可フローが Authorization code grant、token の場合は Impolicit grant に進むことになります。
    • このパラメータが送られてこなかった場合、認可サーバはエラー応答することが求められます(MUST)。
  • redirect_uri については OPTIONAL となっているものの、仕様上、認可サーバはクライアントに要求すべき (SHOULD) となっています。そして、クライアントが URI を指定した場合、その値がクライアント登録時に登録された値と同じであることを指定しなければなりません。これはオープンリダイレクタ対策だと思います。
    • 検証に失敗した場合、エラー応答を行うべき(SHOULD)とされ、リダイレクトさせてなりません(MUST NOT)。
  • scope が指定された場合、認可サーバは受け取った情報をそのままレスポンスとしてクライアントに送り返ります。リダイレクト先の CSRF 対策はクライアントの義務 (MUST) なのですが、それを達成するための推奨 (SHOULD) がこの state の利用になります。
    • クライアントは、この state に他者に類推できず(MUST)、クライアント・User-Agent 間でしかやりとりされない形で保持されなければなりません(MUST)。

以下のようなかんじ。

GET /authorize?response_type=code&client_id=s6BhdRkqt3&state=xyz&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
Host: server.example.com

scope は、空白で区切られた case-sensitive な文字列で構成されます。リクエストパラメータで送られてきたこの scope を無視するか否かは認可サーバ側に判断権があります。 ただし、要求された scope と別の scope を適用することを決定した場合、その結果を認可レスポンスとして返却するのは必須です (MUST)。 また、scope が要求されなかった場合、認可サーバはデフォルトの scope を採用して処理を進めるか、エラーとするかを決めなければなりません。

認可サーバは、このリクエストを受信した後、必須パラメータの存在とその値の妥当性を判定し、リソースオーナーを認証・認可した後で認可レスポンスを返却します。

認可レスポンス

リソースオーナーの認証後、認可サーバはユーザエージェントを、事前のクライアント登録で登録されていたリダイレクト URI にリダイレクトさせます。 リダイレクト先が TLS をサポートするのは必須ではありませんが SHOULD となっており、リダイレクト先が TLS でない場合は認可サーバ側でリソースオーナーに警告すべき(SHOULD)となっています。まぁリダイレクト先がスマフォのカスタム URI になっているような場合はどうしようもないですが、まさにこのケースは PKCE で保護しないといけない部分ですね。

パラメータ REQUIRED/OPTIONAL 備考
code REQUIRED 認可サーバによって生成された認可コード
state REQUIRED クライアントから要求された state の値
HTTP/1.1 302 Found
Location: https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA&state=xyz

認可コードは漏洩時のリスクを軽減するために有効期限を短かくすることが求められて(MUST)おり、最大でも 10 分が推奨(RECOMMENDED)されています。また、クライアントは同じ認可コードを 2 度以上使用してはならず(MUST)、 認可サーバはそういう重複利用は拒否しないといけません(MUST)。これを実現するためには、認可コードの有効フラグみたいなものを保持するなどの実装になりそうです。

一方、認可する際に何かしらのエラーがあった場合は、クライアントに対してエラーを返却します。

パラメータ REQUIRED/OPTIONAL 備考
error REQUIRED 下記参照
error_description OPTIONAL クライアントに対する追加情報
error_uri OPTIONAL クライアント開発者に対し、エラー情報を通知するページ
state REQUIRED クライアントから要求された state の値
HTTP/1.1 302 Found
Location: https://client.example.com/cb?error=access_denied&state=xyz

error については、いくつかの値になります。もちろん、リダイレクト URI が不正の場合は、リダイレクトさせてはいけません。。。

  • invalid_request: パラメータ不足、パラメータ不正
  • unauthorized_client: クライアントにリクエストの際に指定した response_type が許可されていない場合
  • access_denied: リソースオーナーか認可サーバによる認可拒否
  • unsupported_response_type: 認可サーバーがリクエストの際に指定した response_type による認可コード取得をサポートしていない場合
  • invalid_scope: 要求されたスコープの不正
  • server_error: 未知のエラー
  • temporarily_unavailable: 過負荷、メンテナンス中

トークンエンドポイント

トークンエンドポイントは、Authorization code grant における認可コードとアクセストークンの交換、および、リフレッシュトークンによるアクセストークンの更新に使用される、認可サーバ上のエンドポイントです。 もちろんこちらも認可エンドポイントと同様、TLS で保護されることが必須 (MUST) です。認可エンドポイントが GET へのサポートが必須だったのに対し、こちらは POST が必須です(MUST)。 パラメータの値が無い場合はパラメータ自体が無いものと捉えなければならず(MUST)、認識できないようなパラメータは無視する必要があります(MUST)。また、リクエスト/レスポンスのパラメータは重複してはなりません(MUST)。

なお、認可サーバは、

  • クレデンシャルを発行したクライアントに対しては、クライアント認証を行わなければなりません。
  • そのクライアントに対して発行された認可コードであることを検証しないといけません
  • 認可リクエストと同じ redirect_uri が要求されていることを検証しないといけません

トークンリクエスト

パラメータ REQUIRED/OPTIONAL 備考
grant_type REQUIRED Authroization code grant の場合 authorization_code
code REQUIRED 認可サーバから受け取った認可コード
redirect_uri REQUIRED(※1) 認可リクエストで送信した redirect_uri の値
client_id REQUIRED(※2)
  • ※1: 認可リクエストで送信していた場合
  • ※2: 認可リクエストでクライアントが認証されていなかった場合
POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb

トークンレスポンス

これに対して、認可サーバが返却するレスポンスはこんなかんじになります。

パラメータ REQUIRED/OPTIONAL 備考
access_token REQUIRED 認可サーバが発行したアクセストークン
token_type REQUIRED bearermac など、アクセストークンのタイプを示す
expires_in RECOMMENDED アクセストークンの有効期限(秒)
refresh_token OPTIONAL アクセストークンの更新に使用する
scope OPTIONAL(※)
  • expires_in については省略可能ですが、
  • scope: クライアントが要求してきたスコープと同じなのであれば省略可能です。違うのであれば、REQUIRED になります。

このトークンレスポンスについては、MediaType が applicaiton/json になります。 また、HTTP ヘッダとして、Cache-Controlno-store に、Pragmano-cache にするのは必須(MUST) となっています。

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache

 {
  "access_token":"2YotnFZFEjr1zCsicMWpAA",
  "token_type":"example",
  "expires_in":3600,
  "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
  "example_parameter":"example_value"
}

エラー時のレスポンスについては、ステータスコードは 400 (Bad Request) とし、 一方、認可する際に何かしらのエラーがあった場合は、クライアントに対してエラーを返却します。

パラメータ REQUIRED/OPTIONAL 備考
error REQUIRED 下記参照
error_description OPTIONAL クライアントに対する追加情報
error_uri OPTIONAL クライアント開発者に対し、エラー情報を通知するページ
state REQUIRED クライアントから要求された state の値

error については、いくつかの値になります。もちろん、リダイレクト URI が不正の場合は、リダイレクトさせてはいけません。。。

  • invalid_request: パラメータ不足、パラメータ不正
  • invalid_client: クライアント認証の失敗
  • invalid_grant: リフレッシュトークン不正や有効期限切れ、認可リクエストに含まれていた request_uriclient_id と一致しないなど。
  • unauthorized_client: クライアントにリクエストの際に指定した response_type が許可されていない場合
  • invalid_scope: 要求されたスコープの不正

invalid_client については、トークンリクエストヘッダに Authentication ヘッダがない場合は HTTP ステータスコードは 401 でも良く(MAY)、ある場合は 401 とし、レスポンスに WWW-AUthenticate ヘッダを含むことが MUST です。

アクセストークンの更新

リフレッシュトークンによるアクセストークンの更新についても、トークンエンドポイントで提供されます。 これ、Content-Type も HTTP メソッドも同じなんですね。実装しづらい。

パラメータ REQUIRED/OPTIONAL 備考
grant_type REQUIRED refresh_token
refresh_token REQUIRED リフレッシュトークン
scope OPTIONAL

もちろん、アクセストークンの更新の際も、(一度クライアントにクレデンシャルを発行しているのであれば)クライアントの認証は MUST です。 また、リフレッシュトークンがそのクライアントに発行されたものであること、および、リフレッシュトークンの検証も実施しなければなりません (MUST)。

なお、このタイミングで、リフレッシュトークンごと新しく発行しても良い(MAY)ことになっています。 この場合、クライアントは古いリフレッシュトークンを無効化しないといけません(MUST)。また、認可サーバは古いリフレッシュトークンを無効化して良い(MAY)ことになっています。

リソースへのアクセス

クライアントがリソースにアクセスしようとする際、リソースサーバはアクセストークンを検証し、スコープで認められたアクセスであること、および、有効期限が切れていないことを確認しなければなりません(MUST)。 アクセストークンに何を用いるかは RFC6749 では定義していませんが、この検証に失敗した場合、リソースサーバはクライアントにエラーを通知しなければなりません。

Touch IDには複数の指紋を登録できる

Apple Pay を使うようになってから、市井で頻繁に iPhone のロックを解除することが多くなりました。 ぼくは iPhone のセットアップ時に右手の指の指紋を登録していたのですが、iPhone は左ポケットに入れているので、Apple Pay で決済するときは右手に持ち替えてロックを解除する必要がありました。

ダルいなー、左手の指紋を登録しておけば良かったなーと思ったけど、ちゃんと調べたら iPhone には複数の指紋を登録できるんですね。

f:id:kiririmode:20170206233933p:plain

[設定]-[Touch IDとパスワード]-[指紋を追加] で可能です。 どうも 5 本まで登録できるみたいですね。知らんかったわ。

PoEAA: Serialized LOB

今日は Serialized LOB。

Patterns of Enterprise Application Architecture (Addison-Wesley Signature Series (Fowler))

Patterns of Enterprise Application Architecture (Addison-Wesley Signature Series (Fowler))

あんまり珍しくないパターンが多かったけど、このパターンには思い出があって、どうやったって仕様が固まらないけど開発だけは進めないといけなかった某サービス開発のときに使った(邪道です)。

Serialized LOB は、言ってみれば複雑な構造を持ちデータベースとマッピングしづらいデータを、プログラムの方でシリアライズしてデータベースの 1 カラムに突っ込むというパターンです。 本の中ではシリアライズの形式として XML が例示されていますが、今だったら JSON とかを使うんでしょうね。

便利ではあるんですけど、厄介なのはシリアライズし DB に叩き込んだデータは一般に SQL のクエリで検索キーにできない(しづらい)点です。最近(?)は Postgresql の JSON サポートとか、Oracle の XML DB とか、色々と RDB 側のサポートもありますね。ぼく自身は使ったことありませんが。 このため、Serialized LOB はトランザクションとしての「記録」には使えるけれども、その後、それらを使ってのデータ抽出なんかは厳しいと思います。

OAuth2.0で認可コードの漏洩を防ぐPKCE

OAuth 2.0 における通信は基本的に TLS で守られるのが基本なのだけれど、ひとつ TLS で守られない通信があります。それは Client・User-Agent 間の通信です。 Authorization Code Grant においては、認可サーバから認可コードを受けた User-Agent がその情報をクライアントに渡すときに発生します。

Authlete さんの以下の図がすごくわかりやすいのですが、User-Agent (Operation System Browser) から Malicious App に認可コードが漏れていることがわかります。 Native App では、これは Custom Scheme が悪意のある App に上書きされた場合等で発生します。 もちろん、認可コードが漏れたら、アクセストークンも漏れるので、このような認可コードの漏洩は防がなければなりません。

PKCE

これを防ぐのが、RFC 7636 で定義されている Proof Key for Code Exchange by OAuth Public Clients、通称 PKCE (ピクシー) です。 PKCE の原理はすごくシンプルで、認可サーバ側で、「認可リクエスト」を送信してきたクライアントと「トークンリクエスト」を送信してきたクライアントが同一であることを確認するという方法です。

  1. クライアントは、認可リクエストを送信する際、code_verifier という乱数を発生させた上で、ハッシュ関数 t を用いたハッシュ値を計算します。結果としてのハッシュ値 t(code_verifier) とハッシュ関数を、それぞれ code_challengecode_challenge_method として認可リクエストに含め、認可サーバに送信します。
  2. 認可サーバは、認可リクエストを受信した際、code_challengecode_challenge_method を保存しておきます。
  3. クライアントは、トークンリクエストを認可サーバに送信する際、1. で生成した code_verifier をトークンリクエストに含めます。
  4. 認可サーバは、トークンリクエストを受信すると、2. で保存した code_challenge_method である t と使って t(code_challenge) を計算し、それが、code_challenge と一致するかを検証します。

パラメータ追加のみで、認可フローを変えなくて良いので、導入しやすい方法だと思います。

上記ではハッシュ関数としていますが、実際に PKCE で使用できるのは以下の 2 つですが、まぁ S256 一択ですかね。

  1. plain: 恒等関数
  2. S256: SHA-256 でハッシュ化した値を BASE64 に変換したもの

参考文献