ループバックインターフェースリダイレクション

# はじめに

**[RFC 8252](https://www.rfc-editor.org/rfc/rfc8252.html) OAuth 2.0 for Native Apps** の **[7.3. Loopback Interface Redirection](https://www.rfc-editor.org/rfc/rfc8252.html#section-7.3)**（**ループバックインターフェースリダイレクション**）の話です。

# 仕様

同仕様は、OAuth 2.0 のリダイレクション URI のホスト部が**ループバック IP アドレス**の場合、認可サーバーは**ポート番号を可変**として扱わなければならないと述べています。

> _**The authorization server MUST allow any port to be specified** at the time of the request for loopback IP redirect URIs, to accommodate clients that obtain an available ephemeral port from the operating system at the time of the request._

具体的には、認可リクエストに含まれるリダイレクション URI のホスト部が `127.0.0.1` もしくは `::1` 相当の場合、その URI を事前登録されたリダイレクション URI 群と比較する際、認可サーバーは「ポート番号の差異を無視して比較する」という動作をしなければなりません。

例えば、クライアントが `http://127.0.0.1/callback` をリダイレクション URI として事前登録しており、当該クライアントからの認可リクエストに `redirect_uri=http://127.0.0.1:12345/callback` が含まれていた場合、認可リクエストで指定された `http://127.0.0.1:12345/callback` は事前登録されているものと一致すると、認可サーバーは判断しなければなりません。

# 仕様衝突

問題は、RFC 8252 Section 7.3 の要求事項が、**[RFC 6749](https://www.rfc-editor.org/rfc/rfc6749.html) The OAuth 2.0 Authorization Framework** の [3.1.2.3. Dynamic Configuration](https://www.rfc-editor.org/rfc/rfc6749.html#section-3.1.2.3) の要求事項：

> _If the client registration included the full redirection URI, the authorization server MUST compare the two URIs using **simple string comparison** as defined in [RFC3986] Section 6.2.1._

および **[OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html)** の [3.1.2.1. Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest) の要求事項：

> **redirect_uri**
> _REQUIRED. Redirection URI to which the response will be sent. This URI MUST exactly match one of the Redirection URI values for the Client pre-registered at the OpenID Provider, with the matching performed as described in Section 6.2.1 of [RFC3986] \(**Simple String Comparison**\). （以下略）_

と衝突することです。両要求事項とも**単純文字列比較**（**Simple String Comparison**）を明示的に要求しており、ポート番号が可変となることを許していません。

# 衝突解消

社内の識者（[Joseph Heenan](https://www.linkedin.com/in/josephheenan/) と [Justin Richer](https://www.linkedin.com/in/justinricher/)）と相談した結果、歴史的経緯を踏まえ、RFC 6749 Section 3.1.2.3 と OIDC Core 1.0 Section 3.1.2.1 の要求事項よりも RFC 8252 Section 7.3 が優先する、という結論に至りました。

ただし、**[Financial-grade API 1.0 Baseline](https://openid.net/specs/openid-financial-api-part-1-1_0-final.html)** の [7.5. Native Apps](https://openid.net/specs/openid-financial-api-part-1-1_0-final.html#native-apps) は、RFC 8252 の元となった [BCP 212](https://www.rfc-editor.org/info/bcp212) に言及した上でループバックインタフェースリダイレクションのサポートを禁止しているため、

> _When registering redirect URIs, authorization servers_
>
> _1. shall not support "Private-Use URI Scheme Redirection"; and_
> _2. **shall not support "Loopback Interface Redirection".**_

FAPI の文脈では RFC 8252 Section 7.3 の動作をサポートしてはいけません。

# 実装

## スキーム部

RFC 8252 の [7.3. Loopback Interface Redirection](https://www.rfc-editor.org/rfc/rfc8252.html#section-7.3) および [8.3. Loopback Redirect Considerations](https://www.rfc-editor.org/rfc/rfc8252.html#section-8.3) で繰り返し「ループバックリダイレクト URI は `http` を使う」と述べているため、「スキーム部が `http` の場合のみポート番号を可変にする」という解釈・実装もありえますが、識者に相談したところ、「`http` の場合のみに限定する積極的な理由はない」とのことでした。

そのため、**[Authlete](https://www.authlete.com/ja/)** の実装ではスキーム部が `http` でなくてもポート番号を可変として扱います。なお、**RFC 8252 Section 7.3 の動作がサポートされるのは、Authlete 2.2.22 以降です。**

## localhost

RFC 8252 の [8.3. Loopback Redirect Considerations](https://www.rfc-editor.org/rfc/rfc8252.html#section-8.3) は、下記に引用した通り、`localhost` の使用を非推奨としています。そのため、「ホスト部が `localhost` の場合はポート番号を可変にしない」という実装もありえます。

> _While redirect URIs using `localhost` (i.e., `"http://localhost:{port}/{path}"`) function similarly to loopback IP redirects described in [Section 7.3](https://www.rfc-editor.org/rfc/rfc8252.html#section-7.3), **the use of `localhost` is NOT RECOMMENDED**. Specifying a redirect URI with the loopback IP literal rather than `localhost` avoids inadvertently listening on network interfaces other than the loopback interface. It is also less susceptible to client-side firewalls and misconfigured host name resolution on the user's device._

しかし、（１）マイクロソフト社の Active Directory が `localhost` の場合もポート番号を可変とする実装になっており（[Localhost の例外](https://docs.microsoft.com/ja-jp/azure/active-directory/develop/reply-url#localhost-exceptions)）、同じ動作を要望する声が実際に存在すること、および（２）`localhost` を使用するかどうかはクライアント側で完全に制御可能であること、といった理由から、Authlete の実装でも `localhost` の場合もポート番号を可変として扱います。

## 設定

後方互換性の維持を選択できるよう、RFC 8252 Section 7.3 の動作のオン／オフを切り替えるためのフラグ『リダイレクション URI の可変性』を Authlete では用意しました。このフラグは、[Service](https://authlete.github.io/authlete-java-common/com/authlete/common/dto/Service.html) クラスの `loopbackRedirectionUriVariable` プロパティー（参照：[isLoopbackRedirectionUriVariable()](https://authlete.github.io/authlete-java-common/com/authlete/common/dto/Service.html#isLoopbackRedirectionUriVariable--)）に相当します。

# おわりに

新しいユースケースや攻撃手法の登場を契機として OAuth 関連仕様は増えていきます。そのような中で、本記事で紹介した事例のように、仕様が衝突することも珍しくありません。

仕様の衝突には、「[RFC 7518](https://www.rfc-editor.org/rfc/rfc7518.html) で推奨とされている署名アルゴリズム `RS256` がセキュリティ上の理由により [Financial-grade API 1.0 Advanced](https://openid.net/specs/openid-financial-api-part-2-1_0-final.html) では使用禁止となった」といった理解しやすいものから、[RFC 9101](https://www.rfc-editor.org/rfc/rfc9101.html) による破壊的仕様変更（参照：[JAR (JWT-Secured Authorization Request) に関する実装者の覚書](https://qiita.com/TakahikoKawasaki/items/d52ce91866ae0dcc2ff7)）といった難解なものまで、様々なものがあります。

仕様の変遷に追随して自社の認可サーバーの実装を更新し続ける作業はかなり労力を要します。これらの作業を専門家に任せたい方は是非 Authlete 社に[お問い合わせ](https://www.authlete.com/ja/contact/)ください！