認証機能を独自実装する代わりにIDaaSのREST APIを使うアプローチ

こんにちは、ritou です。

最近のあれこれでIDaaSと呼ばれる機能に注目が集まっているような気がしますが、どうしてもフロントエンドでの導入部分が目に付きます。 「新規サービスで使っていこう」ならまだしも「既存のを何とかしたい」みたいな場合にフロントエンドまでごっそり変えるのなんて腰が重くなって仕方ない感じでしょう。

そこで今回は、REST APIを用いた新規導入、移行というアプローチもあるのかなという話を書いておきます。

IDaaS の REST API

この辺りをみてみてはどうでしょう。

APIベースで登録/ログイン/パスワード変更など結構揃っている印象です。 Firebaseだとソーシャルログインも実現できそうですし、Auth0のはまだあんまりじっくりみてないですがMFAとかも書いてあります。 あとはCognitoなんかもAPIいっぱいありますよね。

導入方法

このようなREST APIでは

  • UIを自前で用意
  • 受け取った値をさばくバックエンドの処理をREST APIで実現

という感じになりますね。それこそDeviseでやってたところをこのREST APIを使うパターンです。 サービスの種類は違いますがAuthleteはこれで認可サーバーを作っていく形ですね。

SPAかどうかにもあまり依存せずに使えそうですし、フロントにIDaaSのSDKを入れてガッツリUIレベルまでIDaaSに従う実装よりも自然に導入できそうな気がします。 私自身、自前でいくつか認証機能、基盤の開発をやってきた中でまだガッツリこの方式で作り込んだことはないんですが、やるとしたらこのアプローチかなと考えています。

IDaaS導入における不安要素にIDaaSが落ちたらどうする問題があるかと思うんですが、例えばREST APIを用いて取得したユーザー情報を "外部サービスであるIDaaSからの認証結果" と捉えてセッション管理などを自前にする設計パターンもあるんじゃないかと。 万が一IDaaSが落ちてたとしてもエラーハンドリングというかConfig設定とかで一時的にIDaaSに流さないようにし、登録済みメールアドレスなどを用いて自前でリカバリーというか暫定的な認証機能を提供するような感じならそれほどリスクなくできそうかなー、いやでもやっぱめんどくせーなーなどと思いながら引き続き検討してみるつもりです。

IDaaSにとってそれを使うサービスはどんな役割なのかを意識しよう

例えばOpenID Connectでいうところで ”IDaaSがOP、利用サービスがClient" という立場であるように考えがちです。 今回紹介したREST APIを使うイメージとあっているでしょう。

しかし、フロントエンドのSDKなどを使う場合、セッション管理のあたりまでJSでできてしまいます。 IDaaSの責務でセッション管理までする場合、利用するサービスが行うべきことは「現在ログイン中のユーザーを把握してそれに対するリソースアクセスを提供する」ということになり、役割としては利用するサービスのSPAがClient、バックエンドサーバーがResource Serverとなります。

IDaaSを効果的に使っていくためにはこの辺りも意識して設計/実装を進める必要があるでしょう。

まとめ

  • REST API結構ある
  • フロントエンドをあまり使わずにバックエンドで導入していくアプローチもあるのではないか
  • 自分はClientを作るのかResource Serverだけにするのかというレベルの検討も必要そう

おまけ

Firebase AuthnのAPIに「OAuth認証情報でサインインする」というのがあります。

https://firebase.google.com/docs/reference/rest/auth#section-sign-in-with-oauth-credential

いわゆるソーシャルログインをAPIベースでやろうとするものですね。

過去にネイティブアプリとバックエンドサーバーをOAuth 2.0のClient/Serverとして作り込まれたプラットフォームに関わっていたときに、ネイティブアプリでのソーシャルログイン実装でこのような独自拡張を実装したことがあります。

メンテナになってるPerlのOAuth 2.0 Serverライブラリである OAuth::Lite2 にはその痕跡が残されています。やりたい放題でした。

Changes - metacpan.org

0.09 2014-07-22T07:36:43Z
    - rename grant_type to external_service
    - rename params for grant_type=external_service
 
0.08 2014-06-03T07:54:41Z
    - add support for new grant types
        - "urn:ietf:params:oauth:grant-type:federated-assertion" : obtain access_token from external service assertion

GrantTypeにソーシャルログイン用のものを作成して、例えばGoogleなどから受け取ったAT/RT/AuthzCode/IDTokenなどを送ってログインさせようというものです。 オレオレ拡張仕様のメモ:

social_login_bearer_profile_for_oauth2.md · GitHub

このように、APIベースでもソーシャルログインなど実装できるものはたくさんありますので、今回紹介したREST APIを使ったアプローチを検討してみてはいかがでしょうか。

ではまた!

f:id:ritou:20200819131651p:plain

Capability URLsをBearer Tokenと捉えた場合のJWT適用の可能性

f:id:ritou:20200812192943p:plain

こんばんは。ritouです。 少し前に、このようなスライドを見かけました。

docs.google.com

今回はこのCapability URLsにJWTを使ってみてはいかがかなというお話をします。

Capability URLs as a Bearer Token

挙げられている特徴や要件として

  • 推測できてはいけない
  • URLとして適切な長さ(ブラウザによって処理できないやつが出てこない)

ぐらいで、"えいち、てぃー、てぃー、ぴー、..." とラジオのDJが番組内で紹介できるぐらいの短さを求められたりしないのであれば、OAuth 2.0のBearer Tokenの要件と同等に捉えることもできそうです。

qiita.com

JWTの適用

JSON Web Token、正確には JSON Web Signature を使うことで

  • 構造化されたデータを文字列にできる : リソースに一意に紐づけられるだけでなく、ちょっとしたパラメータを加えられる
  • 改ざんされていないことを検証できる : 第3者が有効なURLを生成しにくい

という特徴を利用できます。

JWTといえばステートレス信仰が湧き上がって来るわけですが、ここでは一旦おいておきます。 JWTをCapability URLsに利用する際のポイントを整理すると

  • 元々設計されているリソース識別子のエントロピーに影響しないURLが作成可能 : 任意のURLにアクセスできる確率の話は署名の方に依る
  • 同じリソースに対して細かいパラメータを加えてやれば有効期限などのちょっとした制御もできる
  • 無効化などの管理もJWT自体の識別子や全体のハッシュ値などを使えば それなりにできる

となるでしょう。 当然、

  • JWTのPayloadに含まれる情報には気をつける必要がある(見られたくないデータは含まない)
  • Payloadにたくさん入れすぎるとURLが長くなる(CWTワンチャン?)

というあたりには気をつける必要があるわけですが、十分実用に耐えうるのではないかと思います。

まとめ

  • Capability URLs も Bearer Token の要件に近いのではないか
  • JWTを適用することで得られるもの、気をつけるべき点をざっくり整理した

という感じです。 この手の機能の設計を行う際に、JWTの適用も選択肢に加えてみるのもいかがでしょうか?

それにしても私が数年前から罹患しているこの「何でもJWTで解決しようとしてしまういわゆるeyJ病」なかなか厄介です。 ではまた。

bosyuが実装したメールアドレスでの登録/ログイン機能とは!?

f:id:ritou:20200727191917p:plain

こんばんは、ritouです。

今日はこの機能を使ってみましょう。

何の話?

medium が前から採用していた方式です。

medium.com

これを新しい方式と捉えるかどうか、個人的にはそれほど新しくは感じません。

メアドでリカバリー手段を確保しつつパスワードを設定させるのが現状一般的な「パスワード認証を用いた登録フロー」だとしたらそこからパスワード設定を抜いたものがメアドによる新規登録フローと言えるでしょう。 また、パスワードを忘れた際にメアドでリカバリーする処理を認証フローとして使うことで「パスワード認証を用いた認証フロー」からパスワードの検証を取り除いたものと考えることができます。いつだったかのbuildersconでもそんな話をしたことがある気がしますね。

f:id:ritou:20200727190404p:plain

当然、この方式ではメールを受信できる環境が第3者の管理下に置かれた場合などが問題となるわけですが、それは既存のパスワード認証でもだいたい同じことが言えるでしょう。 一時的に利用できない場合もあるかもしれません。その場合は、SMSだったり他の認証方式と組み合わせる必要が出てくるでしょう。 この辺りを頭に入れつつ、見ていきましょう。

新規登録 from PC画面

PCから挙動を見てみましょう。メアドを入れます。

f:id:ritou:20200727183359p:plain

新規登録用のリンクを送りましたと出ます。 いや、出ませんね。ログイン用のリンクって書いてあります。 テンプレート間違えてるのでは疑惑がありますがまぁいいでしょう。

f:id:ritou:20200727183429j:plain

送られたメールにはボタンとリンクで新規登録を続けるためのURLがついています。 HTMLメールの扱いなどもあるかもしれませんが、メールを開く環境とbosyuを利用する環境が別だった場合はコピペしてメアドを入力した環境で処理を続けることができます。

f:id:ritou:20200727183448j:plain

アクセスすると新規登録を続けるための画面になります。

f:id:ritou:20200727183501p:plain

新規登録はこれで完了です。

新規登録 from Modile画面

次にモバイルのブラウザで開いた場合の挙動を確認しましょう。

f:id:ritou:20200727184435j:plain

メアドを入れた後の挙動が変わっています。 新規登録コードを送ったと表示され、入力フォームがあります。

f:id:ritou:20200727184449j:plain

これはメールを確認した後に再びモバイルの画面で処理を続けるために、コード入力という方式を選択しているということでしょう。 当然ながら送られてくるメールの内容もPCからの場合とは異なります。 コードを表示しつつ、リンクをコピペすることでもURLにアクセスできます。

f:id:ritou:20200727185039j:plain

mediumでも同様にPC/Mobileのブラウザ(もしくはアプリかどうかも?)を判別しているようですが、現状ではこのやり方がスタンダードと言えるでしょう。

ログイン

スクショ撮りまくりましたが、だいたい一緒っぽいので省略します。

登録状態の返答

パスワードを利用しないにせよ、メールアドレスを入力する機能となると、登録済みかどうかをどう扱うかが気になるところです。

qiita.com

bosyuの場合は、エラーを表示した後にメアドをフォームから消してくれる仕様となっております。

f:id:ritou:20200727162011p:plain

f:id:ritou:20200727161813p:plain

攻撃者視点で言うと、仮にメアド(とパスワード)のリストがあるときに登録済みかどうかを確認することで、生きているメールアドレスを抽出できるとも言えます。bosyuではパスワードを預かっていないと言うことで直接それが別の攻撃に繋がることはないため、ユーザーフレンドリーに結果をお伝えしていると言うところでしょうか。

この辺りはバランスが難しいですね。個人的には上記Qiitaの記事に書いたとおり画面ではエラーを返さない仕組みにしたいところです。

まとめ

bosyuが実装したメアドで登録/ログインの機能を確認しました。 もっといろいろな機能を持つサービスであれば「メールが急に届かなくなった」場合のリカバリーをもう少し考える必要が出てきて悩ましいところですが、比較的ライトなサービスであればこのぐらいの実装で問題ないと思います。

たくさん使われると良いですね。 ではまた!

(追記 : 前からこの辺の話はQiitaに書いてたので参考までに貼っておきます)

qiita.com

qiita.com

ユーザー名にURLやドメインを含むSNSのアカウントとメール通知による拡散行為について

こんばんは。ritouです。

f:id:ritou:20200614004856p:plain

なんの話?

ちょっと前にQiitaでこんなことがありました。

qiita.com

最近はこんなのも見かけました。

ということで、表題の通りSNSのユーザー名にURLやドメインを含み、通知用のメールが送られることで可能となる拡散行為について取り上げます。

拡散行為のターゲットは?

既存ユーザーをターゲットにした拡散行為

Qiitaの例では

  1. 悪意のある者は拡散させたいURL=ドメインを含む名前のアカウントを用意する
  2. 悪意のある者は既存ユーザーを次々とフォローしていく
  3. フォローされた人は、「〜さんからフォローされました」というような文言がメールで送られる
  4. メーラーによってドメイン部分がリンクとなりそこから誘導可能な状態となる

という流れのように見えます。

フォローやいいねを繰り返すことで、ドメインを拡散できる可能性があります。既にたくさんのユーザーを抱えるサービスなどでは注意が必要でしょう。

未登録ユーザーをターゲットにした拡散行為

後者のサービスの例では

  1. 悪意のある者は新規登録時にメールアドレスとURLを含む名前を入力する
  2. メールアドレス向けに「こんにちは ~ さん」のように名前を含むメールが送られる
  3. メールアドレスを持っている人のメーラーによってURL部分がリンクとなりそこから誘導可能な状態となる

新規サービスだからと言って安心してはいけません。 ターゲットのメアドリストを突っ込んでいくことで、URLやドメインを拡散できる可能性があります。

対策

いくつかあるでしょう。

メールで送られる可能性のある名前にURLやドメインが含まれないようにする

現実的に、こんにちは~さん、というメールはよく送られています。 登録時のユーザー名のバリデーションを厳密にやろうと思うとなかなか困難そうです。

URLやドメインを含む可能性がある"名前"を含むメールを送らない

新規登録時には、先にメールアドレスの確認を行うことで、登録完了してから名前入りのメールが行われるようになるなど、頻度は減らせるかもしれません。

URLやドメインがリンクとして扱われないようなメールにする

これも簡単ならやるべきかもしれませんが各メーラーの挙動はどうなんでしょう。

まとめ

URLの拡散行為への対策についてもう少し考えてみても良さそう(雑)

ではまた。


6/17追記

自作サービスがDDoS攻撃された話 - 週休7日で働きたい

「リンクを拡散できる」ことと「たくさんアカウントが作られたこと」のそれぞれの事象について

  • URLを弾く
  • reCAPTCHA や js などで大量アクセスを防ぐ

という考察が行われていますが、それぞれについてより深い考察が行われることが他のサービスで似たような被害が行われることを防ぐことに繋がるでしょう。

2020年版 チーム内勉強会資料その1 : JSON Web Token

おはようございます。ritou です。

5月下旬ぐらいにチーム内勉強会としてJSON Web Token(JWT)についてわいわいやりました。 その際に作成した資料に簡単な説明を添えつつ紹介します。 このブログではJWTについて色々と記事を書いてきましたが、その範囲を超えるものではありません。

ちょっとだけ長いですが、ちょっとだけです。お付き合いください。それでは始めましょう。

JSON Web Token boot camp 2020

JWTBootCamp2020 001

JWTBootCamp2020 002

今回の勉強会では、JWTについて概要、仕様紹介という基本的なところから、業務で使っていくにあたって気をつけるべき点といったあたりまでカバーできると良いなと思っています。

JSON Web Token 概要

JWTBootCamp2020 003

まずは概要から紹介していきます。

JWTBootCamp2020 004

JSON Web Tokenの定義とはということで、RFC7519のAbstractの文章を引用します。

JSON Web Token (JWT) is a compact, URL-safe means of representing claims to be transferred between two parties.

JSON Web Token (JWT) は、2者間で転送されるクレームを表現するためのコンパクトで URLSafeな方法です。

補足します。

JWTBootCamp2020 005

JWTはとにかく色々なデータ、例えば構造化されたものからバイナリデータまでを複数のサービス、システム間でやりとりするため、URLセーフな文字列にエンコードする仕組みです。 また、そのエンコード結果の文字列自体をJWTと呼ぶこともあります。

さらに、JSON Web Signatureという署名をつける仕組みを利用することで改ざん検知が可能になります。JSON Web Encryptionという暗号化を施すことにより、センシティブなデータのやりとりが可能になります。 この2つのうち、よく使われているのを見かけるのがJSON Web Signatureの方でしょう。 ということでここからはJSON Web Signatureについての解説を行います。 (ここからの文中でJWTと書いているものは署名つきのJWSを含みます。)

JWTBootCamp2020 006

そもそも今回、なぜJWTを取り上げたのかにも触れておきます。 誕生のきっかけということで、JWTはOpenIDファウンデーションのWGで行われたID連携やソーシャルログインを実現するための仕組みであるOpenID Connectの仕様策定に合わせ、IETFのJOSE(読みはホゼ) WGにて仕様策定が開始されました。

GoogleAppleでサインインの裏側で動いているOpenID Connectの中で、ID Tokenというユーザーの属性情報や認証時の情報などをやり取りする際にJWTが利用されています。 もともとSAMLというXMLベースのID連携の仕組みで使われていたXML署名という仕組みがあらゆるユースケースに対応できるようにした結果複雑になってしまったという経緯から、JWTはより容易に実装できてコンパクトに表現できるセキュリティトークンを目指しました。 私はこのOpenID Connectの仕様策定にContributorとして参加していたため、この後説明しますが2015年に複数のRFCになるまでの経緯を横目でチラ見していて最近のJWT単独の普及についても注目しております。 ということで今回、ネタとして取り上げました。

JWTBootCamp2020 007

ここからはJWTのユースケースをいくつか見ていきましょう。 サービス、システム間のやりとりに使うという点を意識して、まずはJWTの発行者、そして受信者(利用者)という観点で同一か別かで整理していきます。

JWTBootCamp2020 008

発行者、受信者が同一のユースケースとして、WebアプリケーションにおけるセッションCookieへのJWT適用があります。 (厳密にはmonorithなサービスであれば発行者、受信者が同一となりやすいしょうけれども、規模が大きくなれば発行者、受信者が別になるケースもあり得るでしょう。)

以前からHTTP Cookieにセッション情報を詰め込むという、Cookie Storeとか呼ばれてきたものがあり、以前から様々なWebApplicationFrameworkにてオレオレ署名つきエンコーディングなどで実装されていることはご存知の方が多いかもしれません。これにJWTを利用することができるでしょうというお話です。 さらに、セッションIDをHTTP Cookieに持ってDBなりでセッション情報を管理する実装についてもJWTを適用可能です。セッションIDと発行者、発行日時、有効期限などを合わせて署名つきのJWTに含むことで、ブラウザ上で手元にあるHTTP Cookieの内容をいじって挙動を調べてみるようなことを困難にできます。 処理の流れとしては、Webサーバーがセッション情報に更新があるたびにHTTP Responseとして発行されたものをブラウザがハンドリングし、次のHTTPRequestにてWebサーバーに送ります。

JWTBootCamp2020 009

さらに、WebアプリケーションのHTML Formを用いたPOSTリクエストなどで利用されるCSRF対策トークンにもJWTを適用できます。 この例の方が発行者、受信者が同一である感じが出そうですね。

CSRF対策トークンで重要なのはセッションとの紐付けです。セッションIDそのものではなく鍵を用いて生成したハッシュ値などを格納しておくのが良いでしょう。

JWTBootCamp2020 010

発行者、受信者が別であるユースケースとしては、WebAPIを利用する際の認可用トークンがあります。OAuthのアクセストークンで、発行者であるユーザー認証やアクセス管理を司るサーバー、受信者であるサービスを提供するサーバーが分かれている場合を想像してください。

JWTBootCamp2020 011

よりシンプルなユースケースとして、

  • 署名つきのAPIリクエストにJWTを適用
  • 外部サービスにユーザー情報などを渡す際に署名つきJWTを利用

というものがあります。

JWTBootCamp2020 012

ここまでゆるゆるとユースケースを紹介してきましたが、一旦署名つきのJWTを利用するメリット、デメリットを整理します。 柔軟なデータを文字列にしてやりとり可能なところ、署名とメタデータを組み合わせることで、発行者、受信者、有効期限の検証が可能となる点はとても魅力的だと思います。 一方で、暗号化ではないため、どんな値を含むのかを誰でも知ることができるという点、含まれる情報によるデータサイズの増加には気をつける必要があります。

JWTBootCamp2020 013

JWTが使われている理由として、やはり標準化の強みがあるでしょう。

仕様はRFCになり、ライブラリも各種言語で出揃っています。 OAuth/OpenID Connectをはじめとした標準化プロトコルでの採用実績もありますし、ここまで紹介したように単体で使われるユースケースも増えています。これまで独自の署名つきエンコードを使っていたところからの移行などで利用するケースもあるでしょう。

さらに、最近RFC化されたJWT BCP(Best Current Practice)のように、良くも悪くも枯れてきたと言うか、ここ抑えると安全な実装を実現できると言うのが明確になっている点も様々なところで使われている理由だと思います。

JWTBootCamp2020 014

JWTのユースケースでセッションCookieAPIアクセス時のトークンの例を紹介しました。 このあたりで混乱を呼んでいるものの一つに、Single Page Application のログイン状態の実現方法があります。

この文脈では JWT はエンコードフォーマットだけではなく WebStorage にJWT形式のトークンを保存してAPIアクセスする方式 というような解釈がされがちです。 そのような解釈のまま、HTTP CookieにセッションIDを保存してHTTP Request時に送られるやり方 と比較されたりするんですが、比較の軸が増えてしまって話がまとまりません。

  • セッションIDだけなのか色々詰め込む内包型とするのか
  • 文字列自体に情報を詰め込むのか、ブラウザである程度編集可能なHTTP Cookieの属性値を使うのか

のような比較軸の細かい整理をすることが重要です。

ritou.hatenablog.com

JWTBootCamp2020 015

そして、このCookieとの比較でもよく言及される点として、「JWT=ステートレスでなければならない」「JWTを使う場合は必要な情報を詰め込み、その内容だけで機能を実現させる必要がある」という一種の信仰のようなものがあるのではないかと考えています。

これはとても勿体無いなと感じていて、確かに情報を内包できるという特性は持っていますが、データストアなどを参照するためのキーを持ってはいけないというわけではありません。

例えば単純な識別子を用いたやりとりだったものにJWTを適用するだけで、有効期限や検証という機能を追加できる のです。この考えは既に色々なユースケースで使われているものではありますが、ステートフルなユースケースへも適用できるということを今一度意識してもらえると良いのかなと思っています。

ritou.hatenablog.com

ritou.hatenablog.com

JWTの概要は以上です。

仕様解説

JWTBootCamp2020 016

ここからは、JWTの仕様について簡単に説明していきます。

JWTBootCamp2020 017

JWTに関するコアな仕様が定義されているRFCは5個あります。 その内訳についてですが、

  • 「いつ、誰が、誰に」発行したものか、というようなサービス/システム間のやりとりで必要なメタデータを標準化したもの : RFC7519 JSON Web Token
  • 署名をつけ、検証するために必要なパラメータや実装方法 : RFC7515 JSON Web Signature
  • (今回は紹介しませんが)暗号化周り : RFC7516 JSON Web Encryption
  • 暗号化や署名利用時の鍵の種類、表現 : RFC7517 JSON Web Key
  • アルゴリズム : RFC7518 JSON Web Algorithms

となっています。全てを細かく読んでいくのは時間がかかって大変なので、よく使われている署名つきJWT、JSON Web Signatureについて要点をかいつまんで紹介します。

RFC7515 JSON Web Signature

JWTBootCamp2020 018

JWTBootCamp2020 019

この文字列をご覧ください。皆さんには普通の文字列に見えるかもしれませんが、私にはこう見えます。

JWTBootCamp2020 020

先頭が "eyJ" から始まり、"." で連結されています。

JWTBootCamp2020 021

この文字列の正体としては、RFC7515で定義されている "JWS Compact Serialization" という単一の署名を持つフォーマットであり、世の中で広く使われているのはこのフォーマットです。

仕様ではより複雑なこともできるようになっており、複数の受信者に向けてそれぞれ向けの署名を含むことができる "JWS JSON Serialization" というのも定義されているのですが、使われているのをほとんど見たことがありません。

JWTBootCamp2020 022

改めて先ほどの文字列に戻ります。 最初の eyJ から始まる文字列ですが、Encoded Headerと呼ばれています。

JWTBootCamp2020 023

これはBase64URL EncodeしたJWS Header であり、JWS Header は JSON 形式で表現されます。このJSONの開始タグのあたりで {" から始まる文字列を Base64URL Encodeした時に "eyJ" となるわけです。 内容は自身がどのような種類なのか、署名に関するパラメータを含みます。

JWTBootCamp2020 024

次にPayloadです。2番目の文字列が Encoded Payloadと呼ばれています。

JWTBootCamp2020 025

Base64URL EncodeされたJWS Payloadです。 JWS Headerとは異なり、JWS Payloadはやりとりしたいデータそのものであり、必ずしもJSON形式である必要はありませんが、「誰が」「いつ」「誰に」というような標準的なクレーム(パラメータ)の値が RFC7519 で定義されています。 これと署名検証の仕組みを組み合わせることで「改ざんされていないことが保証できる」文字列となるため、ユースケースはグッと広がります。

JWTBootCamp2020 026

最後にSignatureです。これもEncoded Signatureと呼ばれています。

JWTBootCamp2020 027

Header, Payloadと同様にBase64URL EncodeしたJWS Signatureであり、署名のアルゴリズムはRFC7518、鍵の表現がRFC7517にて定義されています。

JSON Web Signatureの特徴として、署名生成時に利用するBase Stringに対してパラメータのソートなどの正規化が不要であるという点があります。 Twitterなどで使われている OAuth 1.0 という仕組みでは署名つきのリクエストが定義されているのですが、Base Stringを生成するまでの正規化の処理はなかなか面倒です。 JSON Web SignatureではEncoded Header と Encoded Payloadを "." で連結させた値をBaseStringとして利用します。

RFC7519 JSON Web Token

JWTBootCamp2020 028

RFC7519 JSON Web Tokenについて解説します。

JWTBootCamp2020 029

Payloadに含まれるキーバリューの値をJWTクレームと呼びます。

  • JWT自身の識別子である jti
  • host名やサービス内の識別子など、JWT発行者の識別子である iss
  • JWTの受信者、利用者の識別子である aud

という「誰が誰に...」という値が定義されています。

JWTBootCamp2020 030

さらに、「いつ発行され、いつからいつまで有効なのか」を表現するための

  • 発行日時である iat
  • 有効期限 exp
  • この日時以降、有効となる開始日時である nbf

という値が定義されています。

ここまで紹介した値は「汎用的なクレームとして、識別が必要ならば使うと便利だよ。」という意味合いでこのRFC内では基本的にオプショナルです。 JWTを利用するプロトコルや各種プロファイルで「この値を必須として含む」などの定義がされます。

JWTBootCamp2020 031

最初にJWT誕生のきっかけとなったと紹介した「Googleでログイン」を実現するためのプロトコルである「OpenID Connect」でユーザー情報などを伝達するために使われるID TokenはJWTクレームを利用します。

  • issGoogleのアカウント周りを管理するドメイン
  • audGoogleアカウントを受け入れるサービスに client_id として割り当てられた識別子
  • subGoogleアカウントの識別子
  • iss, exp で有効期限を表現

という値がOpenID Connect独自で定義された値と一緒に含まれています。

JWTBootCamp2020 032

ここで紹介した値は全て利用必須なものではなく、これらを利用するプロトコルなどにより決まります。 JWTを扱うライブラリによっては、ここで定義されている値の検証を行うものもあります。 検証の粒度や利用方法については利用するライブラリのドキュメントや実装を追う必要があることに注意は必要でしょう。

RFC7518 JSON Web Algorithms

JWTBootCamp2020 033

RFC7518 JSON Web Algorithmsについて解説します。 ここではJSON Web Signatureの署名生成に利用されるアルゴリズムについて取り上げます。

JWTBootCamp2020 034

一番上にある none については、JWT自体とは別のところで署名生成/検証や暗号化が行われるようなケースで利用します。これではBase64URL Encodeぐらいしかしてないことはここまでの説明でお分りいただけるかと思います。

HSXXX となっているものは、SHA-256, 384, 512といったハッシュ関数を秘密の共有鍵と組み合わせて署名を生成し、同じ計算をして比較することで検証します。 鍵配送が不要な、JWTの発行者、検証者が同一の場合に使いやすいアルゴリズムです。

JWTBootCamp2020 035

RSXXX, PSXXXRSA公開鍵暗号方式ハッシュ関数を組み合わせたデジタル署名を利用するアルゴリズムです。 秘密鍵を用いて署名を生成、公開鍵を用いて署名の検証ができるため、発行者から受信者で公開鍵を渡したり、発行者が公開鍵を提示することで発行者/受信者が別のケースで利用できます。

JWTが使われ始めた頃は、「公開鍵暗号だったら RS256」 という感じで使われていましたが、最近はその状況も変わりつつあります。

JWTBootCamp2020 036

ESXXX公開鍵暗号として楕円曲線暗号を用います。RSAよりも鍵長の短さや処理速度の面で優れているため、最近のプロトコルではこちらを選択するものも見受けられます。

JWTBootCamp2020 037

アルゴリズムと鍵管理の関係は密です。 秘密鍵を公開して後悔しないように気をつけて利用しましょう。

RFC7517 JSON Web Key

JWTBootCamp2020 038

RFC7517 JSON Web Keyについて解説します。

JWTBootCamp2020 039

鍵に関する仕様として、鍵の表現と鍵のセットの表現が定義されています。 アルゴリズムに対応した鍵はどのように表現されるのか、そして鍵のローテーションやアルゴリズム変更時のスムーズな移行を考慮する際に鍵のセットをどう表現するかが重要となります。

JWTBootCamp2020 040

鍵表現のためのパラメータがいくつか定義されています。 このうち重要なのは、鍵の種類である kty, 対応するアルゴリズムである alg, 鍵の識別子である kid です。 他には鍵の利用用途(署名 or 暗号化)である use やさらにその鍵の使い方まで踏み込んだ key_ops があったり、X.509形式の証明書に関連するパラメータがあります。

JWTBootCamp2020 041

HSXXX で利用されるバイナリの秘密鍵kty=octとして表現されます。

JWTBootCamp2020 042

RSXXX で利用されるRSA秘密鍵kty=RSAとして表現されます。長いです。

JWTBootCamp2020 043

公開鍵は少しすっきりと表現されます。alg, kid の値も含まれていますね。

JWTBootCamp2020 044

ESXXX で利用されるECDSAの公開鍵はkty=ECとして表現されます。

JWTBootCamp2020 045

鍵のセットは keys として鍵のリストを持つことで表現されます。 これは先ほども紹介したOpenID ConnectでGoogleから受け取ったJWTの署名検証を行うために公開されている、公開鍵のセットの値です。Appleなども同様に公開しています。

JWTBootCamp2020 046

これらのユースケースとして、有効な鍵情報をjwks_urlとして公開したり、設定ファイルにこの形式で保存するといったものがあります。 弊チームでは設定ファイルなどでPEM形式などを利用していますが、コンパクトに表現できるのであればこのような形式での保存も使っていきたいと思っています。

実装のポイント

JWTBootCamp2020 047

仕様の解説は以上にして、実装のポイントを紹介します。

JWTBootCamp2020 048

ここまで紹介した仕様がRFCになってから5年ぐらいが経ち、良くも悪くも枯れた技術となってきました。 そして最近、RFC8725 JSON Web Token Best Current PracticesとしてJWT実装のベストプラクティスがRFCとなりました。

Qiitaで解説記事を書きましたのでお時間がある方はみてください。

RFC 8725 JSON Web Token Best Current Practices をざっくり解説する - Qiita

JWTBootCamp2020 049

個人ブログにも少し書いたのですが...

JWTBootCamp2020 050

JWTを安全に使っていくためのポイントとして、難しい暗号化処理以外のものとして

  • Payloadに含む情報はよく考えて決める
  • 署名検証処理を確実に
  • 複数のJWTを利用する際は、用途を指定/判別して排他的に検証する

というものが書いてあります。

JWTBootCamp2020 051

Payloadに含む情報について、チーム内の勉強会ではせっかくJWTを使っていてもPayloadに含む情報が足りず、そもそもの目的を満たせていない例を紹介しました(が割愛します)。 これはユースケースに依存するというか、必要な情報が決まるものなのでよく考えましょう。

署名検証については「ちゃんとした」ライブラリを使うことで問題は回避できますが、簡単にこんな攻撃があったよというのを紹介します。

個人的には、最後の用途のあたりが一番重要かと思っているのでスライドのページを割いて紹介します。

JWTBootCamp2020 052

JWTBootCamp2020 053

JWTの署名検証時の脆弱性として有名なのが、alg とSignatureの値を改ざんして検証をパスさせようとするものです。 JWT側の alg に沿って署名検証をしてしまうと、alg=noneを指定してSignature部分を取り去って送られたものを受け入れてしまったり、alg=RS256からHS256に変えられて公開鍵を共有秘密鍵としてハッシュ値をSignatureに指定されたりしたものを受け入れてしまう例が報告されています。

これらを回避するためには、JWTに含まれるalgをそのまま署名検証に使うのではなく、kidに紐付く鍵に紐付くalgを署名検証に利用することが重要です。 その両者が異なる場合はJWTの改ざんが考えられますが、これは署名検証で検知できます。

JWTBootCamp2020 054

ここからはJWTの用途と署名検証について説明します。

ポイントは

  • 用途の表現と指定
  • 鍵の管理
  • 検証

です。

JWTBootCamp2020 055

JWTで用途を表現するために使えるパラメータはいくつかあります。

まずはHeaderのtypです。 例として、Googleが連携済みのサービスにユーザーのセキュリティイベントをJWT形式で通知する仕組みがあって、そこでは secevent+jwt という値を指定しています。 次に、Headerのkidで用途ごとに鍵を分けるという運用もあります。 他には、Payloadにuseusageのような値を含んで用途を表現するというやり方があります。

ritou.hatenablog.com

JWTBootCamp2020 056

これらのどれを使うかについては、利用するサービスの設計やライブラリの状況から柔軟に判断すべきです。

JWTBootCamp2020 057

例えば機能単位で完全に鍵をわけられるような場合はkidに用途を含むことで排他的に検証が可能でしょう。

JWTBootCamp2020 058

鍵の管理には手を出せないがHeaderを自由に指定できる場合はtypの値を利用できるでしょう。ちなみに、ライブラリでこの検証をするものもあります。

JWTBootCamp2020 059

Payloadしか指定できない場合はPayloadに独自の値を指定する必要があるでしょう。 ライブラリでもサポートされていない部分なので、実装漏れがないように気をつける必要があります。

最後に紹介しますが、JWTの署名検証では Header -> Signature -> Payload という順番に扱っていくため、署名検証の処理回数が多い場合はHeaderの参照だけで判断できる方がPayloadの値を参照するよりも負荷がちょっとだけ抑えられるのではと思います。

JWT生成/検証デモ

JWTBootCamp2020 060

最後にJWTの簡単な生成と検証のデモをやってみます。 (予想通り時間もなかったのでざっと流しました。)

JWTBootCamp2020 061

目的としては、簡単なやつでもこれまで紹介した仕様について手を動かすことで理解が深まるかなと言うところです。 業務では、ライブラリを使いましょう。Elixirでは KittenBlue っていうのが便利ですよきっと。

JWTBootCamp2020 062

ここではJWT用のライブラリを使わず、これらの3つの機能を使ってやってみます。 Base64URL Encode/Decode は padding のオプションがあったりするので注意が必要です。

生成

JWTBootCamp2020 063

生成の流れを説明します。 一言で言うと、Header, Payload の値を生成した後、Signature の値を生成して連結します。

1つずつ見ていきましょう。最初はHeaderです。

JWTBootCamp2020 064

ここでJWTに含むHeaderパラメータは3つです。

  • typ ではハンズオンっぽい文字列を用意して、用途を判別できるようにします
  • alg 署名アルゴリズムにはHMAC SHA-256を使います
  • kid 鍵管理を意識しましょうと言ったはずだ

JWTBootCamp2020 065

まずは JSON Encode します。 それを Base64URL Encode すると Encoded Header が出来上がりです。

JWTBootCamp2020 066

次にPayloadです。 ここでは送りたいデータとしてこんな感じのを用意します。 せっかく紹介したんだからRFC7519で定義されている iss とか aud とか exp の値を使えばよかったなと後から思いましたがとりあえずこれでヨシ!

JWTBootCamp2020 067

Headerと同様に、JSON Encode と Base64URL Encode したら Encoded Payload の出来上がりです。 次はSignatureを生成します。

JWTBootCamp2020 068

Signature生成のもとになる文字列は、Encoded HeaderとEncoded Payloadを "." で連結させたものです。正規化不要!簡単ですね。

JWTBootCamp2020 069

今回は鍵として "THIS_IS_SAMPLE_KEY_FOR_JWT_HANDSON" とかを用意して、それで HMAC-SHA256 した値をBase64URL EncodeするとEncoded Signatureの出来上がりです。

JWTBootCamp2020 070

最後に連結させます。 Elixirだとこんな感じです。

iex(1)> encoded_header = %{"alg"=>"HS256", "kid"=>"handson01", "typ"=>"handson+JWT"} |> Jason.encode!() |> Base.url_encode64(padding: false)
"eyJhbGciOiJIUzI1NiIsImtpZCI6ImhhbmRzb24wMSIsInR5cCI6ImhhbmRzb24rSldUIn0"

iex(2)> encoded_payload = %{"Foo"=>"Bar", "Hoge"=>"Fuga"} |> Jason.encode!() |> Base.url_encode64(padding: false)
"eyJGb28iOiJCYXIiLCJIb2dlIjoiRnVnYSJ9"

iex(3)> signature_base_string = encoded_header <> "." <> encoded_payload
"eyJhbGciOiJIUzI1NiIsImtpZCI6ImhhbmRzb24wMSIsInR5cCI6ImhhbmRzb24rSldUIn0.eyJGb28iOiJCYXIiLCJIb2dlIjoiRnVnYSJ9"

iex(4)> encoded_signature = :crypto.hmac(:sha256, "THIS_IS_SAMPLE_KEY_FOR_JWT_HANDSON", signature_base_string) |> Base.url_encode64(padding: false)
"Tp0zcg2nEA1r94EijoymQTTVMwH6iaLoOpxEZf3KcVM"

iex(5)> jwt = encoded_header <> "." <> encoded_signature <> "." <> encoded_signature
"eyJhbGciOiJIUzI1NiIsImtpZCI6ImhhbmRzb24wMSIsInR5cCI6ImhhbmRzb24rSldUIn0.Tp0zcg2nEA1r94EijoymQTTVMwH6iaLoOpxEZf3KcVM.Tp0zcg2nEA1r94EijoymQTTVMwH6iaLoOpxEZf3KcVM"

公開鍵暗号系のalgを利用する場合は、Headerのパラメータで指定してSignature計算のところで適切な署名生成関数を利用します。

生成については割と簡単なので、Qiitaなどで生成を自前でやるのを見かけますが、複数箇所でハードに使っていくとなると共通処理がまとめられ、結果ライブラリを使うのと変わらなくなると思います。

検証

生成ができたので検証もやってみましょう。

JWTBootCamp2020 071

検証の場合、順番が変わります。

Signatureの検証のために先にHeaderの値を参照する必要があり、Payloadの内容を参照するのはSignature検証の後となります。

JWTBootCamp2020 072

生成と逆の手順で、Base64URL DecodeしてJSON Decodeして Header パラメータを取得します。ここで含まれる値であれば、

  1. typ パラメータの値で用途の検証
  2. kid の値が有効なものかどうか
  3. alg の値が kid に紐づいた値と一致しているかどうか

と言う検証を行います。

先ほど述べたように、生成時に alg の値を含まず、省略して3もスキップしても良いでしょう。

JWTBootCamp2020 073

次にSignatureの検証をします。 Base StringはEncoded Header と Encoded Payload を "." で連結したものです。

JWTBootCamp2020 074

kid に紐づく鍵で生成時と同様に値を計算し、Base64URL Encodeした値と Encoded Signature の値を比較します。

JWTBootCamp2020 075

Signatureの検証を終えたら、Payloadの値を参照します。 ここからはJWTを利用するサービスごとによしなにと言うところです。

RFC7519で定義されている iss, aud, exp などのクレームを含む場合はここで検証します。

検証の手順は以上です。

外部サービス

jwt.io というJWTの生成/検証を行う外部サービスがあり、JWTのデバッガがついています。

jwt.io

ここまでで生成したJWTを入れて鍵情報も入れてやると、署名検証を行い成功したことがわかります。

JWTBootCamp2020 076

公開鍵暗号を使うalg=RS256も紹介したかったですが時間がなさそうなのでやめました。 興味がある方は試してみてください。質問などありましたらいつでもどうぞ。

JWTのデバッガは、他にもmicrosoft製のjwt.msなどがあります。 jwt.ms: Welcome!

外部サービスなので、本番環境で単体でAPIアクセスに使えるようなJWTを入力したり、業務で扱う秘密鍵を指定しての署名検証などは避けましょう。

まとめ

  • JSON Web Tokenの概要を紹介しました
  • 様々なサービスで使われているJSON Web Signatureと関連する仕様を紹介しました
  • 実装で気をつけるべきポイントを紹介しました
  • ハンズオン的にJWTの生成/検証を行う方法を紹介しました

初学者向けにまとめたつもりでしたが、もっとユースケース毎にこんな情報をこんな感じで含むといいよねみたいな話や実装についてももうちょっと紹介できると良かったかなとちょっと反省しています。


と、こんな感じでした。 今後もユーザー認証とかリソースアクセスとか、おじさんの守備範囲でその3ぐらいまではやってみたいと思っています。

speakerdeck.com

ではまた!

「ID TokenをAuthorization Headerにつけて送る」というお作法について思うところ

こんばんは、ritouです。

f:id:ritou:20200517193756p:plain

ID Tokenがやりとりされている背景

ちょっと前にこんな話がありました。

blog.ssrf.in

この id_token が JWT になっていますので、これを Authorization: Bearer $ID_TOKEN というヘッダにして oauth2-proxy で保護されているアプリケーションへ送信するだけです。

docs.aws.amazon.com

Authorization ヘッダー (または、オーソライザー作成時に指定した別のヘッダー) に ID トークンを含めます。

この「ID TokenをAuthorization Headerに指定して保護されているっぽいリソースにアクセスする行為」は一体何なのかというお話です。

ある有識者はOAuth 2.0のProtected ResourceをID Tokenで保護することについての投稿をしました。

medium.com

いわゆる3legged(ユーザー、AuthZ/Resource Server, Client)でこういう実装をすると上記リスクがあるのは当然でしょうが、世の中にはセッショントークンとしてID Tokenと名乗る物を使うユースケースが存在します。

ritou.hatenablog.com

IDaaSのような認証基盤を利用するとき、

  • (セッショントークンである)ID TokenをID Token発行元のサービスに送り、何らかの操作を行う

という処理が発生するのだろうと考えています。

OSSなものでそのような実装が行われる際には「これってリソースアクセスでは?ID TokenじゃなくAccess Tokenでやるべきでは?」なんて意見が出るようですが、「いや、こっちはリソースサーバーじゃねーし(セッショントークン相当のID Token送るでヨシ!)」みたいな感じになってるようでした。

カオスなままで良いのか?そもそもセッショントークンの扱いとは?

上記のような現状について「リソースサーバーじゃないからそうか。仕様で定義されていない物ならしょうがないか」となっている意見も見受けられます。 仕様がないからしょうがない。 特定の小さな領域でのみの作法に止まるならばそれでも良いでしょう。 しかし、この話はIDaaSを使わない、単純な1st PartyなNativeApp/SPAとバックエンドサーバーの実装にも影響するのではないかと気になっています。

例えばですが、

これはどうでしょうか。

1st Party なアプリとバックエンドでやりとりにOAuth 2.0を適用!ってのをやったことがある開発者であれば、その間は Access Token を用いたAPIアクセスとなるでしょう。 しかし、今後上記のようなIDaaSのお作法から入った開発者の場合、ID Tokenを使ってAPIアクセスを行うという可能性もあるのではないでしょうか? 暗黙的にこれらの2つの実装が広く使われ始めると、開発者の混乱を生みそうです。そうでもない?

この辺りで自分の認識はどうかというと、むかーし、こんな記事を書きました。

ritou.hatenablog.com

この時は今よりも舌ったらずなので内容もいまいち何言いたいかわからんし、今では説明に使うべきではないと思っている「認証」「認可」なんていうワードを軽々しく使っているため皆さんの反応もパッとしない感じでしたが、ブラウザとWebサーバーとのやりとりもざっくりいったら特殊なクライアントとリソースサーバーとのやりとりと同じような意味合いであり、セッションクッキー/セッショントークンは「凄まじい権限を持った」アクセストークン相当であろうというお話です。 当時はブラウザはUserAgent何だからClientではない、みたいなご指摘をもらったりもしましたが、SPAの場合はClient相当がちゃんといるのでなおさら意識しやすくなったのではないかと思ったりもします。

現状に対する自分の考えるあるべき姿としては「どんなユースケースであれ、ID TokenじゃなくAccess Tokenでやれ」という考えであり、中身に含まれる情報の話や処理についてなど、これから色々足りない部分を補足していきたいところであります。

JWT+RDBを用いたOAuth 2.0 ハイブリッド型トークンの実装例

おはようございます、ritouです。

(⚠️認可イベントの識別子のあたり、ちょっと見直しました!最初に見ていただいた方はもう一回どうぞ!)

f:id:ritou:20200507101740p:plain

前回、ハイブリッド型と呼ばれる OAuth 2.0 のトークン実装について書きました。

ritou.hatenablog.com

その続きとして JWT(JWS) + RDBでできる実装例を紹介します。 理解するにはそれなりの OAuth 2.0 に関する知識が必要になるかもしれませんが、よかったら参考にしてみてください。

何を考えたのか

OAuth 2.0のRefresh Token, Access Tokenを考えます。 要件から整理しましょう。

要件

結構ありますが、最低限の OAuth 2.0 の Authorization Server を実装しようと思ったらこれぐらいはやらないといけないでしょう。

  • RFC6750 で定義されている Bearer Token 相当
  • Refresh Token
    • grant_type=authorization_code で発行される
    • grant_type=refresh_token で自身が更新される、いわゆるローテートにも対応
  • Access Token
    • grant_type=authorization_code, grant_type=refresh_token で発行される
    • Refresh Token からのダウンスコーピング(scopeを減らして指定)が可能
  • トークンから参照できる情報 : 色々とセンシティブな金融関係のAPIを利用するためのトークンなどではなく、Clientからこれらの内容は見れても良い想定
    • Resource Owner's identifier
    • Authorization Request(client_id, scope etc)
  • 無効化
    • Resource Owner + Client 単位で現在発行中の Refresh/Access Token を無効化可能 : Resource Owner が自ら無効化できる想定
    • 個別の認可イベント単位でも無効化可能 : ここまでやらなくても良さそうではあるけど、一応できるようにしておく。
    • 個別の Access Token 単体での無効化まではしない
  • Resource Server の Access Token ハンドリング
    • Token Introspection API(もしくは独自の仕組み)で検証可能
    • 有効期限前に無効化されていることを許容できるならばResource Server自体で検証可能

このような要件を、DBにテーブルいっぱい作ってやるんじゃなくJWTの特徴を生かしてなんとかできないか、というのがこの記事で紹介する実装の目的です。

データの持ち方

JWT部分は仕様策定中のこのDraftを参考にしてます。

tools.ietf.org

JWT(JWS)

  • Header
    • typ : Access Token は at+jwt, Refresh Token は rt+jwt (独自)
    • alg : RSXXX / ESXXX
    • kid : 指定あり。公開鍵は jwks_uri などで共有
  • Payload
    • iss : Authorization Serverの識別子
    • aud : Refresh Token なら Authorization Server, Access Token なら Resource Server の識別子
    • sub : Resource Owner の識別子
    • client_id : Client ID
    • scope : 認可イベントで処理された scope の値
    • iat : 発行日時
    • exp : 発行日時にRefresh Tokenは長め、Access Tokenは短めの時間を追加した有効期限
    • auth_id : Refresh Token は認可イベント(Resource Owner がリソースアクセスを許可するタイミング)単位でユニークな identifier (UUID/ULID的なの)を auth_id として持つ。Access Tokenは同時に発行された、もしくは更新された Refresh Token の auth_id の値を持つ。
  • アクション
    • RDBの create / update の後にRefresh Token/Access TokenのJWT生成

⚠️jti ではなく auth_id として認可イベントの識別子を持つように変更しました。

RDB

  • カラム
    • id : JWT の auth_id と同じ値(もしくはハッシュ値) プライマリーキー
    • client_id : JWT の client_id と同じ値 : 無効化の時に使う。
    • user_id : JWT の sub と同じ値 : 無効化の時に使う。
    • updated_at : Refresh Token の発行(最終更新)日時。ローテートの時に使う。
  • アクション
    • create : 認可イベントで作成される
    • read : grant_type=refresh_tokenの時や、Token Introspection API(もしくは独自の仕組み)が呼ばれた時、あとはResource Onwer に無効化機能を提供する時に参照
      • grant_type=refresh_token では auth_id, updated_at の組み合わせで検索することでローテートされた最新の Refresh Token のみ扱うことが可能
    • update : RefreshTokenのローテートのタイミングで updated_at を更新
    • delete : Resource Owner 自身による無効化などのタイミングで消す。updated_at の値を見て Refresh Token の有効期限を超えたものは処理の途中や定期的に消す。

ポイントは認可イベントが行われた時にのみ生成されるというあたりです。 Access Token 単位でデータを持つと規模によってはデータ量が増えるわけですが、RDBが持つデータ数を有効なRefreshTokenの数に留めることが可能です。

RDBの中身とJWTはこんな感じになります。

# grant_type=authorization_code

## RDB

* id : 10870e51-e06a-03cb-33c7-eca6631d8e3d
* user_id : "12345"
* client_id : "cid_abcde"
* updated_at : 1588788749

## Refresh Token
eyJhbGciOiJSUzI1NiIsImtpZCI6Im9hdXRodG9rZW4yMDIwMDUiLCJ0eXAiOiJydCtqd3QifQ.eyJhdWQiOiJodHRwczovL2F1dGhvcml6YXRpb24tc2VydmVyLmV4YW1wbGUuY29tLyIsImF1dGhfaWQiOiIxMDg3MGU1MS1lMDZhLTAzY2ItMzNjNy1lY2E2NjMxZDhlM2QiLCJjbGllbnRfaWQiOiJjaWRfYWJjZGUiLCJleHAiOjE1ODg4NzQyNDksImlhdCI6MTU4ODc4Nzg0OSwiaXNzIjoiaHR0cHM6Ly9hdXRob3JpemF0aW9uLXNlcnZlci5leGFtcGxlLmNvbS8iLCJzY29wZSI6Im9wZW5pZCBzYW1wbGUgc2FtcGxlMiIsInN1YiI6IjEyMzQ1In0.Op26WcEUS2qG64KMGT2a4EyLziEhta5gcTGIeE4uu2sw9PC9mxoQo18iBNcaAk34n4OKEH1Qi8lzfb6fWMySEohqWKjv-iZtuBfu0O550z1faCx3jhoNqoSwKInMbOJHR8PtJN5E_GEZTNFwH3i4dEk1Moi8KAX8aJ7OZBR3yFipnbb6AC3Vpy0JuLxZMJjU_EVfO3XOZ2YA9OYlpFerflnPUTpPIE1OV5yQ7ecvnPDpShpVS3A-s7n3iZMp7z6kxvkYIsZc0W5JKxrfN-ivkPHaTeKluxM0eOmXV_Q6hmH7U2S0gf6iJB76UOCsw7VtqsTnANLCRI-rBJAgKTu9vg

## Access Token
eyJhbGciOiJSUzI1NiIsImtpZCI6Im9hdXRodG9rZW4yMDIwMDUiLCJ0eXAiOiJhdCtqd3QifQ.eyJhdWQiOiJodHRwczovL3JzLmV4YW1wbGUuY29tLyIsImF1dGhfaWQiOiIxMDg3MGU1MS1lMDZhLTAzY2ItMzNjNy1lY2E2NjMxZDhlM2QiLCJjbGllbnRfaWQiOiJjaWRfYWJjZGUiLCJleHAiOjE1ODg3ODg3NDksImlhdCI6MTU4ODc4Nzg0OSwiaXNzIjoiaHR0cHM6Ly9hdXRob3JpemF0aW9uLXNlcnZlci5leGFtcGxlLmNvbS8iLCJzY29wZSI6Im9wZW5pZCBzYW1wbGUgc2FtcGxlMiIsInN1YiI6IjEyMzQ1In0.v0J_KCws4qQBNYiuWBnqi1Pq0L34_URj4vLnuXXwegdyF087SsgI0j0BTM0Rx9LjgYn4JTmwRKYkNnC7vTW3xpvteXmvQEUVJpMPIfYulwz_oQKOPuB1Nr259Q8d9Bcu7Tlr5skyVjIqya1O6VYEkI7YHBCD2MVh0V6L0pWcS7JazkSzaaS50MdGqDWD7uafLSjdzI52tQfhCiyhhnfi8glU5byg1s3g5hQrXx7hhNqOfECBteySUaVE43W3SVHv2pRlJ-XPla0fohnHtzFqodhPCJ4BuiTU7nh7bzVCeAVfxUZCNitA8oj0SiM9lHjbXtEkqnIXa4JZsJSYFjBLgg

f:id:ritou:20200507231345p:plain

f:id:ritou:20200507230909p:plain

# grant_type=refresh_token

## RDB

* id : 10870e51-e06a-03cb-33c7-eca6631d8e3d
* user_id : "12345"
* client_id : "cid_abcde"
* updated_at : 1588788750 <- updated!!!

## Refresh Token(rotated)
eyJhbGciOiJSUzI1NiIsImtpZCI6Im9hdXRodG9rZW4yMDIwMDUiLCJ0eXAiOiJydCtqd3QifQ.eyJhdWQiOiJodHRwczovL2F1dGhvcml6YXRpb24tc2VydmVyLmV4YW1wbGUuY29tLyIsImF1dGhfaWQiOiIxMDg3MGU1MS1lMDZhLTAzY2ItMzNjNy1lY2E2NjMxZDhlM2QiLCJjbGllbnRfaWQiOiJjaWRfYWJjZGUiLCJleHAiOjE1ODg4NzQyNTAsImlhdCI6MTU4ODc4Nzg1MCwiaXNzIjoiaHR0cHM6Ly9hdXRob3JpemF0aW9uLXNlcnZlci5leGFtcGxlLmNvbS8iLCJzY29wZSI6Im9wZW5pZCBzYW1wbGUgc2FtcGxlMiIsInN1YiI6IjEyMzQ1In0.STXzXd-1x7mT44PosBAGTF46yZ3ZtrfSDhC-6kNYQYRc0bwpHuNEFlFCQkJiX4GS8pCrygoovuqPjPHL4mp68Rebw5hliUJZ6iWGhPpbiSd7JCSXDrXPiJe0hYevgoksfqeGknHp6yiSH0E6KmjZoO7BpZVJr4gcBCC9eeqP4f_TsY7k3m4U_zpUdeyakvqVNfefn3WPzMymxiP-xvH60mbea7HQJXi_dkOweVECLrNEPGoWwkiHAGXaC6GKHs-7Ime1nJF3j6WDYEIzJDtqFkQS_Y14Ps6EgWyFNQegcBMbhc0-LQnFpxqD4e2VEqQPhUwMsPtQhQZDNA2OUbOiAA

## Access Token(down scoped)
eyJhbGciOiJSUzI1NiIsImtpZCI6Im9hdXRodG9rZW4yMDIwMDUiLCJ0eXAiOiJhdCtqd3QifQ.eyJhdWQiOiJodHRwczovL3JzLmV4YW1wbGUuY29tLyIsImF1dGhfaWQiOiIxMDg3MGU1MS1lMDZhLTAzY2ItMzNjNy1lY2E2NjMxZDhlM2QiLCJjbGllbnRfaWQiOiJjaWRfYWJjZGUiLCJleHAiOjE1ODg3ODg3NTAsImlhdCI6MTU4ODc4Nzg1MCwiaXNzIjoiaHR0cHM6Ly9hdXRob3JpemF0aW9uLXNlcnZlci5leGFtcGxlLmNvbS8iLCJzY29wZSI6Im9wZW5pZCBzYW1wbGUiLCJzdWIiOiIxMjM0NSJ9.YaX5u_Ef174aoC7QP48twCZxBHOxGELXD0k-e8aRR7W8c6IcPlUrWnZqQzQahLGm0Cjm7P6fnH7I4FJITiOfQAlWY3t-xqUYILngATs6TJ-gIhBd2W-e4uHQ7S98cVyCS_jI-gz_QnMHOaALz_RILvX_bNTsgZ1XSvGyRzRl5zQ_fagU-0QCbEyma6HYonJ_ua6i_QHzW4WzJ7haOut-E7i0kqv-7EI4EEaC2bf-c8xEqP51scHB1VikrBZ97_iMGmCUzLJ5qAwGShB3Z2bZvbaaGrnNxuCKvV04ayl-KhE6iyIpqPP7Tz8CXy-NnboaYI8gaYmZ8K4yYMqIzQlDuA

f:id:ritou:20200507233134p:plain

f:id:ritou:20200507233447p:plain

まとめ

OAuth 2.0 の Refresh Token / Access Token を JWT + RDB で実装する例を紹介しました。 ベタに UUID/ULID を各種トークンに使用するのに比べて

  • JWTを利用することでResource Server - AuthZ Sever 間の無駄な通信を抑えられる
  • RDB側は最低限のデータ保持で済む
  • Refresh Tokenのローテートにもデータを増やさずに対応可能

という特徴があります。 誰かのお役に立てれば幸いでございます。

社会復帰頑張りましょう。 ではまた。

OAuthのハイブリッド型アクセストークン実装に関するあれこれ

おはようございます。ritouです。

f:id:ritou:20200503025039p:plain

先日オンラインで開かれたAuthlete社の勉強会で、アクセストークンの実装パターンについて触れられていました。

www.authlete.com (titleはもうちょっとなんとかする方が良さそう)

ドキュメントでいうとこの辺りでしょうか。 qiita.com

  • 識別子型
  • 内包型
  • ハイブリッド型

の3種類があると説明されていました。 この中でハイブリッド型の中にも色々あるよなってところで、ちょっと自分の思うところを書いてみようと思います。

上記ドキュメントのハイブリッド型の説明

説明としては

ハイブリッド型の実装方法では、内包型アクセストークンを生成しつつも、それに付随するデータを認可サーバーのデータベース内に持ちます。

という感じで、クライアントから隠蔽すべき情報の扱いとして、

ハイブリッド型アクセストークンを利用し、秘密にしたい情報をアクセストークンには含めず、サーバー側のデータベース内のみに保存する。

とあります。

今回取り上げる実装パターン

こんな感じの2種類の実装パターンを考えます。

  • 識別子のみを内包
  • イントロスペクト可能な内包型

これらはどちらも上記の動画や資料にあるハイブリッド型の範囲に収まっており、どこまで情報を持つかの違いですね。

識別子のみを内包

最低限の情報しか含まない実装です。 Payloadの例はこんな感じになるでしょう。

   {
     "jti": "abcdefghijklmn01234567890",
     "iss": "https://authorization-server.example.com/",
     "aud": "https://rs.example.com/",
     "exp": 1544645174
   }

特徴としては

  • 識別子型 + 最低限のメタデータ + 署名
  • "sub" とかも含まない。データを引くときは "jti" を使って参照する
  • RSによる検証のために "iss", "aud", "exp" を残している

というところです。

識別子型での実装の場合、RSからトークンイントロスペクションなり独自の仕組みでの問い合わせが毎回発生します。 それはしょうがないんでしょうけれども、"hogehogehogehoge" みたいな文字列とかで問い合わせを行うのはもったいないです。 そして有効期限の切れているアクセストークンを弾きたい時も、タイムスタンプ機能を加えたアクセストークン文字列の設計が必要となるでしょう。 そこで、最低限のメタデータと一緒にJWTにしちゃって、諸々の検証によって無駄な問い合わせを軽減しようってのがこの実装です。

次行きましょう。

イントロスペクト可能な内包型

これは上記資料にある内包型+α的な実装です。

   {
     "jti": "abcdefghijklmn01234567890",
     "iss": "https://authorization-server.example.com/",
     "sub": " 5ba552d67",
     "aud":   "https://rs.example.com/",
     "exp": 1544645174,
     "client_id": "s6BhdRkqt3_",
     "scope": "openid profile reademail"
   }
  • Clientから見えて良い情報を内包しちゃう
  • "jti" を使って生存確認ができる

こちらもトークンイントロスペクションもしくは独自の方法で問い合わせを"行える"前提ですが、必須にする必要はないだろうというお話です。

  • そんなにセンシティブな情報を扱わない RS であれば手元の検証だけでOK
  • センシティブな情報を扱うRSの場合、手元の検証 + 問い合わせまでやる

前者はプロフィール画像などほぼ公開されているデータを返すAPI、後者は決済系のAPIなどですね。 個人的には、APIでこれらが混在しても、そんなに大きな問題は起こらないと思っており、最初から「(識別型、内包型、ハイブリッドの)3種類のうちどれ選ぶ!?!?」となる必要はないんだよと思っています。

まとめ

  • ハイブリッド型と呼ばれる実装の中で、内包されるデータが微妙に異なる2種類に注目した
  • 識別子のみを内包し、検証によって無駄な通信を削減するのはどうだろう
  • 識別子の問い合わせは用意しつつ、必須にしないという実装もありそう
  • eyJ! eyJ!

というお話でした。まぁ、細けぇ話ですね。

ではまた。

おまけ

そういえば、6年ぐらい前から同じようなこと言ってました。

ritou.hatenablog.com

しかし見直すと色々怪しいですね。

f:id:ritou:20200503031212p:plain

この記事の例だとユーザー識別子を "sub" で持っていますが、いわゆる "OAuth as a Authentication" に囚われていたのかもしれません。 あくまでOAuthはリソースアクセスすることが目的なので、発行対象のユーザー識別子を必ずしも含んでClientが(見ようと思えば)見れる必要はない、というのが今の考えです。

そして "aud" の値が Client ID になっています。 OIDCのID TokenはClientが検証するためのものなのでこれでいいんですが、OAuthのAccessTokenだと "aud" はアクセストークンを受け付ける Resource Server の値を含むべきでしょう。

今後もぼくのかんがえるさいきょうのおーおーすとーくんせっけいを考えていきたいと思います。

OIDC CIBAのようなDecoupled AuthZ/AuthNプロトコルでリスクベース判定したくない?

おはようございます。ritouです。

f:id:ritou:20200502011658p:plain

OAuth 2.0 の Device Flow(RFC 8628) や OpenID Connect Client Initiated Backchannel Authentication Flow(いわゆるCIBA)、XYZ/XAuthといった次のOAuth候補みたいなプロトコルでは次のような流れがサポートされています。

  • ClientがIdPに認可リクエストをバックチャンネルで送る
  • IdPがユーザーインタラクション用のURLなどを返して Client がユーザーをそこに誘導したり、専用のアプリに通知を送ったりして認証、リソースアクセス等に同意する

いわゆるベーシックなOIDCの認可コードフローでは "Client が動作する環境" と "IdPとユーザーが対話を行う環境" が同一であることが想定されるわけですが、このような流れにすることで "Client が動作する環境" と "IdPとユーザーが対話を行う環境" が別であるケースもサポートできます。

今回はこれらのプロトコルで IdP がリスクベース認証を入れようと思った時に、必要となるパラメータについてのお話です。

"Client が動作する環境" と "IdPとユーザーが対話を行う環境" が同一の場合

いわゆる認可(認証)リクエストってので

  • WebApp な Client がリダイレクトで IdP にユーザーを送る
  • NativeApp な Client が外部ブラウザを立ち上げて IdP にユーザーを送る

という場合、 IdP が自身のエンドポイントへのアクセスを受けた時点のブラウザやデバイスの情報を収集、分析して追加の認証や再認証を要求したり、認証をスキップさせることができるでしょう。

ブラウザが一瞬開いて...みたいな挙動は古き悪きリワード広告みたいでいけんのか?とは思いますが、とりあえず。

"Client が動作する環境" と "IdPとユーザーが対話を行う環境" が別の場合

それに対して、それぞれの環境が別な場合、プロトコルを単純に実装しただければ簡単ではなさそうです。

オンライン決済にCIBAを適用する例を考えてみましょう。

  1. ブラウザのECサイトになんかのIDを突っ込む
  2. 手元のスマホに「ECサイト名」「金額」とかが通知されて「OK」する
  3. ブラウザも決済完了になって終わり

こんなことができるわけですが、1 と 3 を行うブラウザが Consumption Device (CD), 2 を行うスマホが Authentication Device (AD) となります。 物理的に同じ場所にいても、デバイス的には別です。

例えば、「いつも使ってるブラウザかつお気に入りのECサイトなら2の処理をスキップ」なんてことを実装しようと思うとどうでしょう。 ブラウザ情報をどこかで送る必要があります。それはいつでしょうか? ユーザーインタラクションをコントロールするためには、認可(認証)リクエストをバックチャンネルで送るところに含む必要があるでしょう。 この辺りはOAuth 2.0 Rich Authorization Requestsなんかを使って複雑なデータ表現が可能になります。

tools.ietf.org

では、どんな情報を送ったら良いでしょうか。 そもそもこんなの既にどこかでやられてるのに違いないということで、3D Secure 2.0を参考にしてみましょう。

3D Secure 2.0のリスクベース認証の仕組み

この辺りをざっくりと理解するためにちょうど良いドキュメントがあります。

stripe.com

3D セキュア 2 は、企業がオンラインでのクレジットカード決済を安全に認証できるようにするためのセキュリティ規格です。3D セキュア 2 (3DS2) について詳しくご説明します。

とか書いてますが中身英語です。”Frictionless Authentication” ってとこに書いてあります。

3D Secure 2 allows businesses and their payment provider to send more data elements on each transaction to the cardholder’s bank. This includes payment-specific data like the shipping address, as well as contextual data, such as the customer’s device ID or previous transaction history. If the data is enough for the bank to trust that the real cardholder is making the purchase, the transaction goes through the “frictionless” flow and the authentication is completed without any additional input from the cardholder.

いろんなデータを送っているようです(ざっくり)。

これをOIDCで扱うデータに置き換えてみると

  • Clientはデバイス(またはセッション、あるいはその両方)の情報をAuthNリクエストに追加する
  • IdPは受け取った情報からリスクを判断し、それが低い場合は対話をスキップしても良い

って感じにできそうです。

次はデータの内容ですが、リスクベース認証のために3Dセキュア2.0で送信されるデバイス情報は、EMV® 3-D Secure SDK — Device Informationとして定義されています。

具体的には

というあたりのデータが、仕様に準拠していることを認定されたSDKを使用することで取得、送信されます。

# Device Info for Android (from spec)
{
"DV":"1.0", # DV: Data Version
"DD":{"C001":"Android","C002":"HTC One_M8","C004":"5.0.1","C005":"en- US","C006":"Eastern Standard Time","C007":"06797903-fb61-41ed-94c2-4d2b74e27d18","C009":"John's Android Device",....}, # DD: Device Data
"DPNA":{"C010":"RE01","C011":"RE03"}, # DPNA: Device Parameter Not Available
"SW":["SW01","SW04"] # SW: Security Warning. For information about Security Warning, refer to the EMV 3-D Secure SDK Specification.
}

Webブラウザーベースの場合、3D Secure 2.0の仕様で定義された値のリストがあります。

  • Browser Accept Headers
  • Browser IP Address
  • Browser Java Enabled
  • Browser Language
  • Browser Screen Color Depth
  • Browser Screen Height
  • Browser Screen Width
  • Browser Time Zone
  • Browser User-Agent

ということで、全部含めたら結構な量になりそうですが、このアプローチはOIDCにも適用できそうです。

OIDCの認証リクエストにデバイス情報を追加する拡張案

比較的大きなデバイス情報を送る必要がありそうですが、既にOIDC/OAuth 2.0で使われている方法が適用できるでしょう。

  • JWTにシリアライズした値 (OIDC の “request” パラメータ)
  • JSON もしくは JWT をホストするURI (OIDC の “request_uri” パラメータや OAuth 2.0 の PAR)

あんまりこういう方式をネストしたくはない気もしますが、デバイス情報の場合は

  • “device_info”
  • “device_info_uri

というパラメータを新規に用意することで実現可能となるでしょう。 この時、JWTペイロードまたはJSON本文には、デバイス情報が含まれています。

# JWT's Payload and device_uri response
{
 "platform":"Android",
 "device_model":"HTC One_M8",
 "os_version":"5.0.1",
 ...
 "device_name":"John's Android Device",
 ...
}

CIBAの認証リクエストに含む場合、こんな感じになるでしょう。

POST /bc-authorize HTTP/1.1
   Host: server.example.com
   Content-Type: application/x-www-form-urlencoded

scope=openid%20email%20example-scope&
client_notification_token=8d67dc78-7faa-4d41-aabd-67707b374255&
binding_message=W4SCT&
login_hint_token=eyJraWQiOiJsdGFjZXNidyIsImFsZyI6IkVTMjU2In0.eyJ
zdWJfaWQiOnsic3ViamVjdF90eXBlIjoicGhvbmUiLCJwaG9uZSI6IisxMzMwMjg
xODAwNCJ9fQ.Kk8jcUbHjJAQkRSHyDuFQr3NMEOSJEZc85VfER74tX6J9CuUllr8
9WKUHUR7MA0-mWlptMRRhdgW1ZDt7g1uwQ&
client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3A
client-assertion-type%3Ajwt-bearer&
client_assertion=eyJraWQiOiJsdGFjZXNidyIsImFsZyI6IkVTMjU2In0.eyJ
pc3MiOiJzNkJoZFJrcXQzIiwic3ViIjoiczZCaGRSa3F0MyIsImF1ZCI6Imh0dHB
zOi8vc2VydmVyLmV4YW1wbGUuY29tIiwianRpIjoiYmRjLVhzX3NmLTNZTW80RlN
6SUoyUSIsImlhdCI6MTUzNzgxOTQ4NiwiZXhwIjoxNTM3ODE5Nzc3fQ.Ybr8mg_3
E2OptOSsA8rnelYO_y1L-yFaF_j1iemM3ntB61_GN3APe5cl_-5a6cvGlP154XAK
7fL-GaZSdnd9kg&
device_info=eyJ....eyJ... # NEW!!!

IdPはClientから認証リクエストを受け取ったデバイス情報を検証し、ユーザーインタラクション自体をスキップするかどうかを判定できます。 CIBA の場合、 "ユーザーインタラクションなしで即トークンを返す" ことが表現できないと思うので、互換性を考えるとBackchannel Authentication Endpointからはトークンを返さず、その後の各モードに合わせたやり方ですぐにトークンを返すような実装になるでしょう

ユーザーの許可について

ここまで紹介したデータはトラッキング目的でも使われそうなものでした。 Clientはデバイス情報を収集する前にユーザーに使用目的を説明し、同意を得る必要があるかもしれません。

まとめ

この記事のまとめとしては

  • OIDCのリダイレクトフローとCD/ADが分離するフローでは、デバイスリスクの判断のタイミングが異なる
  • 3D Secure 2.0では、Clientはデバイスまたはブラウザーの情報をパラメーターとして送信し、リスクの判断に使用する
  • このアプローチをOIDCに導入するために、追加のパラメーターを考えてみた

となります。

ユーザーの手元のスマホだけで世の中を動かすの、未来感はあっても毎回操作が求められるUXはなかなかしんどいので省略できるものはできた方が良いでしょう。 あんまりこれ系のプロトコル自体にリスクベース認証がどうこうってのは定義されていないものですが、既存の仕組みで応用できるものがあったらどんどん取り込んでいく、取り込めるように作れるのが標準化ってやつだと思います。

初学者向けの話とか仕様紹介だけじゃなくたまにはこういう話も良いですね。

ではまた!

CIBA is 何

ritou.hatenablog.com

ritou.hatenablog.com

In English

medium.com

OAuth 2.0/OIDCに入門した開発者が仕様沼に潜るための次のステップとは?

f:id:ritou:20200430112204p:plain

お疲れ様です、ritou です。

OAuth 2.0やOIDCの入門書(?)を読み終えた開発者の方に、仕様を理解していくための次のステップは何があるかを聞かれました。

そもそもそんなこと言う人は

  • クライアントを実装したい(しなければならない)
  • 認可サーバーを実装したい(しなければならない)
  • セキュリティエンジニアを名乗っていてこの分野を抑えときたい
  • ただ単純に興味がある : そんな人いる?

とかそんな感じな気はしますが、基本的なフローを乗り越えた先に広がる仕様沼への潜り方に戸惑っておられるようでした。 そこで、いわゆる RFC6749/6750/7636 あたりを完全に理解した開発者が山ほどある仕様にどう立ち向かっていくかを考えます。

仕様にも色々ある

IETF の OAuth関連の仕様、いっぱいあります。密です。密です。みみみみみみみみ...

tools.ietf.org

去年に一回まとめ記事を書きました。

qiita.com

OIDCの方もあります。

openid.net

こっちはまだまとめ記事完成してません(やる気が404 not found)

とりあえず、仕様にはいくつかの種類があります。

  • 既存のフローの一部を拡張
  • 新たな認証認可フロー
  • ベストカレントプラクティス(BCP)

などで分類できますが、まずは自分が求めている仕様はどの辺りかを見極める必要があるでしょう。

コンシューマ向けのサービスであれば Device Flow などの新たな認可フローに手を出すのも良いでしょう。 脅威、脆弱性とその対策を知りたければBCP系を攻めるのもありでしょう。 この2つのやり方はそんなに迷わずに取り組めるかと思いますが、FAPIなどを見据えて既存の認可フローを強化するような拡張仕様を見ていく場合はどこから攻めていくかよく考える必要があるでしょう。

認可フローの分割と対応する仕様の見極め

例えば認可コードフローの拡張仕様を見ていく際、一旦フローを分割して考える方法をお薦めします。

  • 認可(認証)リクエス
  • 認可(認証)レスポンス
  • アクセストークンリクエスト/レスポンス
  • リソースアクセス
  • トークンイントロスペクションリクエスト/レスポンス

そして仕様がこれらのどこに対応するものかを整理します。 どれか一つにだけ対応しているわけではなく、複数のパートに関連している場合がほとんどです。 最近Activeな仕様でいうと...

  • JSON Web Token (JWT) Profile for OAuth 2.0 Access Tokens
    • アクセストークンリクエスト/レスポンス
    • リソースアクセス
    • (Implicitだったら認可レスポンス)
  • OAuth 2.0 Demonstration of Proof-of-Possession at the Application Layer(DPoP)
    • アクセストークンリクエスト/レスポンス
    • リソースアクセス
  • OAuth 2.0 Pushed Authorization Requests
    • 認可リクエス
    • (新規エンドポイント)
  • OAuth 2.0 Rich Authorization Requests

という具合に分類できます。仕様がどれに対応するかは仕様のTOCを見れば大体わかりますね。 そうすると各ステップに対応する複数の仕様を比較して考えたりとか、FAPIなどで選択肢として挙げられた仕様への理解も早まるかもしれません。

本当はこれらを全部整理した上で認可フローの各ステップに対応する仕様一覧はこれだ!って出した上でお好きなのから見ていってくださいね〜と言いたいところですがちょっとめんどくさいので、このアプローチに興味ある人がいたら誰か一緒に整理しましょう。

まとめ

  • 仕様の種類を分類して、進むべき道によって方向性を決める
  • 拡張仕様に触れたければ、まずは認可フローの各ステップとの関連を意識せよ

何か相談したいことがあれば声をかけてください。 ではまた。