Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
5
Help us understand the problem. What are the problem?

posted at

updated at

JAR (JWT-Secured Authorization Request) に関する実装者の覚書

はじめに

この記事は、JAR と呼ばれる技術仕様『RFC 9101 The OAuth 2.0 Authorization Framework: JWT-Secured Authorization Request (JAR)』に関する実装者の覚書です。(英語版はこちら)

OpenID Connect Core 1.0 (OIDC Core) と JAR の間には幾つか衝突する部分があります。既存の認可サーバーだけでなく OpenID 互換性検査スイートにさえも影響を及ぼすため、OAuth コミュニティーはそれらの破壊的変更について長い間論争を続けました。

衝突する部分とは何か?それらはどのように解決されたのか? JAR 仕様の未来の読者や実装者のため、この記事はそれらについて記述します。

JAR とは?

JAR とは、認可リクエストパラメーター群を一つの署名付き JWT にまとめるための仕組みです。クライアントアプリケーションは、リクエストパラメーター群を個々に列挙する代わりに、その JWT を (ユーザーエージェントを介して) サーバーに送ります。

その JWT は『リクエストオブジェクト』と呼ばれます。リクエストオブジェクトは request パラメーターの値としてサーバーに送られるか、もしくは、その場所が request_uri パラメーターの値として送られます。

例えば、7 つの別々のクエリーパラメーターを持つ次の認可リクエストですが、

authorization-request.png

request パラメーターを使うと次のようになります。

authorization-request+request_ja.png

また、request_uri パラメーターを使うと次のようになります。

authorization-request+request_uri_ja.png

7 つのリクエストパラメーターは全てリクエストオブジェクトの中に置かれており、また同時に、リクエストオブジェクトの外にも重複して client_id パラメーターが置かれています。

JAR の初期ドラフトは、大胆にも client_id を落として、認可リクエストは request または request_uri のいずれかのパラメーターのみを使うようにと要求していました。しかし、認可サーバー実装者の多くが client_id は必須のまま残しておくべきだと主張しました。なぜでしょうか?理由は次の通りです。

  1. リクエストオブジェクトが対称鍵系アルゴリズムで暗号化されている場合、復号化のために共有秘密鍵が必要です。対称鍵系アルゴリズムでは、クライアントシークレットが鍵として使われるので (OIDC Core Section 10.1)、リクエストオブジェクトを復号化する前に、認可サーバーはクライアントアプリケーションを特定してデータベースからクライアントシークレットを取り出せなければなりません。暗号化されたリクエストオブジェクト内に client_id が含まれているとしても、認可サーバーは復号化の前にその値を知ることはできません。
  2. リクエストオブジェクトが request_uri で指定されている場合、認可サーバーはその場所にリクエストオブジェクトを取りにいかねばなりません。しかし、セキュリティー上の理由から、認可サーバーは事前登録されていない場所にリクエストオブジェクトを取りにいくことを拒否してもよい (個人的には「すべき」と思っています) とされています (参考:OpenID Connect Discovery 1.0, Section 3, require_request_uri_registration)。提示されたリクエスト URI と当該クライアント用に事前登録されているリクエスト URI 群との照合をおこなうため、リクエストオブジェクトを取りにいく前に、認可サーバーはクライアントアプリケーションを特定できなければなりません。離れた場所にあるリクエストオブジェクト内に client_id が含まれているとしても、認可サーバーはリクエストオブジェクトを取りにいく前にその値を知ることはできません。

衝突箇所

リクエストオブジェクトは 2014 に世に出た OIDC Core の一部 (Section 6) として定義されました。JAR の目的は、リクエストオブジェクトの仕様を OIDC Core から取り出し、汎用的に使えるようにすることです。この作業にはそれほど時間がかからないだろうと関係者は考えていましたが、JAR により導入される破壊的変更について OAuth コミュニティーからの同意を得るまでに、最初のドラフトから実に 6 年以上も経過しました。

リクエストパラメーター群のマージ

リクエストパラメーター OAuth 2.0 OIDC JAR
リクエストオブジェクト外の
リクエストパラメーター群
マージされる マージされる 無視される
リクエストオブジェクト内の
リクエストパラメーター群
マージされる マージされる 参照される

OIDC Core では、リクエストオブジェクト内外のリクエストパラメーター群はマージされます。これについては、Section 6.3.3 で次のように明確に述べられています。

The Authorization Server MUST assemble the set of Authorization Request parameters to be used from the Request Object value and the OAuth 2.0 Authorization Request parameters (minus the request or request_uri parameters). If the same parameter exists both in the Request Object and the OAuth Authorization Request parameters, the parameter in the Request Object is used.

一方、JAR ではリクエストオブジェクト外のリクエストパラメーター群を参照することは禁止されています。これについては、Section 5 で次のように述べられています。

However, the authorization server supporting this specification MUST only use the parameters included in the request object.

また、Section 6.3 にも次のようにあります。

The Authorization Server MUST only use the parameters in the Request Object even if the same parameter is provided in the query parameter.

response_type

response_type OAuth 2.0 OIDC JAR
リクエストオブジェクト外の response_type 必須 必須 無視
リクエストオブジェクト内の response_type 任意 任意。存在する場合は、リクエストオブジェクト外の値と一致しなければならない。 必須

OAuth 2.0 (RFC 6749) と OIDC Core では、response_type は必須パラメーターとされています。リクエストオブジェクトが response_type を含んでいたとしても、OIDC Core はリクエストオブジェクト外にも response_type を要求します。OIDC Core の Section 6.1 に次のような記述があります。

So that the request is a valid OAuth 2.0 Authorization Request, values for the response_type and client_id parameters MUST be included using the OAuth 2.0 request syntax, since they are REQUIRED by OAuth 2.0. The values for these parameters MUST match those in the Request Object, if present.

OIDC Core が OAuth 2.0 との互換性を重視していることは明らかです。しかし、JAR は、コミュニティーから反対意見が出たものの、リクエストオブジェクト外の response_type を要求しないことに決めました。

結果として、JAR 規則が適用される場合、OIDC Core に準拠して「リクエストオブジェクト外に response_type を持たない OIDC リクエスト」を拒否する認可サーバーは、誤った実装とみなされることになります。

当然、「JAR 規則が適用される場合にも response_type に関する OIDC Core の規定は有効とするべき」という提案もありました。しかし、最終的に到達した合意は、"servers must not require the duplicates" (サーバーは重複を要求してはならない) でした。(FAPI Issue 315)

『必須』を『任意または無視』に変更することは、逆方向の変更よりも、実装に対して大きなインパクトがあります。というのは、「早い段階で行われるパラメーターチェックを通過した後は必須パラメーターは常に利用可能である」という前提のもとに書かれているソースプログラムを見直し、適宜修正する必要があるからです。

scope

scope OAuth 2.0 OIDC JAR
リクエストオブジェクト外の scope 任意 必須で、かつ openid を含まなければならない。省略された場合、または openid を含まない場合、そのリクエストは OIDC リクエストとはみなされない (ただしこれはエラーではない)。もしもリクエストオブジェクトが与えられ、それが scope を含み、その scopeopenid を含む場合、リクエストオブジェクト外にも scope が必須となり、その scopeopenid を含まなければならない。 無視
リクエストオブジェクト内の scope 任意 任意。存在し、かつ openid を含む場合、リクエストオブジェクト外にも openid を含む scope が必須となる。 任意

OAuth 2.0 では scope パラメーターは任意ですが、OIDC Core は scope パラメーターを必須としました。下記は、OIDC Core の Section 3.1.2.1 から抜粋した scope パラメーターに関する説明です。

REQUIRED. OpenID Connect requests MUST contain the openid scope value. If the openid scope value is not present, the behavior is entirely unspecified. Other scope values MAY be present. Scope values used that are not understood by an implementation SHOULD be ignored. See Sections 5.4 and 11 for additional scope values defined by this specification.

加えて、Section 6.1 は、リクエストオブジェクトが scope を含み、その scopeopenid を含む場合、リクエストオブジェクト外にも重複して scope パラメーターを置くことを要求しています。

Even if a scope parameter is present in the Request Object value, a scope parameter MUST always be passed using the OAuth 2.0 request syntax containing the openid scope value to indicate to the underlying OAuth 2.0 logic that this is an OpenID Connect request.

response_type と同じ結論を適用するのであれば、サーバーは scope の重複指定を要求してはなりません。関連する議論は OAuth メーリングリストのアーカイブに見つけることができます ([OAUTH-WG] [JAR] scope parameter outside request object of OIDC request))。

結果として、JAR 規則が適用される場合、OIDC Core に準拠して「openid を含む scope をリクエストオブジェクト外に持たない OIDC リクエスト」を拒否する認可サーバーは、誤った実装とみなされることになります。

署名

署名 OAuth 2.0 OIDC JAR
リクエストオブジェクトの署名 任意 任意 必須

JAR は、リクエストオブジェクトの署名を常に要求します。一方、OIDC Core では署名の無いリクエストオブジェクトも許容されます。OIDC Core の Section 6.1 は次のように述べています。

The Request Object MAY be signed or unsigned (plaintext)

興味深い副作用があります。もしも認可サーバーが常に JAR 規則を適用するのであれば、ディスカバリードキュメント (OpenID Connect Discovery 1.0) 内の request_object_signing_alg_values_supported から none を取り除くべきです。

FAPI

FAPI (Financial-grade API) は高度な API セキュリティーのための仕様です。FAPI は OIDC を基盤として策定されているので、FAPI の実装も JAR により影響を受けます。

参考までに、FAPI Part 2 には次に示すように、リクエストオブジェクトに関係する要求事項が幾つかあります。

  • [FAPI Part 2, Section 5.2.2, Clause 1] shall require the request or request_uri parameter to be passed as a JWS signed JWT as in clause 6 of OIDC;
  • [FAPI Part 2, Section 5.2.2, Clause 10] shall require that all parameters are present inside the signed request object passed in the request or request_uri parameter;
  • [FAPI Part 2, Section 5.2.2, Clause13] shall require the request object to contain an exp claim;
  • [FAPI Part 2, Section 8.6, Clause 1] shall use PS256 or ES256 algorithms;

PAR

PAR (OAuth 2.0 Pushed Authorization Requests) はもう一つの新しい仕様です。クライアントアプリケーションは認可リクエストを認可サーバーに事前登録し、登録した認可リクエストに対応するリクエスト URI の発行を受けます。そのリクエスト URI は、クライアントアプリケーションが認可リクエストを投げる際に request_uri パラメーターの値として用いることができます。

次の図は PAR の流れを示しています。詳細は『図解 PAR : OAuth 2.0 Pushed Authorization Requests』を参照してください。

par_ja.png

私がここで PAR に言及した理由は、PAR 仕様が、PAR エンドポイント (Pushed Authorization Request Endpoint) に渡されるリクエストオブジェクトの処理を JAR 規則に従って処理するように要求しているからです。下記は PAR 仕様からの抜粋です。

Clients MAY use the request parameter as defined in JAR to push a request object JWT to the authorization server. The rules for processing, signing, and encryption of the request object as defined in JAR apply.

PAR エンドポイントは登録された認可リクエストに対応するリクエスト URI を発行します。リクエスト URI の値と有効時間は、次の例のように、request_uri および expires_in の値として、PAR エンドポイントからのレスポンスに含まれます。

{
  "request_uri": "urn:example:bwc4JK-ESC0w8acc191e-Y1LTC2",
  "expires_in": 90
}

混乱を招きがちですが、発行されるリクエスト URI の有効時間と登録された認可リクエストの有効期限は異なります。次の表は、何がそれぞれの値に対応しているかを示しています。

リクエスト URI リクエスト URI の有効時間 参照される認可リクエストの有効期限
request を用いた PAR リクエストで発行 PAR レスポンス内の expires_in リクエストオブジェクト内の exp
request を用いない PAR リクエストで発行 PAR レスポンス内の expires_in N/A
その他 N/A 参照されるリクエストオブジェクトの内の exp

見落としやすい点ですが、PAR エンドポイントの実装は、認可リクエストを保存する時にリクエストオブジェクトの exp クレームに関する情報を落としてはいけません。というのは、発行されたリクエスト URI が後で使われる際、exp クレームの値が参照されるかもしれないからです。例えば、[FAPI Part 2, Section 5.2.2, Clause 13] の検証ステップでは、exp クレームの存在が確認されます。

メタデータ

JAR は、サーバーとクライアントのそれぞれに、require_signed_request_object というメタデータを定義しています。

Section 9.2 "OAuth Authorization Server Metadata Registry" にある、当該メタデータの説明は次のようになっています。

Indicates where authorization request needs to be protected as Request Object and provided through either request or request_uri parameter

メタデータの名前と説明は、このメタデータは署名付きリクエストオブジェクトを要求するか否かを制御するだけかのような印象を与えます。しかし、Section 10.5 "Downgrade Attack" を読むと、それ以上の意味を持つことが分かります。

When the value of it as a server metadata is true, then the server MUST reject the authorization request from any client that does not conform to this specification. It MUST also reject the request if the request object uses "alg":"none". If omitted, the default value is false.

つまり、"require_signed_request_object":true は、(1) 認可リクエストは request または request_uri のいずれかを用いなければならない、かつ、(2) リクエストオブジェクトは JAR 規則に従って処理・検証される、ということを意味します。

"require_signed_request_object":true の際に期待される動作は、様々なユースケースをサポートしたい認可サーバーの実装にとっては、残念ながら柔軟とは言えません。そのため、ベンダーはカスタムオプションを用意することになるでしょう。例えば Authlete (バージョン 2.2) は、上記の (1) と (2) に対応する二つの別々の設定項目を提供しています。Connect2idnode-oidc-provider などの他のベンダーや個人も、過去に OAuth メーリングリストで彼らの JAR に対するアプローチを説明しています。興味があればメールアーカイブを覗いてみてください。

例 (Authlete)

request-object-configuration-items_ja.png

リクエストオブジェクト
必須」を選択すると、このサービスへの認可リクエストは request または request_uri パラメーターを用いて常にリクエストオブジェクトを指定しなければなりません。

ここで必須を選択し、かつ、リクエストオブジェクト処理JAR 互換を選択すると、サービスは require_signed_request_object サーバーメタデータが true であるかのように動作します。当該サーバーメタデータの詳細については JAR (JWT Secured Authorization Request) 仕様を参照してください。
リクエストオブジェクト処理
JAR 互換」を選択すると、リクエストオブジェクトは JAR (JWT Secured Authorization Request) に定義されている規則に基づいて処理されます。「後方互換」を選択すると、OpenID Connect Core 1.0 に定義されている規則が適用されます。

JAR 規則と OIDC Core 1.0 規則の差分は次の通りです。
  • JAR 規則では、リクエストオブジェクトの署名が常に要求されます。
  • JAR 規則では、リクエストオブジェクト外のリクエストパラメーターを参照することが禁止されます。結果として、JAR 規則が適用される場合、リクエストオブジェクト外のパラメーター群とリクエストオブジェクト内のパラメーター群はマージされなくなります。
  • OIDC Core 1.0 規則では、リクエストオブジェクト内に response_type が含まれる場合でも、response_type クエリー/フォームパラメーターをリクエストオブジェクト外にも置かなければなりません。一方、JAR 規則が適用される場合、リクエストオブジェクト外の response_type クエリー/フォームパラメーターは必須ではなくなります。しかし、代わりに、JAR 規則ではリクエストオブジェクトが常に response_type を含まなければなりません。
  • OIDC Core 1.0 規則では、OIDC リクエストは、リクエストオブジェクト内に scope が含まれる場合でも、openid を含む scope クエリー/フォームパラメーターをリクエストオブジェクト外にも置かなければなりません。一方、JAR 規則が適用される場合、リクエストオブジェクト外の scope クエリー/フォームパラメーターは必須ではなくなります。なお、認可リクエストが OIDC リクエストとして認識されるためには openid を含む scope をリクエストオブジェクト内に含めておく必要があります。

ここで JAR 互換を選択し、かつ、リクエストオブジェクト必須を選択すると、サービスは require_signed_request_object サーバーメタデータが true であるかのように動作します。当該サーバーメタデータの詳細については JAR 仕様を参照してください。

おわりに

FAPI バージョン 1 は 2020 年末までにファイナライズされ、FAPI バージョン 2 が始まります。JAR は、FAPI バージョン 2 の構成要素として採用されることになっています (参考:FDX Global Summit Fall 2020 での崎村さんのキーノート "Global Adoption of FAPI Among Open Banking Standards... And Beyond")。

JAR は破壊的変更を含みますが、その採用は既定路線となっています。新しい仕様への準備をしていきましょう。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
5
Help us understand the problem. What are the problem?