fbpx

Keycloak(14.0.0)で試す OpenID Connect CIBA #keycloak #ciba #oauth #oidc

この記事は1年以上前に投稿されました。情報が古い可能性がありますので、ご注意ください。

1. 目次


  1. 目次
  2. 概要
  3. CIBA(シーバ)とは何か?
  4. 今回試す全体像の説明
  5. 今回使う環境の説明
  6. CIBAを検証するための検証用データの作成・確認
  7. リクエストベースでの解説
  8. 後書き

2. 概要


shiba チームの中村です。今回は6月18日にリリースされた Keycloak14.0.0 を用いてOpenID Connect Client Initiated Backchannel Authentication(CIBA)を試してみました。

CIBAのサポートはKeycloakのバージョン13.0.0から有効化されましたが、現在テクノロジープレビューの状態であり、デフォルト無効であったり、管理画面のGUI上の変更だけでは設定できない項目などがあったので、皆さんへの共有のためにこの記事を作成しました。

今回の記事で説明する箇所

また、今回の記事では主に下記の部分について解説していきます。

  • KeycloakでCIBAを有効にする設定
  • KeycloakでCIBAを試す際の
    • 変更・確認が特に必要な設定
    • 簡易シーケンス図
    • curlコマンドのサンプル 及び 各フィールドの説明

今回の記事で説明しない箇所

下記の内容については詳細な解説は割愛させていただきます。参考になる公式のリンクを貼っておいたので必要に応じて確認お願いします。

3. CIBA(シーバ)とは何か?


CIBAとは、OpenID Connect Client Initiated Backchannel Authentication の略称であり、OpenIDFoundationの MODRNA(Mobile Operator Discovery, Registration & autheNticAtion)のワーキンググループによって作られた仕様です。

認証したいユーザの有効な識別子を取得できるRPが、エンドユーザとの対話(ユーザーのブラウザなどを介しての対話など)を行わずに、ユーザを認証するための対話フローを開始できるようにするための、新しい認証フローです。

この仕様はパブリックレビュー期間が2018年12月14日に開始され、2019年2月4日に Implementer's Draftとして承認されました。

この仕様をざっくり説明すると、下記のような流れを取ります。

  1. クライアントはOPのバックチャネル認証エンドポイントに認証リクエストを送信。
  2. 受信したOPはバックグラウンドでユーザーを認証しようとし、認証を識別する一意の識別子を返却。
  3. クライアントは polling、ping、push のいずれかの方法で、IDトークン、アクセストークン、オプショナルで場合によっては更新トークンを取得。

より詳しい詳細はこちらをご確認ください。

4. 今回試す全体像の説明


次に今回試す全体像をご説明します。

今回はKeycloakでCIBAのポーリングモードを用いて検証します。ポーリングモードで今回試す処理の簡易のシーケンス図が下記になります。

上記のポーリングでやりとりするケースのシーケンスの中で緑色の点線に囲まれた箇所がCIBAに該当する部分で、主に下記の2箇所です。

  • Authentication Request とそのレスポンス
  • Token Request とそのレスポンス

ではそれ以外の下記はなにかというと、Keycloak側の仕様です。

  • Authentication Delegation Requestとそのレスポンス
  • Authentication Result Notification とそのレスポンス

というのもそもそもCIBAはバックエンドの認証を開始する方法は仕様として定義しておりますが、ユーザーをどのように認証するかということは定義しておりません。

また、今回のフローではユーザー認証の部分をスキップし、認証したものとして 認証結果を通知するリクエスト(図での Authentication Result Notification)を送って試します。

勿論よく聞くようなプッシュ通知を送り、確認されたら Authentication Result Notification を送るような実装も考えられます(こちらは別途記事化するかもしれません)。ですが今回の記事では一旦CIBA部分にだけ絞ってお話していきます。

5. 今回使う環境の説明


ではまず今回試した環境について説明していきます。
今回はdocker環境で試しました。keycloakのイメージは jboss/keycloak を用いて、認証用のエンティティとしては仮でRuby + Railsで簡易のAPIを実装していきます。
ここで例となるdocker-composeの設定例を記載します。
version: '3.8'

services:
  keycloak:
    container_name: keycloak
    image: jboss/keycloak:14.0.0
    command: -b 0.0.0.0 -Dkeycloak.profile.feature.ciba=enabled
    ports:
      - "8088:8080"
    volumes:
      - ./docker/keycloak/demo-config/standalone-ha.xml:/opt/jboss/keycloak/standalone/configuration/standalone-ha.xml
    environment:
      KEYCLOAK_USER: admin
      KEYCLOAK_PASSWORD: password
  
  authn-server:
    container_name: authn-server
    build:
      context: ./docker/authn-server
    command: bash -c "rm -f tmp/pids/server.pid && bundle e rails s -p 3000 -b '0.0.0.0'"
    ports:
      - 3000:3000
    volumes:
      - ./docker/authn-server:/my_app

上記のdocker-composeの設定には2点ポイントがあり、これらについて順番に解説していきます。

  1. CIBAを有効化するための設定
  2. Authentication Channel Providerの設定

authn-serverというのは Authentication entityの役目は下記の3つです。

  • keycloak から Authentication Delegation Request という認証を委任するリクエストを受ける。
  • 任意の認証を行う
    ※ 本資料では行いません
  • keycloak に Authentication Result Notification という認証結果を通知するリクエストを送信する。
    ※ 本資料では検証のため、curlでリクエストを送信しますのでこの機能は不要です。

KeycloakのCIBA対応部分を試すためだけならば、極めてシンプルな処理しか要求されないため任意の言語やソフトウェアを使って実装してください。

実装に必要な要件などは後の章の  7. リクエストベースでの解説 の中の ④ Response (③ Authentication Delegation Request のレスポンス) の中で説明します。

CIBAを有効にするための設定

KeycloakではCIBAの機能は現在テクノロジープレビュー状態であり、 デフォルトで無効 となっているため、これを有効化する時は設定が必要です。

この設定を有効にするには2つの方法があります。
-Dkeycloak.profile.feature.ciba=enabled のようにCIBAの設定だけ有効化する
-Dkeycloak.profile=preview のように preview 機能を有効化する

今回のdocker-composeでは上記の方法のうち前者の設定を行っています。

より詳しい詳細はこちらをご確認ください。

Authentication Channel Providerの設定

Keycloakで CIBA を使う時は下記2つのプロパイダーを利用します。

  • Authentication Channel Provider
    KeycloakとADを介して実際に利用者を認証するエンティティとの間のコミュニケーションを提供。
  • User Resolver Provider
    クライアントから提供された情報からKeycloakのUserModelを取得し、ユーザを識別。

Keycloakには両方のデフォルトプロバイダーが準備されていますが、認証チャネルプロバイダーのみ設定する必要があります

公式の設定例を見てみましょう。

<spi name="ciba-auth-channel">
    <default-provider>ciba-http-auth-channel</default-provider>
    <provider name="ciba-http-auth-channel" enabled="true">
        <properties>
            <property name="httpAuthenticationChannelUri" value="https://backend.internal.example.com/auth"/>
        </properties>
    </provider>
</spi>
この設定の中で変更が必要な項目は、httpAuthenticationChannelUri です。この項目はAD(Authentication Device)を介してユーザーを実際に認証するエンティティのURIで、実際に検証を行う場合などはこの値を指定する必要があります。デフォルトではhttps://backend.internal.example.com/auth になっております。

より詳しい詳細はこちらをご確認ください。

ではこの設定をどこで設定するかというと、standalone-ha.xmlを書き換えることで試してみましょう。これは jboss/keycloak のdockerのイメージでは docker-entrypoint.sh の中で、サーバー構成のパラメーターを渡さない場合はデフォルトでは standalone-ha.xml を使用するためです。

参考:

例示したdocker-composeでは下記のように volumes で standalone-ha.xml を書き換えていますが、適宜自分の設定で置き換える用に設定してください。

volumes:
  - ./docker/keycloak/demo-config/standalone-ha.xml:/opt/jboss/keycloak/standalone/configuration/standalone-ha.xml

ではどんな風に書き換えるかというと、下記の <subsystem xmlns="urn:jboss:domain:keycloak-server:1.1">の要素の中に該当の処理を追加していきましょう。

<subsystem xmlns="urn:jboss:domain:keycloak-server:1.1">
    ※多くの処理
</subsystem>

追加する位置についてですが、他のSPIの設定 <spi name="hostname"> の要素の後ろに下記のように追加してみましょう。今回追加した設定は <spi name="ciba-auth-channel">の要素の処理になります。

<spi name="hostname">
    <default-provider>${keycloak.hostname.provider:default}</default-provider>
    <provider name="default" enabled="true">
        <properties>
            <property name="frontendUrl" value="${keycloak.frontendUrl:}"/>
            <property name="forceBackendUrlToFrontendUrl" value="false"/>
        </properties>
    </provider>
    <provider name="fixed" enabled="true">
        <properties>
            <property name="hostname" value="${keycloak.hostname.fixed.hostname:localhost}"/>
            <property name="httpPort" value="${keycloak.hostname.fixed.httpPort:-1}"/>
            <property name="httpsPort" value="${keycloak.hostname.fixed.httpsPort:-1}"/>
            <property name="alwaysHttps" value="${keycloak.hostname.fixed.alwaysHttps:false}"/>
        </properties>
    </provider>
</spi>
<spi name="ciba-auth-channel">
    <default-provider>ciba-http-auth-channel</default-provider>
    <provider name="ciba-http-auth-channel" enabled="true">
        <properties>
            <property name="httpAuthenticationChannelUri" value="http://authn-server:3000/api/v1/auth"/>
        </properties>
    </provider>
</spi>

ですが、この http://authn-server:3000/api/v1/auth はあくまで今回のdocker-composeの設定の場合での一例です。各自の環境に合わせた FQDN や IP、 Port、パスを設定してください。

6. CIBAを検証するための検証用データの作成・確認


CIBAを試すためには検証用の適当なデータが必要です。Keycloakの管理画面にログインして準備していきましょう。

準備が必要な項目は下記になります。

  • レルムの作成
  • ユーザーの作成
  • クライアントの作成
  • シークレットトークンの確認
  • CIBAの設定の確認・変更  (※ この変更はより検証がしやすくするための変更です)

レルムの作成

まずは任意のレルムを作成してください。今回の記事ではレルムの名前は SampleRealm で作成します。

今後の文章ではレルム名の部分を SampleRealm として説明していくので、今後の資料では適宜自分の環境に合わせて読み替えてください。

ユーザーの作成

次にユーザーを作成します。今回の記事では下記の値で作成します。

フィールド名
Username cl-taro

今後の文章ではユーザーの値を上記の値として説明していくので、今後の資料では適宜自分の環境に合わせて読み替えてください。

Usernameは任意の値で問題ありませんが、後々の手順で使用する機会があるため、検証時はわかりやすい名前のほうがよいかもしれません。

クライアントの作成

クライアントを作成します。今回の記事では下記の値で作成します。

フィールド名
Client ID
test-client
Client Protocol
openid-connect ※これはデフォルト値です

備考: Root URL は今回のフローでは登録不要です。

作成後の Test-client の Setting タブで下記の変更を行っていきます。

  1. Access Type のセレクトボックスの値を public から confidential に変更
  2. Standard Flow EnabledON から OFF に変更
    補足: CIBAのフローではリダイレクト用のURIの登録は不要です。ですが、デフォルトだと他のフローのサポートのために、リダイレクト用のURI登録が必要になります。そのため Standard Flow Enabled を意図的に無効にします。例えば、仮に ON のままクライアントを保存しようとすると次のエラーが表示されます。

    Error! You must specify at least one redirect uri

  3. OIDC CIBA Grant Enabledのチェックを OFF から ON に変更

上記の設定が終わり次第 Save からクライアントに対する設定を保存してください。

クライアントのシークレットトークンの確認

Test-client の Credentials タブで Secret の値を確認します。今回の例では仮に 2ae6d81d-bc78-434e-a7b8-985921e0d47b とします。この値は各環境で変わります。今後の資料では適宜自分の環境に合わせて読み替えてください。

Client Authenticator は初期値のままで問題ありません。念の為 Client and Secret であることを確認しておいてください。

CIBAの設定の確認・変更

レルムのAuthenticationのConfigureの画面にCIBA Policy というタブがあると思うので開いてください。

サンプルのようなdockerの設定と、レルム名にしている場合は下記のようなURLで開くこともできます。 http://localhost:8088/auth/admin/master/console/#/realms/SampleRealm/authentication/flows

keycloak-ciba-setting

各項目を簡単に説明していきます。

名前 説明
Backchannel Token Delivery Mode
ライアントがアクセストークンやオプションでリフレッシュトークンなどを取得する方法で。CIBAでは "poll", "ping", "push" の3つがあります。Keycloakは現時点では"poll" のみサポートしています。
Expires In
auth_req_id の有効期限を秒単位で示す、正の整数値です。Keycloakのデフォルト値は "120" です。 この値はCIBAの仕様上必須です。
Interval
クライアントがトークンエンドポイントへのポーリング要求を再度行うまでに待機しなければならない最小時間を、秒単位で示す正の整数値です。指定がない場合のデフォルト値はCIBAの仕様上は5秒です。Keycloakのデフォルトも "5" です。この値はCIBAの仕様上オプショナルです。
Authentication Requested User Hint
認証が要求されているエンドユーザーを識別する方法です。CIBAでは "login_hint", "login_hint_token", "id_token_hint" の3つがあります。 Keycloakは現時点では "login_hint" のみサポートしています。

今回の検証では各リクエストに着目して少しずつ確認していくため、検証途中で auth_req_id の有効期限切れを迎えないようにExpires Inの設定を "600" に変更しておきしょう。

より詳しい詳細はこちらをご確認ください。

7. リクエストベースでの解説


ではもう1度、簡易シーケンス図を見てみましょう。

リクエストとレスポンスに適当な番号(①~⑩)をつけて順に解説していきます。

大まかなかたまりとしては、

① Authentication Request
② Successful Authentication Request Acknowledgement
③ Authentication Delegation Request
④ Response (Authentication Delegation Requestのレスポンス)

①~④は、クライアントがKeycloakに認証を要求する部分です。内部でKeycloakはAutentication entityに認証を委任するリクエスト(②〜③)を送信します。

 

⑤ Token Request
⑥ Token Error Response

⑤~⑥は、認証を終えていない状態でトークンリクエストを行い
トークンエラーレスポンスを受け取る部分です。

 

⑦ Authentication Result Notification
⑧ Response (Authentication Result Notificationのレスポンス)

⑦~⑧は認証の結果をAutentication entityからKeycloakに通知を行う部分です。今回のケースでは⑦で正常に完了したものとして通知を行います。

 

⑨ Token Request
⑩ Successful Token Response

⑨~⑩は認証が正常に完了している状態でトークンリクエストを行う部分です。⑩のレスポンスを⑥のレスポンスと比較して確認してください。

では①番から順にリクエストを見ていくことにしましょう。

① Authentication Request

CIBAのフローはまず認証を要求するリクエストから始まります。

このリクエストは、HTTP POST メソッドを使用し、パラメータを application/x-www-form-urlencoded 形式でHTTPリクエストのBodyに含め送信します。

また、クライアントの認証方式に関しては OpenID Connect Core 1.0 で、client_secret_basic として説明されているような、Basic認証の形式を用いて行っていきます。

この形式は クライアントIDとクライアントシークレットをコロン(:)ではさんで結合しbase64化したものを Authorization ヘッダーに載せます。私の設定の場合は、クライアントIDが test-client で クライアントシークレットが 2ae6d81d-bc78-434e-a7b8-985921e0d47b なので、Macでのコマンドの一例だと下記のようになります。

$ echo -n 'test-client:2ae6d81d-bc78-434e-a7b8-985921e0d47b' | openssl base64

dGVzdC1jbGllbnQ6MmFlNmQ4MWQtYmM3OC00MzRlLWE3YjgtOTg1OTIxZTBkNDdi

 

今回使うヘッダーは下記になります。

ヘッダー名 説明
Content-Type
application/x-www-form-urlencoded
Authorization
Basic bXljbGllbnQ6NDQ4N2JkYzgtZTQ1ZS00OGMyLTg2MzUtY2IyM2NiNWUyZjYz
client_secret_basic 形式時の渡し方です。

 

Bodyに含ませる値は下記になります。

名前 説明
scope
openid
アクセスを要求している範囲です。この値は必須です。
login_hint
cl-taro
認証が要求されているエンドユーザーを特定するための、OpenIDプロバイダーへのヒントになる情報です。
Keycloakのデフォルトでは、これはユーザーのユーザー名になります。
CIBAの仕様としては login_hint_token, id_token_hint, login_hint のうち1つは必須になります。
binding_message
test message 0001
CD(consumption device) と AD(authentication device) の両方に表示されることを意図した人間が読める識別子またはメッセージなどです。
この値により、エンドユーザーは、AD(authentication device)で行われたアクションがCD(consumption device)で開始された要求に関連していることを確認することができるため、CIBAの仕様としてはこの値はオプショナルですが、多くの場合必要だと個人的には考えております。

またリクエストURLには レルム名 を含んでおります。各自作成したレルム名で読み替えてください。http://localhost:8088/auth/realms/{レルム名}/protocol/openid-connect/ext/ciba/auth

より詳しい詳細はこちらをご確認ください。

サンプルの場合はこのようになります。http://localhost:8088/auth/realms/SampleRealm/protocol/openid-connect/ext/ciba/auth

サンプルの CURL文としては下記になります。
curl -X POST \
   -H "Content-Type:application/x-www-form-urlencoded" \
   -H "Authorization:Basic dGVzdC1jbGllbnQ6MmFlNmQ4MWQtYmM3OC00MzRlLWE3YjgtOTg1OTIxZTBkNDdi" \
   -d "scope=openid%20email%20example-scope" \
   -d "login_hint=cl-taro" \
   -d "binding_message=test message 0001" \
 'http://localhost:8088/auth/realms/SampleRealm/protocol/openid-connect/ext/ciba/auth'

② Successful Authentication Request Acknowledgement

①の Authentication Request のレスポンス(②)は application/json 形式で送られてきます。レスポンスを整形すると下記のようになります。

{
    "auth_req_id": "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0..iazuIbFU1q16EjLHwBI-aQ.RAnmpznLSmM3UF75kK59-5cTVBlrSbXDXJQqZjQg9FScQSh_12UhxP5XExEDaDq_0m2viZ_mGFs7O0wq_LQ1sfxAd9uJd2UpD2p8XWlrPyl16FE6evHRjDGCGipvDkjTeBFlrTTapGcvPmxnSH_uu8XJeY_tGSWSSg-WxSAnDYB9FvjkfJDsfFQdU2_sOI2LBRnQlCuJBAdEkRhyQNQliSCE9cy9Zo9MWmCrVcDBft0_pYa3DIbfHxFs6ITqlb6_B9zEIVWZJsq2mcj9utTRxsR4B7fOJ4gP-e6MmmoIgNsTWG_HHkv-dwo9_rhCkSCIqpjvXrSkDccTz3BQvYD1xzxeuaod-2L31wp3donFonEnI5FPwb0BTwMPZ0rH_f6Dmee_RW9cBIGfBz3wGT7K9nHDU1OcyQdZtRqUuzR6GSnjFjNVRy_zry-0ujs3QgWS2rNHlQQqkQAXY6qpncVBSAkGGCf8dlgU3S31152MKtles3LVhKFUETybNhPsWFIqpkIPPZ0Y5qWksMGsEyNu49LIM1eqpoV2jTPAg9re7Cx9tdZr_XteuqREIZchJmYuEMmhk6H-_lTa9AfsL1db3pLpL-PwOZltmwcHOUwEcpIjqr91hZ-CdgzQvhNOJHfJyBRxpreHqDxIs7tBR8VGwFmtKiLKOu-mGuFnaICFwFHA1DgIFSpN9Tret4CoFqWjF50XTzjYkwKrqCQYIul7BWENDWcDwLA1JE5dU5LOZBn1HykSwerQceOlX9wcktgO9ajP9au9twggSlyy1rWSm0FgIBGULeUxgmD6hroyLIlZYsUYnOH2Ur2W9fIq0j7PAvznq5JC2OWWB4OtkeANeSxvEzle8M1Y-97n91M3tXogUdOQccy9ahUD15lzpSUoHFNpajr1IXVLTleqCpI-Gw.mfS4kUF5fPO4ZTCQNL3CvQ",
    "expires_in": 600,
    "interval": 5
}

このJSON形式のデータを簡単に説明していくと、

キー名 説明
auth_req_id
認証を要求するリクエストを識別するための一意の識別子です。この値はCIBAの仕様上必須で返ってきます。
expires_in
auth_req_id の有効期限を秒単位で示す、正の整数値です。この値はCIBAの仕様上必須で返ってきます。
interval
クライアントがトークンエンドポイントへのポーリング要求を再度行うまでに待機しなければならない最小時間を、秒単位で示す正の整数値です。指定がない場合のデフォルト値はCIBAの仕様上は5秒です。この値はCIBAの仕様上オプショナルです。

auth_req_id は 後の ⑤ Token Request のリクエストに使いますので、任意の媒体にメモしておいてください。

 

より詳しい詳細はこちらをご確認ください。

③ Authentication Delegation Request

KeycloakにCIBAの仕様に沿った ①のAuthentication Request のリクエストが届くと、KeycloakはAuthentication entityに認証を委任するリクエスト(Authentication Delegation Request)をHTTP POST メソッドを使用して送ります。

このリクエストのヘッダーは下記になります。

ヘッダー名 説明
Content-Type
application/x-www-form-urlencoded
Authorization
Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJVZW9OQjYxbGt3ZGhJX1ctOU02S2haUksxWFpKdHdjd0pMQ3dJVFA1dTd3In0.eyJleHAiOjE2MjY3MTQzNTAsImp0aSI6IjNiYzRmNTkwLTQ0MmMtNDQ0ZS04YWU0LWRhODcxYmM5MmFkOCIsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4OC9hdXRoL3JlYWxtcy9TYW1wbGVSZWFsbSIsImF1ZCI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4OC9hdXRoL3JlYWxtcy9TYW1wbGVSZWFsbSIsInN1YiI6ImM4Zjg4YzliLTA2YTUtNDgwNy1iZjJmLTYxMzVjNjBhMDY4NiIsInR5cCI6IkJlYXJlciIsImF6cCI6InRlc3QtY2xpZW50In0.ivP4wN8wzLuOpmj6GaUxulmLxvuiXKvAQAl2ifBklm6iVJtJNiCZ24pcTw0GrE7SjBrn6SblO2eAscb1gQyIQGWZohbav0KZbhL37ZDlqK9ZYRNE0nWHYspBsB0kVR-Svj58BrGOvCuotmNrz5nPOgYjvlUl5V_d7mSxyYGUZ-3IBFsOvqo_aqKD2336ygjx_TDFTZ1LLjFA7cUAE6iLnFYPmFexYJrls7jvUI4OiJyIYMq3okIYhteZjeZSQKmz3PeMkSUl3W6qe4QoI7245Y7a862QbQOG7wqzJouYLwJVrzqtjbluWZMToy3A-oOEJk5HtAC59YMgMRUOGU9unA
この値は都度違う識別子で Authentication entity が認証の結果をKeycloakに通知するリクエストを行うときに使用します。

Bodyに含ませる値は下記になります。

名前 説明
scope
openid profile roles email
Authentication entityが認証後にユーザーに同意を求める範囲です。この値は必須です。
login_hint
cl-taro
認証が要求されているエンドユーザーを特定するための、Authentication entityへのヒントになる情報です。
Keycloakのデフォルトでは、これはユーザーの ユーザー名 になります。
is_consent_required
false
Authentication entityが、scopeについて認証されたユーザーから同意を得る必要があるかどうかを示す値です。
binding_message
test message 0001
①のAuthentication Requestに含めた値と同じで、CD(consumption device) と AD(authentication device) の両方に表示されることを意図した人間が読める識別子またはメッセージなどです。

このリクエストは、Authentication Channel Providerの設定で登録したURLにリクエストを送信します。例えば、今回は下記のように設定を記載したので、 http://authn-server:3000/api/v1/auth になります。

<spi name="ciba-auth-channel">
    <default-provider>ciba-http-auth-channel</default-provider>
    <provider name="ciba-http-auth-channel" enabled="true">
        <properties>
            <property name="httpAuthenticationChannelUri" value="http://authn-server:3000/api/v1/auth"/>
        </properties>
    </provider>
</spi>

 

④ Response (③ Authentication Delegation Request のレスポンス)

Authentication Delegation Request のレスポンス(④)として必要な用件は、 HTTP status code として 201 を返却することだけです。

ただし、Authentication entityの実装としては ③ Authentication Delegation Request で受け取った Authorization ヘッダーの値を、後の ⑦ Authentication Result Notification のリクエストに使いますので、任意の媒体に記録しておいてください。

⑤ Token Request

このリクエストはトークンを要求するリクエストです。
CIBAの仕様では、トークンをやりとりする方法として下記の3パターンがあります。

  • Poll Mode
  • Ping Mode
  • Push Mode

ただし、Keycloak は poll のみに対応しており、デフォルトも poll です。また、このポーリングの処理は ① Authentication Request の レスポンス(②) にある、 interval のタイミングより短い間隔でポーリングを行うとエラーが返ってくるため注意が必要です。

このリクエストのヘッダーは下記になります。

ヘッダー名 説明
Content-Type
application/x-www-form-urlencoded
Authorization
Basic bXljbGllbnQ6NDQ4N2JkYzgtZTQ1ZS00OGMyLTg2MzUtY2IyM2NiNWUyZjYz
client_secret_basic 形式時の渡し方です。①のリクエストで設定した Authorization ヘッダーの値と同じ値です。

Bodyに含ませる値は下記になります。

名前 説明
grant_type
urn:openid:params:grant-type:ciba
この値は必須で、urn:openid:params:grant-type:ciba である必要があります
auth_req_id
eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0..iazuIbFU1q16EjLHwBI-aQ.RAnmpznLSmM3UF75kK59-5cTVBlrSbXDXJQqZjQg9FScQSh_12UhxP5XExEDaDq_0m2viZ_mGFs7O0wq_LQ1sfxAd9uJd2UpD2p8XWlrPyl16FE6evHRjDGCGipvDkjTeBFlrTTapGcvPmxnSH_uu8XJeY_tGSWSSg-WxSAnDYB9FvjkfJDsfFQdU2_sOI2LBRnQlCuJBAdEkRhyQNQliSCE9cy9Zo9MWmCrVcDBft0_pYa3DIbfHxFs6ITqlb6_B9zEIVWZJsq2mcj9utTRxsR4B7fOJ4gP-e6MmmoIgNsTWG_HHkv-dwo9_rhCkSCIqpjvXrSkDccTz3BQvYD1xzxeuaod-2L31wp3donFonEnI5FPwb0BTwMPZ0rH_f6Dmee_RW9cBIGfBz3wGT7K9nHDU1OcyQdZtRqUuzR6GSnjFjNVRy_zry-0ujs3QgWS2rNHlQQqkQAXY6qpncVBSAkGGCf8dlgU3S31152MKtles3LVhKFUETybNhPsWFIqpkIPPZ0Y5qWksMGsEyNu49LIM1eqpoV2jTPAg9re7Cx9tdZr_XteuqREIZchJmYuEMmhk6H-_lTa9AfsL1db3pLpL-PwOZltmwcHOUwEcpIjqr91hZ-CdgzQvhNOJHfJyBRxpreHqDxIs7tBR8VGwFmtKiLKOu-mGuFnaICFwFHA1DgIFSpN9Tret4CoFqWjF50XTzjYkwKrqCQYIul7BWENDWcDwLA1JE5dU5LOZBn1HykSwerQceOlX9wcktgO9ajP9au9twggSlyy1rWSm0FgIBGULeUxgmD6hroyLIlZYsUYnOH2Ur2W9fIq0j7PAvznq5JC2OWWB4OtkeANeSxvEzle8M1Y-97n91M3tXogUdOQccy9ahUD15lzpSUoHFNpajr1IXVLTleqCpI-Gw.mfS4kUF5fPO4ZTCQNL3CvQ
認証を要求するリクエストを識別するための一意の識別子です。具体値としては① Authentication Requestのレスポンスである②に含まれている auth_req_id の値です。この値は必須です。

サンプルの CURL文としては下記になります。

curl -X POST \
   -H "Content-Type:application/x-www-form-urlencoded" \
   -H "Authorization:Basic dGVzdC1jbGllbnQ6MmFlNmQ4MWQtYmM3OC00MzRlLWE3YjgtOTg1OTIxZTBkNDdi" \
   -d "grant_type=urn:openid:params:grant-type:ciba" \
   -d "auth_req_id=eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0..iazuIbFU1q16EjLHwBI-aQ.RAnmpznLSmM3UF75kK59-5cTVBlrSbXDXJQqZjQg9FScQSh_12UhxP5XExEDaDq_0m2viZ_mGFs7O0wq_LQ1sfxAd9uJd2UpD2p8XWlrPyl16FE6evHRjDGCGipvDkjTeBFlrTTapGcvPmxnSH_uu8XJeY_tGSWSSg-WxSAnDYB9FvjkfJDsfFQdU2_sOI2LBRnQlCuJBAdEkRhyQNQliSCE9cy9Zo9MWmCrVcDBft0_pYa3DIbfHxFs6ITqlb6_B9zEIVWZJsq2mcj9utTRxsR4B7fOJ4gP-e6MmmoIgNsTWG_HHkv-dwo9_rhCkSCIqpjvXrSkDccTz3BQvYD1xzxeuaod-2L31wp3donFonEnI5FPwb0BTwMPZ0rH_f6Dmee_RW9cBIGfBz3wGT7K9nHDU1OcyQdZtRqUuzR6GSnjFjNVRy_zry-0ujs3QgWS2rNHlQQqkQAXY6qpncVBSAkGGCf8dlgU3S31152MKtles3LVhKFUETybNhPsWFIqpkIPPZ0Y5qWksMGsEyNu49LIM1eqpoV2jTPAg9re7Cx9tdZr_XteuqREIZchJmYuEMmhk6H-_lTa9AfsL1db3pLpL-PwOZltmwcHOUwEcpIjqr91hZ-CdgzQvhNOJHfJyBRxpreHqDxIs7tBR8VGwFmtKiLKOu-mGuFnaICFwFHA1DgIFSpN9Tret4CoFqWjF50XTzjYkwKrqCQYIul7BWENDWcDwLA1JE5dU5LOZBn1HykSwerQceOlX9wcktgO9ajP9au9twggSlyy1rWSm0FgIBGULeUxgmD6hroyLIlZYsUYnOH2Ur2W9fIq0j7PAvznq5JC2OWWB4OtkeANeSxvEzle8M1Y-97n91M3tXogUdOQccy9ahUD15lzpSUoHFNpajr1IXVLTleqCpI-Gw.mfS4kUF5fPO4ZTCQNL3CvQ" \
 'http://localhost:8088/auth/realms/SampleRealm/protocol/openid-connect/token'

より詳しい詳細はこちらをご確認ください。

⑥ Token Error Response

この資料では Token Request を認証されていない状態で送ったケースと、認証が正常に完了している状態で送ったケースでそれぞれの状態のレスポンスを紹介していきます。

⑥はまず認証されていないケースのレスポンスです。レスポンスはHTTPステータスコード400で application/json 形式で送られてきます。レスポンスを整形すると下記のようになります。 今回のケースでは認証が済んでいないので、認証要求が保留となっているという情報が含まれています。

{
    "error": "authorization_pending",
    "error_description": "The authorization request is still pending as the end-user hasn't yet been authenticated."
}

このJSON形式のデータを簡単に説明していくと下記のようになります。

キー名 説明
error
エラーコード。必須。
error_description
クライアントが発生したエラーを理解するのに役立つ追加情報。オプショナル。

今回のエラーは、エンドユーザーがまだ認証されていないため、認証要求はまだペンディングされていることを示しています。

より詳しい詳細はこちらをご確認ください。

⑦ Authentication Result Notification

このリクエストは 認証結果をAuthentication entity から Keycloak に通知するリクエストです。

このリクエストのヘッダーは下記になります。

ヘッダー名 説明
Content-Type
application/json
Authorization
Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJVZW9OQjYxbGt3ZGhJX1ctOU02S2haUksxWFpKdHdjd0pMQ3dJVFA1dTd3In0.eyJleHAiOjE2MjY3MTQzNTAsImp0aSI6IjNiYzRmNTkwLTQ0MmMtNDQ0ZS04YWU0LWRhODcxYmM5MmFkOCIsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4OC9hdXRoL3JlYWxtcy9TYW1wbGVSZWFsbSIsImF1ZCI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4OC9hdXRoL3JlYWxtcy9TYW1wbGVSZWFsbSIsInN1YiI6ImM4Zjg4YzliLTA2YTUtNDgwNy1iZjJmLTYxMzVjNjBhMDY4NiIsInR5cCI6IkJlYXJlciIsImF6cCI6InRlc3QtY2xpZW50In0.ivP4wN8wzLuOpmj6GaUxulmLxvuiXKvAQAl2ifBklm6iVJtJNiCZ24pcTw0GrE7SjBrn6SblO2eAscb1gQyIQGWZohbav0KZbhL37ZDlqK9ZYRNE0nWHYspBsB0kVR-Svj58BrGOvCuotmNrz5nPOgYjvlUl5V_d7mSxyYGUZ-3IBFsOvqo_aqKD2336ygjx_TDFTZ1LLjFA7cUAE6iLnFYPmFexYJrls7jvUI4OiJyIYMq3okIYhteZjeZSQKmz3PeMkSUl3W6qe4QoI7245Y7a862QbQOG7wqzJouYLwJVrzqtjbluWZMToy3A-oOEJk5HtAC59YMgMRUOGU9unA
この値は③ Authentication Delegation Requestで送信されてきた Authorizationヘッダーと同じ値を使います
Bodyに含ませるJSONは下記になります。
キー名 説明
status
SUCCEED
必須。この値は SUCCEED (認証が正常に完了), UNAUTHORIZED (認証が完了していない), CANCELLED(認証がユーザーによってキャンセルされた) のいずれかである必要があります。
このリクエストはAuthentication entity から送信するものですが、今回は代わりにコンソールから送ってみます。サンプルの CURL文としては下記になります。
curl -i -X POST \
   -H "Content-Type:application/json" \
   -H "Authorization:Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJVZW9OQjYxbGt3ZGhJX1ctOU02S2haUksxWFpKdHdjd0pMQ3dJVFA1dTd3In0.eyJleHAiOjE2MjY3MTQzNTAsImp0aSI6IjNiYzRmNTkwLTQ0MmMtNDQ0ZS04YWU0LWRhODcxYmM5MmFkOCIsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4OC9hdXRoL3JlYWxtcy9TYW1wbGVSZWFsbSIsImF1ZCI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4OC9hdXRoL3JlYWxtcy9TYW1wbGVSZWFsbSIsInN1YiI6ImM4Zjg4YzliLTA2YTUtNDgwNy1iZjJmLTYxMzVjNjBhMDY4NiIsInR5cCI6IkJlYXJlciIsImF6cCI6InRlc3QtY2xpZW50In0.ivP4wN8wzLuOpmj6GaUxulmLxvuiXKvAQAl2ifBklm6iVJtJNiCZ24pcTw0GrE7SjBrn6SblO2eAscb1gQyIQGWZohbav0KZbhL37ZDlqK9ZYRNE0nWHYspBsB0kVR-Svj58BrGOvCuotmNrz5nPOgYjvlUl5V_d7mSxyYGUZ-3IBFsOvqo_aqKD2336ygjx_TDFTZ1LLjFA7cUAE6iLnFYPmFexYJrls7jvUI4OiJyIYMq3okIYhteZjeZSQKmz3PeMkSUl3W6qe4QoI7245Y7a862QbQOG7wqzJouYLwJVrzqtjbluWZMToy3A-oOEJk5HtAC59YMgMRUOGU9unA" \
   -d \
'{
  "status":"SUCCEED"
}' \
 'http://localhost:8088/auth/realms/SampleRealm/protocol/openid-connect/ext/ciba/auth/callback'

このリクエストは認証が正常に完了したということを伝えています。

また、Authorizationヘッダーが なぜ ① Authentication Request や ⑤ Token Request に用いる値と違うのか、と気になる方も一部居られるかもしれませんが、リクエスト元・リクエスト先・用途 を思い出してください。基本的なイメージとしては 誰が 何を識別・認証したいのか によって決めるものだと考えてください。
リクエスト元 リクエスト先 Authorizationヘッダーの用途
① Authentication Request クライアント
(今回は任意のコンソール)
Keycloak クライアント を認証するため
③ Authentication Delegation Request Keycloak Authentication entity Authentication entity を識別するため
⑤ Token Request クライアント
(今回は任意のコンソール)
Keycloak クライアント を認証するため
⑦ Authentication Result Notification Authentication entity Keycloak Authentication entity を認証するため

 

⑧ Response (⑦ Authentication Result Notification)のレスポンス

レスポンスはHTTPステータスコード200で application/json 形式で送られてきます。レスポンスを整形すると下記のようになります。この値をCIBAフローにおいて意識しておく必要が基本的にないため解説を割愛いたします。

{
    "type": "application",
    "subtype": "json",
    "parameters": {},
    "wildcardType": false,
    "wildcardSubtype": false
}

 

⑨ Token Request

このトークンリクエストは⑤で行ったものと全く同じものです。違うのは今回は認証が成功している状態で送っているということのみです。

サンプルの CURL文としては下記になります。

curl -X POST \
   -H "Content-Type:application/x-www-form-urlencoded" \
   -H "Authorization:Basic dGVzdC1jbGllbnQ6MmFlNmQ4MWQtYmM3OC00MzRlLWE3YjgtOTg1OTIxZTBkNDdi" \
   -d "grant_type=urn:openid:params:grant-type:ciba" \
   -d "auth_req_id=eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0..iazuIbFU1q16EjLHwBI-aQ.RAnmpznLSmM3UF75kK59-5cTVBlrSbXDXJQqZjQg9FScQSh_12UhxP5XExEDaDq_0m2viZ_mGFs7O0wq_LQ1sfxAd9uJd2UpD2p8XWlrPyl16FE6evHRjDGCGipvDkjTeBFlrTTapGcvPmxnSH_uu8XJeY_tGSWSSg-WxSAnDYB9FvjkfJDsfFQdU2_sOI2LBRnQlCuJBAdEkRhyQNQliSCE9cy9Zo9MWmCrVcDBft0_pYa3DIbfHxFs6ITqlb6_B9zEIVWZJsq2mcj9utTRxsR4B7fOJ4gP-e6MmmoIgNsTWG_HHkv-dwo9_rhCkSCIqpjvXrSkDccTz3BQvYD1xzxeuaod-2L31wp3donFonEnI5FPwb0BTwMPZ0rH_f6Dmee_RW9cBIGfBz3wGT7K9nHDU1OcyQdZtRqUuzR6GSnjFjNVRy_zry-0ujs3QgWS2rNHlQQqkQAXY6qpncVBSAkGGCf8dlgU3S31152MKtles3LVhKFUETybNhPsWFIqpkIPPZ0Y5qWksMGsEyNu49LIM1eqpoV2jTPAg9re7Cx9tdZr_XteuqREIZchJmYuEMmhk6H-_lTa9AfsL1db3pLpL-PwOZltmwcHOUwEcpIjqr91hZ-CdgzQvhNOJHfJyBRxpreHqDxIs7tBR8VGwFmtKiLKOu-mGuFnaICFwFHA1DgIFSpN9Tret4CoFqWjF50XTzjYkwKrqCQYIul7BWENDWcDwLA1JE5dU5LOZBn1HykSwerQceOlX9wcktgO9ajP9au9twggSlyy1rWSm0FgIBGULeUxgmD6hroyLIlZYsUYnOH2Ur2W9fIq0j7PAvznq5JC2OWWB4OtkeANeSxvEzle8M1Y-97n91M3tXogUdOQccy9ahUD15lzpSUoHFNpajr1IXVLTleqCpI-Gw.mfS4kUF5fPO4ZTCQNL3CvQ" \
 'http://localhost:8088/auth/realms/SampleRealm/protocol/openid-connect/token'

⑩ Successful Token Response

認証が正常に完了している場合のトークンレスポンスを見てみましょう。

レスポンスはHTTPステータスコード200で application/json 形式で送られてきます。レスポンスを整形すると下記のようになります。このレスポンスでクライアントがアクセストークンやリフレッシュトークンを手に入れることができました。

{
    "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJVZW9OQjYxbGt3ZGhJX1ctOU02S2haUksxWFpKdHdjd0pMQ3dJVFA1dTd3In0.eyJleHAiOjE2MjY3MTc0MjgsImlhdCI6MTYyNjcxNzEyOCwianRpIjoiOWQ4Y2JlOTQtZTMxYy00ZGQ5LWJkYmItM2E4ZTQ4Y2Q5YTUzIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDg4L2F1dGgvcmVhbG1zL1NhbXBsZVJlYWxtIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6ImM4Zjg4YzliLTA2YTUtNDgwNy1iZjJmLTYxMzVjNjBhMDY4NiIsInR5cCI6IkJlYXJlciIsImF6cCI6InRlc3QtY2xpZW50Iiwic2Vzc2lvbl9zdGF0ZSI6IjAzMDQ1MzBkLTYyYjUtNDgzYi05ZTBmLTk4NTNkY2VjNWRiOSIsImFjciI6IjEiLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsib2ZmbGluZV9hY2Nlc3MiLCJ1bWFfYXV0aG9yaXphdGlvbiIsImRlZmF1bHQtcm9sZXMtc2FtcGxlcmVhbG0iXX0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6ImVtYWlsIHByb2ZpbGUiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsIm5hbWUiOiJ0YXJvIGNyZWF0aW9ubGluZSIsInByZWZlcnJlZF91c2VybmFtZSI6ImNsLXRhcm8iLCJnaXZlbl9uYW1lIjoidGFybyIsImZhbWlseV9uYW1lIjoiY3JlYXRpb25saW5lIiwiZW1haWwiOiJjcmVhdGlvbmxpbmVAZXhhbXBsZS5jb20ifQ.JEt-YEqNJ5wx343zBB2tz4ImwxPjzLfjsYIW-jrZIkjGcdYB5T_2vEMSNJT8J2Os3wy5MWwF0ZreQM8KTHkBncIDFd6Pt9lpNI5nM1ROmIixmJ9JtSOl9oGrcN2yH2lS9mMIbZg4STlh5ULLywHah9Qm6nO5nWjrCYtPkG-EF7SMvec53_K363lguiqKuN_aqjfdLgvDdEvrAajbQ5ZYwq72-K9xP_frtt1IMeLEONJeLB_xO3sgwmpoCzTUZvuCGXIP9wrpxQ6o0fqFtJyOBWPlNb1YxAvFTyZtlxrdjfWBPp9xcys5a8xfJgr1cG3MuLiaADQOkYnE14wsJEZ9UQ",
    "expires_in": 300,
    "refresh_expires_in": 1800,
    "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI5YmNlY2RiNC0xZDNjLTQ4MDAtYjdlOS03NjEzYmMwNDVhNzMifQ.eyJleHAiOjE2MjY3MTg5MjgsImlhdCI6MTYyNjcxNzEyOCwianRpIjoiMTU2YmRiOGItZmU4ZS00NjIxLTgzZDAtZTM2ZDRjZWJjYjhmIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDg4L2F1dGgvcmVhbG1zL1NhbXBsZVJlYWxtIiwiYXVkIjoiaHR0cDovL2xvY2FsaG9zdDo4MDg4L2F1dGgvcmVhbG1zL1NhbXBsZVJlYWxtIiwic3ViIjoiYzhmODhjOWItMDZhNS00ODA3LWJmMmYtNjEzNWM2MGEwNjg2IiwidHlwIjoiUmVmcmVzaCIsImF6cCI6InRlc3QtY2xpZW50Iiwic2Vzc2lvbl9zdGF0ZSI6IjAzMDQ1MzBkLTYyYjUtNDgzYi05ZTBmLTk4NTNkY2VjNWRiOSIsInNjb3BlIjoiZW1haWwgcHJvZmlsZSJ9.8tllRuyakDap2Ob6IVMbvjLWBqLZel3m7AHoglKtzCc",
    "token_type": "Bearer",
    "not-before-policy": 0,
    "session_state": "0304530d-62b5-483b-9e0f-9853dcec5db9",
    "scope": "email profile"
}

より詳しい詳細はこちらをご確認ください。

8. 後書き

今回はKeycloak14.0.0 を用いてCIBAを試してみました。この資料を読むことで、CIBAの対応範囲や、KeycloakでCIBAを実装する時の勘所、処理の流れのイメージを掴むことに対して少しでも参考になれば幸いです。

この資料にはユーザーを認証する部分(CIBAの仕様外)の所を含めておりません。機会があればプッシュ通知などを含めた汎用的なフローも簡単に検証・解説できればと思っております。

新規CTA