🎂

IETF OAuth WGの仕様全部...じゃなく差分見る - 2020/12

2020/12/25に公開

どーも、ritouです。

Digital Identity技術勉強会 #iddance Advent Calendar 2020最終日の記事です。

去年のいつだったか、Qiitaにこんな記事を書きました。
IETF OAuth WGの仕様全部見る - 2019/10 - Qiita

今回の記事は、1年ちょっとたって、これどうなったかなー?って言うものです。
たくさんある上に同じこと書いてもアレなので、差分を整理しつつ前の記事と合わせて読んでもらえると良いかと思います。

仕様のありか

ここです。

Oauth Status Pages

RFCになった系 : 18 -> 22 (←仕様の数)

ここ1年でRFCになったのは4つあります。どれも前の記事で「RFC-Editor's Queue」「IESG Processing:」という、RFC間近な状態でしたので大きな変更もなさそうです。
振り返りなのでざっくりな説明と関連リンクを載せておきます。

RFC 8725 JSON Web Token Best Current Practices

様々なユースケースで使われることになったJWT、それを扱う上で 基本的な9個の脅威、脆弱性と12個のベストプラクティス が書かれています。

もうさすがに JWTには脆弱性があるので使ってはいけない みたいなこと言う人も減った気がしますが、今後もOAuth/OIDC/GNAPなどのプロトコル、そしてそれ以外のユースケースでもJWTがたくさん使われると思います。特にオレオレ設計でJWTを利用する際は、本体であるJOSE系のRFCと合わせてこれを読み、まともな実装ができているかを確認するのも良いでしょう。

RFC 8705 OAuth 2.0 Mutual-TLS Client Authentication and Certificate-Bound Access Tokens

いわゆるMTLSってやつです。
内容は

  • PKIや自己署名なクライアント証明書を用いたClient認証
  • クライアント証明書とAccess Tokenの紐付け

という感じです。

OAuth/OIDCでやりとりされるAccess TokenをSender-Constrainedにするための仕組み としてFAPIなどでも重要な立ち位置にいるのがMTLSです。

  • トークン要求時
    • ClientはToken Endpointに各種トークンを要求する際にクライアント証明書を利用する
    • AuthZ ServerはAccess/Refresh Tokenを証明書と紐付ける
  • リソースアクセス時
    • Clientはリソースアクセス時にもクライアント証明書を利用する
    • Resource ServerはAccess Tokenとクライアント証明書との紐付けを検証する

このような処理の流れは後でも出てくるので覚えておきましょう。

RFC 8707 Resource Indicators for OAuth 2.0

認可リクエスト、トークンリクエスト、リソースアクセスと一連の流れの中で、Access Token/Refresh Tokenの対象となる "Resource" を指定するための仕様です。

表現として近いのはGoogleのOAuth 2.0のscopeです。
OAuth 2.0 Scopes for Google APIs  |  Google Identity Platform

利用したいAPIのパスのような文字列をscopeに指定しています。

Scopes for YouTube Data API, v3

これを使いたい!ってのを指定する感じです。
直感的ですが、あくまでGoogleが決めたscopeの仕様です。

対照的なのはOpenID Connectのscopeとかでしょうかね。 "openid profile email" とかです。
この場合、いわゆるUserInfo API使わせてくれ!とは言ってるわけすが、実際のUserInfo APIのURLはそれぞれサービスによって異なります。
例えば複数のサービスのアカウントでログインする機能を作りたい場合、それぞれのUserInfo APIの値を指定してやるより「決められたscopeを指定、アクセス先はConfigなどから取得する」の方が実装はシンプルです。

なので、これがscopeよりも優れているという話ではなく、要件によって使い分けていく必要がある類のものでしょう。使いどころとしては

  • 1つのscopeに複数のリソースがあり、Clientから細かく利用したいリソース を指定 -> リソースオーナーに細かい単位の権限要求
  • アクセストークン要求時に 今から使う予定のResource を指定 : クライアントからリソースサーバーに送られるAccess Tokenの権限を最小限に
  • Clientからの要求はscope単位でざっくり、ユーザーの許可は細かく -> 認可サーバー、リソースサーバーはトークンに紐づけられたResourceの値でアクセスコントロールが可能

というあたりがあるでしょう。
ちゃんと整理すると「何とかパターン」みたいな名前がつけられそうなぐらいまで精査できそうな気もしますがこの辺にしておきます。

RFC 8693 OAuth 2.0 Token Exchange

これももうちょっと注目されて欲しい仕様ではありますが、仕様の対象がマイクロサービスなどでのある程度複雑なトークン発行システムとかなので、あまり記事なども見かけなかった気がします。

もうちょっとわかりやすいユースケース、例えばクライアントがソーシャルログインで取得した外部サービスのOIDC IDTokenを認可サーバーに送って認証(自分とこのIDToken発行)みたいなのとかにも応用できるかなど、イレギュラーかもしれませんが使いどころをもう少し考えてみたいですね。

IESG Processing:

JWT BCPがRFCになったため卒業、新たに "JSON Web Token (JWT) Profile for OAuth 2.0 Access Tokens" がここに入っています。

JSON Web Token (JWT) Profile for OAuth 2.0 Access Tokens

JWT形式のAccess Tokenについては "識別子型" vs "内包型" vs "ハイブリッド" もあるよみたいな説明をAuthlete社の勉強会で話されていて、自分でもブログの記事を書いたりしました。正解は一つではないと思いますが、JWTの利点を生かした設計ができれば良いのかなと思っています。

また、LocalStorage vs Cookieどっちがいいとか相変わらずわちゃわちゃしてる勢も、JWTの中身はというとユーザーIDに署名できればOKぐらいな設計/実装も見られます。 内部のセッション表現と外部サービスに渡すトークンの設計は厳密には一緒ではないですが、JWTを用いた設計をする際にはこの仕様も結構役に立つんじゃないかなと思っています。

ここ一年でRevision 02から10へと活発な更新が続いている仕様です。
RFCになったタイミングなどで解説を書こうかなと思います。

Active: 5 -> 6

5個あったのが2個になり、新たに4個入ってきて現在6個です。
増えたやつだけざっと紹介します。

OAuth 2.0 Demonstrating Proof-of-Possession at the Application Layer (DPoP)

MTLSと同様に、Sender-constrained な Access Token を実現するための仕組みです。
クライアント証明書のハンドリングが必要なMTLSに対し、DPoPは鍵ペアを管理してJWTなヘッダを付与することで紐付け、検証を可能とします。

  • トークン要求時
    • ClientはToken Endpointに各種トークンを要求する際に鍵ペアを生成、公開鍵情報とHTTPリクエストの情報を含んだJWT(DPoP proof JWT)を生成してHTTPヘッダーに含む
    • AuthZ ServerはAccess/Refresh Tokenを公開鍵と紐付ける
  • リソースアクセス時
    • Clientはリソースアクセス時にも公開鍵情報とHTTPリクエストの情報を含んだJWT(DPoP proof JWT)を生成してHTTPヘッダーに含む
    • Resource ServerはAccess Tokenとの紐付けを検証する
# Access Token Request to Token Endpoint
## Request
POST /token HTTP/1.1
   Host: server.example.com
   Content-Type: application/x-www-form-urlencoded;charset=UTF-8
   DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6Ik
    VDIiwieCI6Imw4dEZyaHgtMzR0VjNoUklDUkRZOXpDa0RscEJoRjQyVVFVZldWQVdCR
    nMiLCJ5IjoiOVZFNGpmX09rX282NHpiVFRsY3VOSmFqSG10NnY5VERWclUwQ2R2R1JE
    QSIsImNydiI6IlAtMjU2In19.eyJqdGkiOiItQndDM0VTYzZhY2MybFRjIiwiaHRtIj
    oiUE9TVCIsImh0dSI6Imh0dHBzOi8vc2VydmVyLmV4YW1wbGUuY29tL3Rva2VuIiwia
    WF0IjoxNTYyMjYyNjE2fQ.2-GxA6T8lP4vfrg8v-FdWP0A0zdrj8igiMLvqRMUvwnQg
    4PtFLbdLXiOSsX0x7NVY-FNyJK70nfbV37xRZT3Lg

## DPoP proof JWT
### Header
{
  "typ": "dpop+jwt",
  "alg": "ES256",
  "jwk": {
    "kty": "EC",
    "x": "l8tFrhx-34tV3hRICRDY9zCkDlpBhF42UQUfWVAWBFs",
    "y": "9VE4jf_Ok_o64zbTTlcuNJajHmt6v9TDVrU0CdvGRDA",
    "crv": "P-256"
  }
}
### Payload
{
  "jti": "-BwC3ESc6acc2lTc",
  "htm": "POST",
  "htu": "https://server.example.com/token",
  "iat": 1562262616
}

# Request to Resource Server
## Request
   GET /protectedresource HTTP/1.1
   Host: resource.example.org
   Authorization: DPoP Kz~8mXK1EalYznwH-LC-1fBAo.4Ljp~zsPE_NeO.gxU
   DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6Ik
    VDIiwieCI6Imw4dEZyaHgtMzR0VjNoUklDUkRZOXpDa0RscEJoRjQyVVFVZldWQVdCR
    nMiLCJ5IjoiOVZFNGpmX09rX282NHpiVFRsY3VOSmFqSG10NnY5VERWclUwQ2R2R1JE
    QSIsImNydiI6IlAtMjU2In19.eyJqdGkiOiJlMWozVl9iS2ljOC1MQUVCIiwiaHRtIj
    oiR0VUIiwiaHR1IjoiaHR0cHM6Ly9yZXNvdXJjZS5leGFtcGxlLm9yZy9wcm90ZWN0Z
    WRyZXNvdXJjZSIsImlhdCI6MTU2MjI2MjYxOH0.lNhmpAX1WwmpBvwhok4E74kWCiGB
    NdavjLAeevGy32H3dbF0Jbri69Nm2ukkwb-uyUI4AUg1JSskfWIyo4UCbQ

## DPoP proof JWT
### Header
{
  "typ": "dpop+jwt",
  "alg": "ES256",
  "jwk": {
    "kty": "EC",
    "x": "l8tFrhx-34tV3hRICRDY9zCkDlpBhF42UQUfWVAWBFs",
    "y": "9VE4jf_Ok_o64zbTTlcuNJajHmt6v9TDVrU0CdvGRDA",
    "crv": "P-256"
  }
}
### Payload
{
  "jti": "e1j3V_bKic8-LAEB",
  "htm": "GET",
  "htu": "https://resource.example.org/protectedresource",
  "iat": 1562262618
}

この仕様を使うとPublic ClientでもSender-Constrained Tokenが利用できるということで、ユースケースに応じてMTLSとこれが今後使われていくでしょう。

OAuth 2.0 Rich Authorization Requests

通称、"RAR"ちゃんです。
盛り盛りの認可リクエストを利用するための仕様です。

  • 必要なユーザーの属性情報を細かく指定
  • 決済に必要な情報を細かく指定
  • その他いろいろな分野でやりとりする情報

を無理やり既存の認可リクエストのパラメータに載せるのではなく、authorization_details と言うパラメータにJSON形式で指定できるようにしましょうと言うことですね。

でも、そんなことしたら認可リクエストが大きくなりすぎちゃう...と気になります。そこで...

OAuth 2.0 Pushed Authorization Requests

こっちは通称、"PAR"ちゃんです。
45周年を迎えたキキララみたいなもんでしょう。

RARで大きくなるともうGETのリクエストパラメータに載せるのはしんどいし、細かーく指定したものをユーザー自身とかに途中で書き換えられたら実に悲しいですよっと。
そこで、事前にバックチャンネルでドカンと送ってしまおうと言うのがPARです。

OIDC CIBAやOAuth 1.0でもいいですが、最初にバックチャンネルで認可リクエストを送っておいてそれにひもづく値を引っ張り回すのはフロントエンドでたくさんの値を取り回すのよりも色々と安全にできます。
新しい仕様であるGNAPの理解にもつながるかもしれません。

The OAuth 2.1 Authorization Framework

OAuth 2.1ってやつですね。これもいくつか解説記事が出ています。
その度にコメントしてるんですが、OAuth 2.0との差分という部分に書かれていない差分である、ClientType=credentialed についてちょっと紹介しておきます。

"credentialed": Clients that have credentials and their identity has
been not been confirmed by the AS are designated as "credentialed
clients"

今までは

  • Confidential : Client Secretを安全に管理できる
  • Public : できない。のでClient Secretを使わない

ぐらいで分類されていた部分に

  • Credentialed : アプリのインストール時などに動的にClient RegistrationなどでCredentialを取得、保管している

みたいな立ち位置のやつが加わった感じです。
今のところは Confidential もしくは Credentialed ならクライアント認証やる! みたいな記述が多いですが、もう少しこいつの扱いがわかりやすく定義されると良いかなーと言うのと、差分に書いておいてくれ!っと言うのが今のところの感想です。

まとめ

RFCになったもの、もうちょいのもの、議論が熱いものに絞って差分を調べてみました。
とりあえず、今回の感想としては

Authleteの人、ほぼ全部解説記事出しとるやないか。理解が捗るな。

と言うことですね。
興味を持っていただいた方はさらに細かく調べて解説でも書いてもらうと次回以降追記しますのでやってみましょう。

また、OAuth関連の仕様こんな感じですが、他にも関連する仕様はあります。
OpenID Foundationの各種WGで策定中の仕様もいつかまとめたいと思います(下書きはあるけど心が折れている。誰か一緒に やらないか!)。

Specifications | OpenID

アドカレ終わり

記事の投稿、閲覧、SNSでの意見など、ご参加いただいた皆様、お疲れ様でした。
個人としては、内容はともかく、今年は #iddance だけで5本も書きました。
もう、この人ぐらい●にそうです。

この人ぐらい●にそう

まぁ、生きてたらまた来年やりましょう。
ではまた。

Discussion