Open6

OpenID Connect User Questioning API 1.0

yapooyapoo

OpenID Connect User Questioning API 1.0 (openid-connect-user-questioning-api-1_0)

概要

(OpenID Connect における) クライアントがユーザーに対して選択肢式の質問を行いたいとき、質問と選択肢を OpenID Provider (OP) に送信することで、OP がユーザーに対して適当なインターフェースを提供して質問への回答を促し、クライアントがその結果を OP を介して得る方法が定められている。

質問の例としては「この中で好きな色はなんですか? (選択肢:赤、青、その他)」などがありうる。

ユーザーが OP に対して質問に回答した後、クライアントが質問の回答を受け取る方法は以下の 2 通り定められている。

  • Pulled-By-Client flow (クライアントが OP の特定のエンドポイントをポーリングする)
  • Pushed-To-Client flow (OP は質問の回答を得たらクライアントの特定のエンドポイントにその回答を送信する)

以下に主な処理フローを示す。

yapooyapoo

User Questioning Request Endpoint

User Questioning Request

クライアントはユーザーに行いたい質問があるとき、このエンドポイントに POST リクエストすることで質問を OP に送信する。このエンドポイントは OAuth 2.0 のアクセストークンで保護されていなければならない。リクエストパラメータは以下の通り。

パラメータ 要否 説明
client_notification_token Pushed-To-Client flow の場合必須 OP が Client Notification Endpoint にリクエストする際に用いる Bearer トークン。
user_id アクセストークンが特定のユーザーに紐づかない場合必須 質問を行いたいユーザーの識別子
user_id_type user_id と同様 user_id の種別を表す。sub (識別子) または email (メールアドレス) または msisdn (携帯電話番号) または、独自に定義した値
question_to_display 必須 質問文。「この中で好きな色はなんですか?」など。OP は質問を表示する際にこの文章を改変するべきではない。
statements_to_display 必須 選択肢を文字列の配列で指定する。これも OP は改変するべきではない。
wished_amr 任意 OP がユーザーに質問に回答させる前に行ってほしい認証方法を指定する
wished_acr 必須 OP がユーザーに質問に回答させる前に行ってほしい認証の ACR を指定する

以下は Section 4.1.1 に記載のリクエストの例である。

POST /questions HTTP/1.1
Host: server.example.com
Accept: application/json
Authorization: Bearer SlAV32hkKG 
Content-Type: application/json
Content-Length: xxx

{
  "client_notification_token":"vO4n2DAeRrcWE0VAlo4I",
  "user_id":"+33612345678",
  "user_id_type":"msisdn",
  "question_to_display":"Do you allow ...?",
  "statements_to_display":["Yes","No"],
  "wished_acr":"3",
  "wished_amr":["PIN_OK"]
}    

成功レスポンス

このとき OP は質問に一意に紐づく ID question_id を発行して 200 OK でクライアントに返却する。

以下は Section 4.1.3 に記載のレスポンスの例である。

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: xxx

{
  "question_id": "984dcc7d3d4d4b0f9f8022e344f9"
}

エラーレスポンス

エラーレスポンスについては後述する。エラーコードとしては以下が定義されている。

  • server_error
  • invalid_request
  • no_suitable_method
  • unauthorized
  • unknown_user
  • unreachable_user
yapooyapoo

User Questioning Polling Endpoint

Polling Request

Pulled-By-Client flow のとき、クライアントは OP が提供するこのエンドポイントを一定の時間間隔で GET リクエストによりポーリングして回答を得る。

このエンドポイントは OAuth 2.0 のアクセストークンで保護されていなければならない。また、クエリパラメータによる以下のリクエストパラメータを要求する。

クエリパラメータ 要否 説明
question_id 必須 User Questioning Request Endpoint で返却された question_id

また、以下のリクエストヘッダを受け付ける。

リクエストヘッダ 要否 説明
Client-Timeout 任意 クライアントが何秒間レスポンスを待つかを指定する

以下はリクエストの例である。

GET /questions_polling?question_id=984dcc7d3d4d4b0f9f8022e344f9 HTTP/1.1
Host: server.example.com
Accept: application/json
Authorization: Bearer SlAV32hkKG 
Client-Timeout: 10

成功レスポンス

成功した場合、OP は User Statement Token を生成してクライアントに返却する。

User Statement Token

User Statement Token は以下のクレームを持つ JWT である。

クレーム 要否 説明
question_id 必須 User Questioning Request Endpoint で返却したもの
iss 必須 OP を示す。OpenID Connect で OP が発行する ID トークンの iss クレームと同じ値
sub 必須 質問の回答したユーザーの識別子。OpenID Connect で OP が発行する ID トークンの sub クレームと同じ値[1]
aud 必須 OpenID Connect で OP が発行する ID トークンの aud クレームと同じ値
user_id User Questioning Request で user_id が指定されている場合は必須。それ以外は禁止。 質問に回答したユーザーの識別子。
user_id_type user_id と同様 User Questioning Request の同じパラメータと同様
question_displayed 必須 ユーザーに表示された質問文。もし User Questioning Request の通りに表示しなかった場合は、実際に表示した質問文とする。
displayed_statements 必須 ユーザーに表示された選択肢。もし User Questioning Request の通りに表示しなかった場合は、実際に表示した選択肢とする。
statement 必須 ユーザーの選択
statement_date 必須 ユーザーが質問に回答した日時を UnixTime で指定する
used_amr 必須 ユーザーが質問に回答する前にユーザーが行った認証方法
used_acr 任意 ユーザーが質問に回答する前にユーザーが行った認証の ACR

レスポンス

OP はレスポンスパラメータ user_statement_token で User Statement Token を返却する。

以下は Section 4.2.4 に記載のレスポンス例。

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: xxx

{
  "user_statement_token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImsyYmRjIn0.eyJxdWVzdGlvbl9pZCI6Ijk4NGRjYzdkM2Q0ZDRiMGY5ZjgwMjJlMzQ0ZjkiLCJpc3MiOiJodHRwczovL3NlcnZlci5leGFtcGxlLmNvbSIsImF1ZCI6WyJzOFYzd204SVhNVzZUYm9helhaWCJdLCJzdWIiOiJnMTE3WUJ0Q1pPM21BS1BRS1A4byIsInVzZXJfaWQiOiIrMzM2MTIzNDU2NzgiLCJ1c2VyX2lkX3R5cGUiOiJtc2lzZG4iLCJxdWVzdGlvbl9kaXNwbGF5ZWQiOiJXaGljaCBpcyAuLi4_IiwiZGlzcGxheWVkX3N0YXRlbWVudHMiOlsiYmx1ZSIsInJlZCIsInllbGxvdyJdLCJzdGF0ZW1lbnQiOiJyZWQiLCJzdGF0ZW1lbnRfZGF0ZSI6MTMxMTI4Mjk3NSwidXNlZF9hY3IiOiIyIiwidXNlZF9hbXIiOlsiQ0xJQ0tfT0siXX0.bbDeU13rouABOOGrvIdQ4BN6GZtHvf1_oZEtGMrMoF9y2l66Dnyv_LL0eip2eCT56N7nH8iACNoJawCG_O2nYfUfJTU8t-tu-MNLe9p69PtUQkxShFzvWXPQfri_OXQSs1zdtZ5NNVUX0ulPLm7QOniNFvY4sHwAkci4V1Czj72FGsxcUsUS6KUJRY7B2znP5ykl7m75iCp0Ydx72NshWCWzQxDfXPdQpuJoB96OXDuuukYIrOY1yZOqkUGE6AqPcmMUJEWmT7OCHNi0Acsr0jU-fwxoAfYxQ641pW203PfVHXVRd06Ly0xHT3b50FM1jnJM4tq98j78zu1cxVc2ww"
}

Pending Response

ユーザーの回答がまだ行われていない場合、OP は ステータスコード 304 Not Modified でレスポンスを返却する。

クライアントは回答が得られるまでリクエストを繰り返すが、前回のリクエストから最低 1 秒以上の間隔をあけることが推奨されている。

エラーレスポンス

エラーレスポンスは後述する。ここでは以下のエラーコードが定義されている。

  • duplicate_requests
  • forbidden
  • high_rate_client
  • high_rate_question
  • server_error
  • invalid_question_id
  • invalid_request
  • timeout
  • unauthorized
  • unreachable_user
  • user_refused_to_answer
脚注
  1. アクセストークンがユーザーに紐づくものだった場合、ここで sub はクライアントの Sector Identifier を用いた pairwise であることを推奨している (public でも良いとは書かれている)。これは OP 側が ID トークンに含める sub のタイプと一致していないとクライアントにとって解釈できない値になってしまうのだが、ここで PPID を推奨しているのはどういう背景なのだろうか。 ↩︎

yapooyapoo

Client Notification Endpoint

このエンドポイントはここまでに述べたものとは異なり、OP ではなくクライアントが提供するエンドポイントである。
Pushed-To-Client flow の場合、OP はユーザーからの回答を得た場合やエラーが発生した場合に、クライアントの提供する本エンドポイントに POST リクエストを行い、回答を通知する。

このフローが用いられる場合、クライアントは OP が本エンドポイントにリクエストする際に用いるアクセストークンを User Questioning Request で client_notification_token リクエストパラメータによって送信している。OP はこのアクセストークンを下記いずれのリクエストの場合にも用いることになる。

回答を得た場合

OP は User Questioning Polling Endpoint の場合と全く同様に User Statement Token を発行し、リクエストパラメータ user_statement_token を用いてクライアントに送信する。

以下は Section 4.3.1 に記載のリクエストの例である。

POST /notification HTTP/1.1
Host: client.example.com
Content-Type: application/json
Authorization: Bearer vO4n2DAeRrcWE0VAlo4I 
Content-Length: xxx

{
  "user_statement_token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImsyYmRjIn0.eyJxdWVzdGlvbl9pZCI6Ijk4NGRjYzdkM2Q0ZDRiMGY5ZjgwMjJlMzQ0ZjkiLCJpc3MiOiJodHRwczovL3NlcnZlci5leGFtcGxlLmNvbSIsImF1ZCI6WyJzOFYzd204SVhNVzZUYm9helhaWCJdLCJzdWIiOiJnMTE3WUJ0Q1pPM21BS1BRS1A4byIsInVzZXJfaWQiOiIrMzM2MTIzNDU2NzgiLCJ1c2VyX2lkX3R5cGUiOiJtc2lzZG4iLCJxdWVzdGlvbl9kaXNwbGF5ZWQiOiJXaGljaCBpcyAuLi4_IiwiZGlzcGxheWVkX3N0YXRlbWVudHMiOlsiYmx1ZSIsInJlZCIsInllbGxvdyJdLCJzdGF0ZW1lbnQiOiJyZWQiLCJzdGF0ZW1lbnRfZGF0ZSI6MTMxMTI4Mjk3NSwidXNlZF9hY3IiOiIyIiwidXNlZF9hbXIiOlsiQ0xJQ0tfT0siXX0.bbDeU13rouABOOGrvIdQ4BN6GZtHvf1_oZEtGMrMoF9y2l66Dnyv_LL0eip2eCT56N7nH8iACNoJawCG_O2nYfUfJTU8t-tu-MNLe9p69PtUQkxShFzvWXPQfri_OXQSs1zdtZ5NNVUX0ulPLm7QOniNFvY4sHwAkci4V1Czj72FGsxcUsUS6KUJRY7B2znP5ykl7m75iCp0Ydx72NshWCWzQxDfXPdQpuJoB96OXDuuukYIrOY1yZOqkUGE6AqPcmMUJEWmT7OCHNi0Acsr0jU-fwxoAfYxQ641pW203PfVHXVRd06Ly0xHT3b50FM1jnJM4tq98j78zu1cxVc2ww"
}

エラーを通知する場合

この場合は後述するエラーレスポンスのレスポンスパラメータと同様のリクエストパラメータを持つ。ただし、追加で question_id が必須となる。

以下は Section 4.3.2 に記載のリクエスト例である。

POST /notification HTTP/1.1
Host: client.example.com
Content-Type: application/json
Authorization: Bearer vO4n2DAeRrcWE0VAlo4I 
Content-Length: xxx

{
  "question_id":"984dcc7d3d4d4b0f9f8022e344f9",
  "error":"unknown_user",
  "error_description":"The user is unknown",
  "error_uri":"https://server.example.com/errors/unknown_user"
}

クライアントによるレスポンス

クライアントはこのリクエストに対してステータスコード 200 OK でレスポンスする。

yapooyapoo

エラーレスポンス

エラーレスポンスは RFC 6749 の Section 5.2 で定義されているものと全く同様である。

加えて、Section 5 でいくつかのエラーコードが新規に定義されている。

yapooyapoo

感想

質問を行うタイミングについて

OP がいつユーザーに対して質問を行うのかは仕様としては定義されていない。

Section 1.6 で本仕様を利用するべき状況の条件の 1 つとして「The Client needs to have a real-time interaction with an End-User that may not be currently using the Client.」とあるので、OP は User Questioning Request を受け付けた後にすぐにユーザーに質問を行う必要があると思われる。

これは push 通知などを用いるイメージだろうか?OP 側のサービスがネイティブアプリで提供されている場合はユーザーがすぐに回答できるかもしれないが、Web ブラウザで提供されるサービスしかない場合にはすぐに回答が得られるケースはあまり無いような気がする。

ユースケースについて

そもそもクライアントのサービスを利用していない状況で、かつ質問にリアルタイムで答えて貰う必要があり、しかもクライアント側の独自の通知などの経由ではなく OP を介して質問に答えさせたいケースというのがイメージできない。一応 Section 1.5 として質問例もあるものの、この点も含めた実装は想像できなかった。