このビデオは、2018 年 7 月 24 日に開催された Financial APIs Workshop 2018 のプレゼンテーション録画のひとつです。 Authlete API に Financial-grade API (FAPI) を実装するにあたり、スコープおよび Request Object をどのように処理しているかを解説し、それらを含めた Authlete の機能拡張のポイントを、 Authlete の池田英貴が紹介します。
プレゼンテーション資料は https://www.slideshare.net/fintechlabs-io/authlete-fapi-implementation-part-2-107692329 からダウンロード可能です。
池田: Authlete の池田と申します。『認可サーバーの FAPI 対応に関して』というタイトルでですね、 同様に、別の観点からお話ししていきます。
内容としては大きく 3 つに分かれていて、最初の 2 つはけっこう技術的に突っ込んだ話になると思います。 最後に Authlete の次期バージョンについて、簡単に説明させていただければと思います。
最初に 1 点目からです。
先ほど他の方からの説明があったように、FAPI の仕様群としては大きく 5 つに分かれていて、 このうち認可サーバーを実装するという観点でとくに重要になってくるのは、パート 1 とパート 2 の 2 つになっています。
パート 1 とパート 2 が具体的にどんな内容なのかいうと、 こっちがパート 1 で、認可サーバーに対してはこういう要求がされる、というかたちになっています。
一方でパート 2 の方は Read and Write ということで、 より厳しい制約が付与されていて、 パート 1 の事項に加えて、さらにたとえば Request Object が必須ですよとか、 response_type に対するパターンが決まってるとか、 そういった内容になっています。
いずれにせよここで重要なのは、認可サーバーとしてパート 1 の仕様に従うか、 あるいは パート2、Read and Write の仕様に従うかによって、 振る舞いが異なるということです。
たとえばここにクライアントアプリがあります。 一方で FAPI の、Read Only と Read and Write の両方に対応したサーバーがあります。
ここにリクエストが飛んでくるわけです。 認可サーバーとしては、このリクエストを Read Only の仕様に従って処理すべきなのか、 あるいは Read and Write の仕様に従って処理すべきなのかを、判定できないといけないわけです。
それをどうやってやるのかという話ですけど、 仕様書を読んでも、とくに書いてないんです。
我々がいま思っている結論は、これはスコープを利用して判定するというやり方をしています。
ちょっと認可サーバーの頭の中を覗くようにイメージしてほしいんですけど、
認可サーバーの中にスコープがいくつか定義されている中に、
read_account
という、自分の口座情報を読み取るようなことを許すスコープがあったとします。
ここに、Key-Value 形式で、属性をひも付けます。
属性は、Keyとして fapi
、Valueとして r
というものを一個定義してるんですけど、
この認可サーバーは、FAPI=r(の属性がひもづいているスコープ)ってのがきたら、
これは FAPI の Read Only に対応するものとする、と定義しておきます。
その上でどうなるかというと、たとえばクライアントアプリから、この FAPI 対応の認可サーバーに認可リクエストが来たとします。
そうするとここの部分に、read_account
というスコープが含まれています。
なので、認可サーバーとしては、read_account
が来た、と。これは "fapi:r"
の属性にひもづくかたちになると。
そして "fapi:r"
は Read Only に対応することがわかるので、
結果的にこの認可リクエストは、FAPI の Read Only、パート 1 に従って処理すればいいんだな、っていうことがわかってきます。
こういうふうに、ランタイムで実際にスコープの値を見て、Read Only なのか Read and Write なのかっていうのを判定するように、うちでは実装しています。
属性定義の例を説明すると、
先ほど言ったように、属性の Key として fapi
、属性の Value として r
っていうものがきたら、
このリクエストは Read Only として処理します。
一方で同じ fapi
でも、Value の部分が rw
、これは Read and Write として処理します。
それ以外の場合に関しては、通常の OpenID Connect の Authentication Request としてリクエストを処理するようにしています。
こういうふうに、仕様には載っていなんですけど、ランタイムで判定するところがひとつ必要になりました。
続いて、ちょっと話はかなりスペシフィックになるんですけれども、Request Object の話になります。
FAPI のパート 2 の項目 10 番に、こんな内容の仕様が書かれています。
これを日本語に訳すとですね、 すべてのパラメーターが、署名された Request Object 内に存在するということを、認可サーバーは要求するものとする。 ということなので、認可サーバーはこれを検証しないといけない、ということになります。
とくに今回注目したいのは、この赤字の部分、「全てのパラメーターが」「リクエストオブジェクト内に存在する」という部分。 これをどうやっているのかを具体的に見ていきたいと思います。
たとえば認可リクエストがこういうふうになりました、と。
Request Object って、先ほどからいろいろな方が説明していますけど、具体的にはここの青い四角の部分です。 ここの部分を Request Object と言っています。 これをデコードすると、中にこういう JSON が入っています。
何を検証するかというと、すべてのパラメーターが Request Object 内に存在するということを確認したい。
実際にやっていけばいいわけです。たとえば response_type
っていうパラメーターがあって、Request Object 内にもあるな、と。
その値は code id_token
で、値も一致するな、と。
文字列比較しながら一個一個チェックしてやっていけばできるんじゃないのと、まあ思うわけです。
ただですね、本当にそれでいいの? っていうのはここで言いたいことなんですよ。
というのは、 実は認可リクエストっていうのは、こういう claims
っていうパラメーターも含めることができるんです。
この claims
は、ちょっと複雑なのでデコードすると、こんな JSON になっています。
一方で、これは Request Object です。デコードします。
Request Object の中にも claims
っていうパラメーターが入っています。
この 2 つの JSON を、文字列比較で等しいかどうかをチェックするんですか、という話です。
途端に、あまり筋の良くないのがわかって、たとえばこの 2 つの JSON は同じ内容ですよね。
で、片方にスペースが入ってたら、どうします?
同じ内容として評価すべきですけど、この(左に示した)全体を文字列、この(右に示した)全体を文字列で、(双方を)文字列として評価してしまったら、等しい値としては評価できないんです。
同様に、列挙の順序が異なっていた場合。
この(左に示した例は) "key1":"value1"
、
こっち(右に示した例)は "key2":"value2"
っていうのが最初に来ています。この 2 つは同じ JSON として評価するべきですけど、
実際に文字列比較だとうまくいかない。
このように、単純に検証を文字列比較だけでやるのはうまくいかないということに気づきまして、 ではどうやるのって話です。
ここからの話は我々のやり方なので、これが絶対に正解というわけではないです。 具体的なやり方はというと、まず JSON を比較して、ひとつをオブジェクトにマッピングします。 ここでオブジェクトっていうのは、ペアレントがチャイルドを持つような、階層構造を持つオブジェクトです。
このオブジェクトを、各子ノードの Key を用いて、たとえば昇順とかアルファベット順にソートします。 これはソートしても順番は変わらないんですけど、最終的に各ノードをソートした順番で評価して、 メッセージダイジェストを生成します。
同様に、別のノードを評価して、ダイジェストを更新します。こうすることで、この JSON からダイジェストっていうのが生成できます。
もう一方の JSON についても、マッピングして並び変えします。これは並び変えすると key1:value1
が先に来るようになります。
同様にダイジェストを生成します。
ダイジェストを更新して、最終的にこの 2 つのダイジェストを比較することで、JSON が等しいかを判断します。
単純な文字列比較だけではなく、こういった、ダイジェスト生成をしてやるっていうような、ちょっとひと工夫必要なところが出てきました、という話です。
ちょっと細かい話にはなったんですが、以上のように実際に FAPI を実際に実装して、振り返って思うことです。
やっぱり悩むところが多かったのは、仕様書の先にある話、とくに仕様書に書かれていない部分です。 そういったところを 2 つほど紹介させてもらいましたけど、今回提示したものが、絶対に正解というつもりはないです。 ほかにもベターな方法とかあるかもしれませんので、参考程度にしていただければと思います。
細かい話は以上です。
最後に Authlete 2.0 についてかんたんに説明します。
先ほど Justin のほうからも説明がありましたけど、 Authlete っていうサービスを我々はやっています。
Authlete ってなんなの? って言ったら、 Web API だと思ってください。 認可サーバーを、開発者がかんたんに作るための、Web API です。
たとえば Stripe の API を使ったら決済システムをかんたんに作れるようになります。 あれと同じ要領で、認可サーバーをかんたんに作るための API を我々は提供しています。
とくにちょっと強調しておきたいのは、Justin をはじめ、かなり世界的に有名な技術者が設計や実装に関わっているので、 そこらへんのクオリティはかなり高いと自負しています。
実際の採用例等ですが、銀行さん、SIer を中心に、分野を問わず広く活用いただいております。 2017年には FIBC というフィンテック系のイベントで大賞をもらうこととなりました。
このたびいろいろ機能を追加するにあたって、大幅に、バージョンを 1.0 から 2.0 へと、アップグレードすることになったんですけれども、 主要な新機能の紹介をすると、まず FAPI モードっていうのが追加されます。
Authlete は GUI も提供していて、そこで認可サーバーの設定もできるようになっているんですけど、 ここ(「サポートするプロフィール群」)に「FAPI」があって、 ここのチェックボックスを入れて、更新ボタンを押すと、この認可サーバーは FAPI に対応するようになります。
同様に先ほど口頭で話していた、スコープの属性管理に関する機能も、GUI でできるようになっています。
さらに、Justin のほうから話にあったクライアント認証についても、 ここらへんでボタンをポチポチすることで、対応する認証方式をサポートできるようになります。
あとは PKI の証明書のチェーンの検証機能等も追加されます。
その他いろいろ便利なAPIを追加予定です。 Authlete 2.0 を近日中にリリースする予定ですので、 ぜひ機会があれば使ってみていただければと思います。
発表としては以上になります。ご静聴ありがとうございました。