OpenID Connect User Questioning API 1.0

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 は質問の回答を得たらクライアントの特定のエンドポイントにその回答を送信する)
以下に主な処理フローを示す。

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

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
-
アクセストークンがユーザーに紐づくものだった場合、ここで
sub
はクライアントの Sector Identifier を用いた pairwise であることを推奨している (public でも良いとは書かれている)。これは OP 側が ID トークンに含めるsub
のタイプと一致していないとクライアントにとって解釈できない値になってしまうのだが、ここで PPID を推奨しているのはどういう背景なのだろうか。 ↩︎

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 でレスポンスする。

感想
質問を行うタイミングについて
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 として質問例もあるものの、この点も含めた実装は想像できなかった。