Edited at

世界最先端の認証認可技術、実装者による『CIBA』解説


はじめに

この記事では、『OpenID Connect Client Initiated Backchannel Authentication Flow - Core 1.0』、通称『CIBA Core』の解説をおこなう。(CIBA は『シーバ』と読む)

CIBA Core は 2018 年 12 月 14 日に Public Review 期間に入った(アナウンス)。スケジュール通りに進めば、2019 年 2 月初旬には Implementer's Draft として承認される

※:2019 年 2 月 4 日に承認のアナウンスがあった。

Public Review に先立ち、実装者の視点からいろいろ突っ込みを入れておいたので、実装上大きく問題になる箇所はないと思われる(突っ込みを入れ過ぎた結果、仕様書の Acknowledgements に私の名前が記載されている)。また、実際に Authlete 本体に CIBA の実装を追加することもできた。

ciba-merge-request.png

feature/ciba ブランチから Authlete 2.1 へのマージリクエスト

以下、仕様策定作業に参加していた経験と、実際に実装した経験をもとに、CIBA について解説する。


1. CIBA 概要


1.1. フロー開始方法

RFC 6749 で定義されている旧来の認可コードフローおよびインプリシットフローでは、クライアントアプリケーションが Web ブラウザを介して認可サーバーの認可エンドポイントに認可リクエストを投げるところからフローが始まる(参考→『OAuth 2.0 の認可レスポンスとリダイレクトに関する説明』)。

start-from-authorization-endpoint.png

一方 CIBA では、バックチャネル認証エンドポイント(Backchannel Authentication Endpoint)という新たに定義されたエンドポイントに対してクライアントアプリケーションが直接バックチャネル認証リクエスト(Backchannel Authentication Request)を投げるところからフローが始まる。

start-from-backchannel-authentication-endpoint.png


1.2. ユーザー認証・同意取得

旧来のフローと CIBA フロー、どちらの場合も、リクエストを受けた認可サーバーは、クライアントアプリケーションが承認を求めていることをユーザーに伝え、承認するか否かの判断をユーザーに仰ぐ。旧来のフローでは、この処理を、認可画面を表す HTML を認可エンドポイントから Web ブラウザに送り返すことによって実現する。認可サーバーは、ユーザーの判断結果を、HTML フォームが生成する HTTP リクエストとして受け取る。

user-consent-via-web-browser.png

一方 CIBA では、ユーザー認証処理および同意取得処理を、ユーザーの認証デバイス(Authentication Device)に担ってもらう。認証デバイスの典型例はスマートフォンだ。この処理は、バックチャネル認証エンドポイントからクライアントに対して応答を返したあと、バックグラウンドで行われる

user-consent-via-authentication-device.png

CIBA のユーザー認証・同意取得において注目すべきは、クライアントアプリケーションがユーザーの管理下になく、また、クライアントアプリケーションと認証デバイスが物理的に離れていても問題無い点だ。例えば、クライアントアプリケーションが沖縄のコールセンターのオペレーターの目の前で動いているアプリケーションだとしても、ユーザー認証・同意取得処理は、東京からコールセンターに電話をかけているユーザーの手元にあるスマートフォン上でおこなわれる、といったことが CIBA により実現可能となる。


1.3. 同意後のトークン発行

旧来のフローでは、ユーザーの同意を得たあと、認可サーバーは、認可コードフローでは認可コードを発行し、インプリシットフローではアクセストークンを直接発行する。いずれにしても、認可サーバーはクライアントアプリケーションと直接やりとりできないので、Web ブラウザを介してクライアントアプリケーションに認可コードもしくはアクセストークンを渡さなければならない。このときに使われるトリックが HTTP リダイレクトで、図にすると次のようになる(詳細解説→『OAuth 2.0 の認可レスポンスとリダイレクトに関する説明』)。旧来のフローが『Redirect フロー』に分類されるのはこれが理由である。

redirection-in-authorization-code-flow.png

認可コードフローのリダイレクト

redirection-in-implicit-flow.png

インプリシットフローのリダイレクト

一方 CIBA では、ユーザーの同意を得たあとの処理の流れは、三種類ある。それぞれ、POLL モードPING モードPUSH モードと呼ばれる。いずれも、最終的にはクライアントに ID トークン・アクセストークン・リフレッシュトークン(任意)を発行することになる。


1.4. POLL モード

POLL モードでは、バックチャネル認証エンドポイントにリクエストを投げて応答を得たあと、クライアントはトークンエンドポイントに対してトークンリクエストを繰り返す(ポーリング)。

ciba-poll-mode.png

トークンリクエストを受けたときにまだ認証デバイスでの処理が終わっていない場合、認可サーバーは "error":"authorization_pending" を含む JSON を 400 Bad Request で返す。クライアントは、このエラーを受け取っている間は、トークンリクエストを繰り返す。なお、トークンリクエストの間隔が短過ぎると、認可サーバーは "error":"slow_down" を返してくる。

ID トークン・アクセストークン・リフレッシュトークン(任意)を受け取るか、もしくは、authorization_pending / slow_down 以外のエラーを受け取れば、クライアントはトークンエンドポイントへのポーリングをやめる。


1.5. PING モード

PING モードでは、認証デバイスでのユーザー認証・同意取得処理が終わったあと、認可サーバーはクライアントのクライアント通知エンドポイント(Client Notification Endpoint)に対して通知を送る。この通知を受けたあと、クライアントはトークンエンドポイントにトークンリクエストを投げる

ciba-ping-mode.png

クライアント通知エンドポイントとクライアントアプリケーション本体は別々でかまわないが、クライアント通知エンドポイントの実装は、通知を受け取ったことをクライアントアプリケーション本体に何らかの方法で伝えることができなければならない。

トークンエンドポイントからは、ID トークン・アクセストークン・リフレッシュトークン(任意)が返ってくる。


1.6. PUSH モード

PUSH モードでは、認証デバイスでのユーザー認証・同意取得処理が終わったあと、認可サーバーは ID トークン・アクセストークン・リフレッシュトークン(任意)を生成し、それらをクライアント通知エンドポイントに直接渡す

ciba-push-mode.png

PING モードと同様、クライアント通知エンドポイントとクライアントアプリケーション本体は別々でかまわないが、クライアント通知エンドポイントの実装は、受け取ったトークン群をクライアントアプリケーション本体に何らかの方法で渡すことができなければならない。


2. CIBA リクエスト・レスポンスまとめ

ここでは、CIBA フローのリクエスト・レスポンスのパラメーター群について簡潔にまとめる。各パラメーターの詳細については仕様書原文を参照のこと。


2.1. 共通


2.1.1. クライアント認証

CIBA フロー内のバックチャネル認証リクエストおよびトークンリクエストにおいて、クライアント認証方式により、次のパラメーター群が追加で必要となる。

クライアント認証方式
追加パラメーター等

client_secret_basic

Authorization ヘッダー

client_secret_post

client_id, client_secret

client_secret_jwt

client_assertion, client_assertion_type

private_key_jwt

client_assertion, client_assertion_type

tls_client_auth

client_id, クライアント証明書

self_signed_tls_client_auth

client_id, クライアント証明書


2.1.2. request リクエストパラメーター

バックチャネル認証リクエストでは、request という名の特別なリクエストパラメーターを使うことができる。詳細については、後述の「3.1. リクエストオブジェクト」を参照のこと。


2.1.3. トークンリクエスト

POLL モードと PING モードでは、クライアントはトークンリクエストをおこなう。リクエストの内容は両モードで共通となる。

POST {トークンエンドポイント} HTTP/1.1

Host: {認可サーバー}
Content-Type: application/x-www-form-urlencoded

grant_type={グラントタイプ}& // 必須。
auth_req_id={バックチャネル認証エンドポイントから発行されたもの} // 必須。



  • grant_type の値は urn:openid:params:grant-type:ciba で固定。


2.1.4. トークンレスポンス

POLL モードと PING モードでは、クライアントはトークンリクエストをおこなう。それに応じてトークンエンドポイントはレスポンスを返すが、その内容は両モードで共通となる。

HTTP/1.1 200 OK

Content-Type: application/json
Cache-Control: no-store

{
"access_token": "{アクセストークン}",
"token_type": "{トークンタイプ}",
"refresh_token": "{リフレッシュトークン}", // 任意
"expires_in": {アクセストークン有効期間秒数},
"id_token": "{IDトークン}"
}


2.2. POLL モード

ciba-poll-mode.png

(再掲)


2.2.1. POLL モード:バックチャネル認証リクエスト

POST {バックチャネル認証エンドポイント} HTTP/1.1

Host: {認可サーバー}
Content-Type: application/x-www-form-urlencoded

scope={スコープ群}& // 必須。openid を含める。
acr_values={認証コンテキストクラス群}& // 任意。
(login_hint_token|id_token_hint|login_hint)={ヒント}& // いずれかが必須。
binding_message={メッセージ}& // 任意。
user_code={ユーザーコード}& // 設定によっては必須。
requested_expiry={auth_req_id有効秒数} // 任意。


  • POLL モードでは通知が行われないので、バックチャネル認証リクエストに client_notification_token を含める必要はない。


2.2.2. POLL モード:バックチャネル認証レスポンス

HTTP/1.1 200 OK

Content-Type: application/json
Cache-Control: no-store

{
"auth_req_id": "{新規発行されたauth_req_id}",
"expires_in": {新規発行されたauth_req_idの有効期間秒数},
"interval": {最小許容ポーリング間隔秒数}
}


2.3. PING モード

ciba-ping-mode.png

(再掲)


2.3.1. PING モード:バックチャネル認証リクエスト

POST {バックチャネル認証エンドポイント} HTTP/1.1

Host: {認可サーバー}
Content-Type: application/x-www-form-urlencoded

scope={スコープ群}& // 必須。openid を含める。
client_notification_token={クライアント通知トークン}& // 必須。
acr_values={認証コンテキストクラス群}& // 任意。
(login_hint_token|id_token_hint|login_hint)={ヒント}& // いずれかが必須。
binding_message={メッセージ}& // 任意。
user_code={ユーザーコード}& // 設定によっては必須。
requested_expiry={auth_req_id有効秒数} // 任意。


  • PING モードでは通知が行われるので、client_notification_token は必須となる。


2.3.2. PING モード:バックチャネル認証レスポンス

HTTP/1.1 200 OK

Content-Type: application/json
Cache-Control: no-store

{
"auth_req_id": "{新規発行されたauth_req_id}",
"expires_in": {新規発行されたauth_req_idの有効期間秒数},
"interval": {最小許容ポーリング間隔秒数}
}


2.3.3. PING モード:通知

POST {クライアント通知エンドポイント} HTTP/1.1

Host: {通知を受けるサーバー}
Authorization: Bearer {クライアント通知トークン}
Content-Type: application/json

{
"auth_req_id": "{バックチャネル認証エンドポイントから発行された値と同じ}"
}


2.4. PUSH モード

ciba-push-mode.png

(再掲)


2.4.1. PUSH モード:バックチャネル認証リクエスト

POST {バックチャネル認証エンドポイント} HTTP/1.1

Host: {認可サーバー}
Content-Type: application/x-www-form-urlencoded

scope={スコープ群}& // 必須。openid を含める。
client_notification_token={クライアント通知トークン}& // 必須。
acr_values={認証コンテキストクラス群}& // 任意。
(login_hint_token|id_token_hint|login_hint)={ヒント}& // いずれかが必須。
binding_message={メッセージ}& // 任意。
user_code={ユーザーコード}& // 設定によっては必須。
requested_expiry={auth_req_id有効秒数} // 任意。


  • PUSH モードでは通知が行われるので、client_notification_token は必須となる。


2.4.2. PUSH モード:バックチャネル認証レスポンス

HTTP/1.1 200 OK

Content-Type: application/json
Cache-Control: no-store

{
"auth_req_id": "{新規発行されたauth_req_id}",
"expires_in": {新規発行されたauth_req_idの有効期間秒数}
}


  • PUSH モードではトークンリクエストは行われないので、バックチャネル認証レスポンスに interval は含まれない。


2.4.3. PUSH モード:通知

POST {クライアント通知エンドポイント} HTTP/1.1

Host: {通知を受けるサーバー}
Authorization: Bearer {クライアント通知トークン}
Content-Type: application/json

{
"auth_req_id": "{バックチャネル認証エンドポイントから発行された値と同じ}",
"access_token": "{アクセストークン}",
"token_type": "{トークンタイプ}",
"refresh_token": "{リフレッシュトークン}", // 任意
"expires_in": {アクセストークン有効期間秒数},
"id_token": "{IDトークン}"
}


  • PUSH モードの通知に含まれる ID トークンには、auth_req_id の値を値として持つurn:openid:params:jwt:claim:auth_req_id クレームが含まれる。また、リフレッシュトークンが発行されている場合には、そのハッシュ値を値として持つ urn:openid:params:jwt:claim:rt_hash クレームが含まれる。リフレッシュトークンのハッシュ値の計算方法は、『OIDC Core 3.2.2.9. Access Token Validation』に記載されているアクセストークンのハッシュ値計算方法に準じる。


3. CIBA 実装者の所感

ここでは、CIBA 実装者が気にするポイントを、CIBA 仕様策定過程の議論を交えながら、幾つか取り上げる。


3.1. リクエストオブジェクト

CIBA のドラフトを初めて読んだ時に最初に厄介だと思ったのは、バックチャネル認証リクエストのリクエストパラメーター群を、OIDC Core のリクエストオブジェクトと同じように(OIDC Core, 6. Passing Request Parameters as JWTs)、JWT にまとめて request リクエストパラメーターとして指定できるようにしてある点だった。

リクエストオブジェクトが渡ってきた場合、サーバー側では、もし暗号化されていれば復号化し、それから署名の検証をおこなう。復号化処理および署名検証処理でそれぞれ鍵が必要であるが、それらを登録済みの JWK Set から検索する処理の実装が、まず面倒である。次に、アルゴリズムの違いで復号器や署名検証器の生成方法が異なるので、その点も面倒である。署名検証が終わったあとに、ペイロード部の audissexpiatnbfjti を検証するのも面倒である。

ただ、ドラフト段階でリクエストオブジェクトの暗号化について言及がなかった(特に暗号関連のメタデータ定義が存在しなかった)ので、「暗号化をサポートする予定はあるのか?なければその旨仕様書に明記してほしい。あるならば暗号関連のメタデータを定義すべき」という要望を出したのが Issue 105 である。


  • Issue 105】CIBA: encryption of backchannel authentication request

この結果、仕様書に次の一文が加わることとなった。


Note that encrypted JWT authentication requests are not supported.


おかげで JWT の暗号化に対応しなくてもよくなったので、その分実装が楽になった。

次に気になったのは、request パラメーターが使われた場合、他のリクエストパラメーターの扱いをどうするか、という点だった。OIDC Core では、request に含めたリクエストパラメーターを再度 request 外に指定してもかまわない。どっちかにしか入れなくてもかまわない。FAPI だとまた要求仕様が異なる。

OIDC Core では、RFC 6749 との互換性のため、request パラメーターを使っている場合でも、client_idresponse_type は指定しなければならない。例えそれらが request に指定された JWT の中に含まれていても、だ。逆に、JWT に含まれていた場合、JWT 外で指定された値と同一であることをチェックしなければならない。とにかくいろいろ面倒だ。

しかしながら、CIBA のバックチャネル認証エンドポイントは新規作成する仕様なわけだから、OIDC Core のような面倒を避けることが可能なはずだ。そこで、「request が使われていた場合、他のリクエストパラメーターの使用を禁止してはどうか?」という提案をおこなったのが、Issue 117 だ。


  • Issue 117】CIBA: other request parameters when "request" is present

この結果、request パラメーターが使われた場合は他のリクエストパラメーターを禁止する方向でドラフトが変更されたが、依然として不明瞭な部分が残っていたので、ジョセフ・ヒーナンが Issue 128 を挙げた。


  • Issue 128】ambiguities in 7.1.1 signed authentication request

最終的に、次のような仕様になった。


  1. request が使われた場合、他のリクエストパラメーター群を使用してはならない。

  2. ただし、クライアント認証に関わるリクエストパラメーター群(client_idclient_assertion など)は、request 外に指定する。

  3. 逆に、クライアント認証に関わるリクエストパラメーター群を request 内に含めてはならない。

原文は次のとおり。


The signed authentication request JWT is passed as an application/x-www-form-urlencoded HTTP request parameter with the name request. Authentication request parameters MUST NOT be present outside of the JWT, in particular they MUST NOT appear as HTTP request parameters. Additional HTTP request parameters as required by the given client authentication method, however, MUST be included as application/x-www-form-urlencoded parameters (e.g. Mutual TLS client authentication uses client_id while JWT assertion based client authentication uses client_assertion and client_assertion_type).


これにより、OIDC Core と比べて request の処理は大分楽になった。

あと、興味深いのは、JWT の署名アルゴリズムが非対称鍵系に限定されることである。これにより、対称鍵系アルゴリズムである HS256HS384HS512 は排除されることになる。OIDC の文脈においては、「クライアントシークレットも他の鍵と同様に JWK Set 内で管理する」という実装にでもなっていない限り、対称鍵系アルゴリズムの鍵の算出には別途対応が必要となるので(OIDC Core, 10.1. Signing)、アルゴリズムを非対称鍵に限定するという仕様は実装の容易化に資する。実装者としてはありがたい。

なお、OIDC Core, 6.2.2. で定義されている request_uri パラメーターはバックチャネル認証リクエストには存在しないので安心してほしい。もしこれがあったら、実装はかなり大変なことになる。


3.2. クライアント認証

CIBA Core, 7.1. Authentication Request の第二段落には次のように書いてある。


The Client MUST authenticate to the Backchannel Authentication Endpoint using the authentication method registered for its client_id, such as the authentication methods from Section 9 of [OpenID.Core] or authentication methods defined by extension in other specifications.


つまり、バックチャネル認証エンドポイントにおいてクライアント認証が必須ということである。必然的に、CIBA を利用できるのは Confidential クライアントだけであり、Public クライアントは利用できない、ということになる。

CIBA が言及している『OIDC Core, 9. Client Authentication』に列挙されているクライアント認証方式は次のとおり(none を除く)。

クライアント認証方式

1
client_secret_basic

2
client_secret_post

3
client_secret_jwt

4
private_key_jwt

FAPI の文脈においては、『OAuth 2.0 Mutual TLS Client Authentication and Certificate Bound Access Tokens』で定義されている下記のクライアント証明書系クライアント認証方式も考慮する必要がある。

クライアント認証方式

5
tls_client_auth

6
self_signed_tls_client_auth

認可サーバーがサポートするクライアント認証方式については、通常、エンドポイント毎にメタデータが存在する。例えば、トークンエンドポイントでサポートするクライアント認証方式については token_endpoint_auth_methods_supportedというメタデータが用意されている(RFC 8414)。同様にして、イントロスペクションエンドポイント(RFC 7662)については introspection_endpoint_auth_methods_supported、リボケーションエンドポイント(RFC 7009)については revocation_endpoint_auth_methods_supported、というメタデータが用意されている。

そこで、バックチャネル認証エンドポイントでサポートするクライアント認証方式を列挙するためのメタデータとして backchannel_authentication_endpoint_auth_methods_supported を提案したのが、Issue 102 だ。


  • Issue 102】CIBA: Metadata for client auth at backchannel endpoint

しかしながら、このメタデータが仕様に追加されることはなかった。なぜか? これには、CIBA 仕様の編集者であるブライアン・キャンベルの考えがある。彼のコメントをそのまま転載しよう。


There's some historical weirdness to how client authentication has come to be represented (and named) in metadata. Client registration metadata has only a token_endpoint_auth_method, which despite the name has become the de facto place in the client data model to say how the client will authenticate to the AS when making any direct client -> AS call. The parameter name is a bit unfortunate but I believe it makes sense to have a single (backchannel) authentication method per client. RFC 8414 took a different direction and has revocation_endpoint_auth_methods_supported and introspection_endpoint_auth_methods_supported etc., which I believe was a mistake. I don't see a clear use case for needing or allowing a different set of auth methods for the different endpoints. And having a bunch of _supported parameters for different endpoints seems likely to clutter up the metadata document with a bunch of redundant info.

I'd propose that some text be added into CIBA core that says that, for the backchannel authentication endpoint, the AS supports the same client authentication methods as indicated with token_endpoint_auth_methods_supported. And state that, for a client, the token_endpoint_auth_method is the authentication method registered for its client_id regardless of the endpoint being called.


これは分かる。サポートするクライアント認証方式を表すメタデータをエンドポイント毎に用意するのは、実装者としては面倒だし、正直、エンドポイント毎にクライアント認証方式を変更可能にする必要があるユースケースを考え出すのも難しい。クライアント認証方式を表すメタデータは全エンドポイント共通で一つあればおそらく十分。ブライアンは「RFC 8414 は revocation_endpoint_auth_methods_supportedintrospection_endpoint_auth_methods_supported を定義し、異なる方向へ進んでしまったが、これは間違いだったと私は信じている」とまで言っている。

この結果、バックチャネル認証エンドポイントにおけるクライアント認証方式を表すための新たなメタデータが追加されることはなくなった。かわりに、サーバー側は token_endpoint_auth_methods_supported、クライアント側は token_endpoint_auth_method で代用することとなった。また、クライアント認証方式で JWT 系を使う際に、その JWT の署名アルゴリズムを表すメタデータである token_endpoint_auth_signing_alg_values_supported(サーバー側)と token_endpoint_auth_signing_alg(クライアント側)も同様の扱いとなる。

ところで、実はバックチャネル認証リクエストでは client_id リクエストパラメーターは必須ではない。この点は RFC 6749 の認可リクエストと異なる。では、バックチャネル認証エンドポイントの実装はどのようにしてクライアント識別子を特定するのか?

まず、クライアント認証方式が client_secret_basicclient_secret_post の場合、これは簡単である。なぜなら、クライアント ID の値が直接リクエストの中に含まれているからだ。

次に、クライアント認証方式が client_secret_jwtprivate_key_jwt の場合だが、このときは client_assertion リクエストパラメーターの値として指定された JWT のペイロードに含まれる iss クレームの値をクライアント ID とみなして処理すればよい。

最後に、クライアント認証方式が tls_client_authself_signed_tls_client_auth の場合だが、実はこの認証方式にはクライアント ID を示す情報が含まれない。そのため、これらのクライアント認証方式を使う場合、client_id リクエストパラメーターを含めなければならない。

CIBA 仕様の『7.1. Authentication Request』の後半に追加された次の文は、上記のことを念頭に置いたものである。


When applicable, additional parameters required by the given client authentication method are also included (e.g. JWT assertion based client authentication uses client_assertion and client_assertion_type while Mutual TLS client authentication uses client_id).



3.3. 認証コンテキストクラス

バックチャネル認証リクエストには acr_values というリクエストパラメーターがある。このリクエストパラメーターは OIDC Core にも存在し、いろいろ面倒なやつだ(参考→「16. 認証コンテキストクラス」)。

acr_values には、ユーザー認証時に満たしてほしい認証コンテキストクラスを、望ましい順番に列挙する。OIDC Core では、認可サーバー側は、できるだけ指定された認証コンテキストクラスを満たそうと試みる。しかし、例えどれ一つ満たせなくても、実はエラーとはみなされない。なぜなら、acr_values を用いて認証コンテキストを指定した場合、任意という扱いになってしまうからだ。

これを必須としたい場合、つまり、どれかを満たせない限りエラーとみなしてほしい場合、acr_values 以外の方法で認証コンテキストを指定する必要がある。その方法は、大方の人が予想するよりもかなり面倒なもので、claims リクエストパラメーターを用いて指定する JSON 内の深い階層に {"essential":true} と書かなければならないのだ。下記は、urn:mace:incommon:iap:silver を必須とする例だ。

{

"id_token":
{
"acr":
{
"essential": true,
"values": ["urn:mace:incommon:iap:silver"]
}
}
}

こんな面倒なことを誰がやるのかと思うかもしれないが、『Financial-grade API - Part 2: Read and Write API Security Profile』の『5.2.3. Public Client』に “shall request user authentication at LoA 3 or greater by requesting the acr claim as an essential claim as defined in section 5.5.1.1 of [OIDC];” という要求事項があるため、FAPI Part 2 に対応するためには、この仕組みの実装が必要となる。

そういうわけで、バックチャネル認証リクエストにおいて ACR を必須とマークする手段を提供するつもりがあるかどうかを問い合わせたのが、Issue 103 だ。claims リクエストパラメーター(OIDC Core, 5.5. Requesting Claims using the "claims" Request Parameter)の実装はかなり大変で、後から追加するのは厄介なので、最初に「claims をサポートするのかしないのか」をはっきりさせておきたかったのだ。


  • Issue 103】CIBA: Means to require "acr" as "essential"

最終的に、ACR を必須とマークする手段は提供しないことになった。ブライアンのコメント “I think that something complicated like the "claims" request parameter from OIDC Core would be overkill in CIBA.” からもわかる通り、CIBA はできるだけ仕様を簡素化しようとしている。


3.4. クレーム指定

OIDC Core では、ID トークンに埋め込んで欲しいカスタムクレームをクライアント側から指定する手段が存在する。claims リクエストパラメーターがそれだ。(1)この手段をバックチャネル認証リクエストでも提供するのか、また、(2)scope リクエストパラメーターに profileemailaddressphone が含まれていた場合にそれらを『OIDC Core, 5.4. Requesting Claims using Scope Values』と同じ方法で解釈すべきか、という問い合わせをしたのが Issue 106 である。


  • Issue 106】CIBA: Means to request claims to be embedded in the issued ID token

結果、カスタムスコープを指定する方法は提供しない、また、profileemailaddressphone は OIDC Core と同様に解釈する、ということが決まった。

claims リクエストパラメーターの値は、仕様書をよく読んで実装してみれば分かるが、JSON パーサーで特殊クラスのインスタンスに一気にマッピングできる類のものではなく、Map などの汎用クラスのインスタンスに変換したあとに手作業でパース処理をおこなう必要がある。つまり、面倒くさい。claims リクエストパラメーターをサポートしなくてよいのは、実装者としてはありがたい。


3.5. ヒント

バックチャネル認証リクエストには、対象となるユーザーを特定するためのヒントを含めなければならない。ヒントを指定するためのリクエストパラメーターとして、login_hint_tokenid_token_hintlogin_hint の三つがある。バックチャネル認証リクエストパラメーターは、これらのうち必ず一つを含まなければならず、また、同時に複数のヒントを含めてはならない。

id_token_hintlogin_hint は OIDC Core に倣ったものだ。login_hint_token は CIBA で追加されたものであるが、そのフォーマットは何も規定されていない。

さて、リクエストのバリデーションの実装を開始すればすぐに気がつくが、



  1. login_hint_token が含まれていたら id_token_hintlogin_hint は含まれていてはならない。


  2. id_token_hint が含まれていたら login_hint_tokenlogin_hint は含まれていてはならない。


  3. login_hint が含まれていたら login_hint_tokenid_token_hint は含まれていてはならない。


  4. login_hint_tokenid_token_hintlogin_hint のどれか一つが含まれていなければならない。

をチェックする処理は、もちろん実装可能であるが、美しいコードにならない。

「いずれか一つを指定」というメカニズムを実現するには、ヒント毎にリクエストパラメーターを用意するのではなく、次のようなリクエストパラメーター設計にしたほうがよい。

リクエストパラメーター名
説明

hint_type
ヒントの種類

hint
ヒントの値

クライアントの実装、サーバーの実装、どちらにとってもこちらのほうが都合が良い。そこで提案したのが、Issue 123 だ。

しかしながら、この案は却下されることになる。ブライアンも、三つのパラメーターは awkward で、hint/hint_type のアプローチに利点があることは認めつつも、時期的なことを考えると、変更を加えるほどではないという意見だった。

私の案のほうが良いと今でも思っているが、MODRNA ワーキンググループが CIBA Core の Public Review 入りを急いでいたことは分かっていたので、食い下がることはしなかった。

ただし、Authlete の実装には私の考え方が反映される。というか、CIBA 対応認可サーバーのサンプル実装をしている池田君も、Consumption Device / Authentication Device のシミュレーターを実装しているヘンリックさんも同意見であり、現実世界で実際に CIBA 実装に携わっているデベロッパーの多数意見(サンプル数=3)が hint/hint_type アプローチを推しているわけなので、デベロッパー向けの製品である Authlete はそれに従う。

具体的には、バックチャネル認証リクエストを解析する Authlete の /api/backchannel/authentication API が返すレスポンス(BackchannelAuthenticationResponse)には、loginHintTokenidTokenHintloginHint というレスポンスパラメーターはなく、代わりに hintTypeUserIdentificationHintType)と hintString)というレスポンスパラメーターが含まれる。

なお、Authlete 共用サーバーのバージョンは 1.1 と古く、CIBA には対応していない(FAPI にも対応していない)。CIBA 対応済みの Authlete を試用したい場合は sales@authlete.com まで問い合わせてほしい。


3.6. その他

他にも、client_notification_token の値に使える文字種とその長さ(Issue 104)、PUSH モードで発行される ID トークンには urn:openid:params:jwt:claim:rt_hash が含まれるが他のモードでは含まれないのは意図的なのか(Issue 127)、PUSH モード時のエラーコードとして expired_token って使う?(Issue 143)、など、実装者の立場から明確化してほしい点を中心にいろいろ Issue を挙げた。ここに挙げていない新しい発見もあると思うので、興味があれば MODRNA ワーキンググループの Issue リストに目を通すとよいだろう。

余談であるが、仕様書内の大量の typo や文法ミスを指摘しまくった(Issue 99Issue 110Issue 129)のは日本人の私である。これは、typo や文法間違いは、英文を一語一語丁寧に読む(読まざるを得ない)非ネイティブスピーカーのほうが気がつきやすいことを示している。こういう貢献の仕方もあるので、日本人の皆さんも気後れすることなく OpenID Foundation のワーキンググループの活動に参加してほしい。


3.7. 今後

2018 年末に CIBA Core が Public Review に附されたのは、議論が出尽くして枯れた仕様となったから、ではなく、欧州の金融政策の日程に間に合わせるため、という理由が大きい。とはいえ、元となった仕様とかなり差分があるので、ここで一度 CIBA Core の Implementer's Draft 第一版を世に出すことの意義は大きい。

FAPI は、Implementer's Draft 第一版リリース後も議論が続き、2018 年 10 月末に第二版がリリースされた。これと同じように、CIBA Core についても、第一版リリース後も議論が続き、やがては第二版が出るだろう。現に Public Review 期間中にも仕様に影響しそうな Issue が幾つか挙がっている。この分野に携わる者は MODRNA ワーキンググループの議論を継続的に追っていく必要がある。

なお、CIBA Core の Public Review 入りを受け、FAPI ワーキンググループでは FAPI-CIBA プロファイルの議論が再開された。「FAPI では PUSH モード禁止」などの制約事項や追加要求事項がまとめられることになる。こちらもあまり時間をおかずに Public Review 入りすることになるだろう。


4. Authlete の CIBA 実装

Authlete は、CIBA をサポートするため、新たに次の 4 つの API を追加した。

1
/api/backchannel/authentication

用途
バックチャネル認証リクエストを解析する

リクエスト仕様
BackchannelAuthenticationRequest

レスポンス仕様
BackchannelAuthenticationResponse

2
/api/backchannel/authentication/issue

用途

auth_req_id を発行する

リクエスト仕様
BackchannelAuthenticationIssueRequest

レスポンス仕様
BackchannelAuthenticationIssueResponse

3
/api/backchannel/authentication/fail

用途
バックチャネル認証エンドポイントからのエラー応答を生成する

リクエスト仕様
BackchannelAuthenticationFailRequest

レスポンス仕様
BackchannelAuthenticationFailResponse

4
/api/backchannel/authentication/complete

用途
ユーザー認証・認可後の完了処理をおこなう

リクエスト仕様
BackchannelAuthenticationCompleteRequest

レスポンス仕様
BackchannelAuthenticationCompleteResponse

また、/api/auth/token API の実装に CIBA サポートを追加した。

これらの API を下図のように組み合わせて利用することで、CIBA の各フローを実装することができる。

ciba-poll-mode-authlete.png

ciba-ping-mode-authlete.png

ciba-push-mode-authlete.png

CIBA 対応認可サーバーの実装方法ついては次の記事が詳しい。

なお、Authlete は意図的にユーザーデータ管理・ユーザー認証をおこなわない設計になっているので(参照→『OAuth 2.0 / OIDC 実装の新アーキテクチャー』)、認可サーバーの実装と、その実装と認証デバイスとのやりとりは、Authlete 利用者に自ら実装してもらうことになる。

オープンソースとして公開している認可サーバーのサンプル実装である java-oauth-server の最新版は CIBA 対応済みである。ただし、CIBA を試すには、バックエンドに CIBA 対応済みの Authlete(バージョン 2.1 以降)が必要である。試用したい場合は sales@authlete.com まで問い合わせてほしい。

また、今回新規開発した認証デバイスとクライアントのシミュレーターについては、それらをホスティングするウェブサイトを公開する予定である。


おわりに

FAPI の Implementer's Draft 第二版には、CIBA は FAPI の構成要素の一つであると書かれている。イギリスのオープンバンキングも CIBA の採用を決定した。オーストラリアの金融機関も CIBA に大きな関心を示している。日本ではまだ馴染みが薄いかもしれないが、海外、特に欧州では「CIBA を使うのが当たり前」という雰囲気さえできつつある。

CIBA が可能にする新しいユースケースは、新しいビジネスを生み出す。このビジネスチャンスを皆さんもとらえてほしい。Authlete 社も、(おそらく)世界で最初の商用 CIBA 実装を武器に、新しいマーケットに進出する!(新年の抱負)

今年もよろしくお願いします。