OAuth/OpenID Connect の CIBA (Client Initiated Backchannel Authentication)ってなに
3/28(木)に開催された「OAuth & OIDC 勉強会(FAPI & CIBA 特集!) 」へ行ってきました。
主催はAuthlete社という、OAuthやOpenID Connect(以下OIDC)の認可サーバを構築できるBaasを提供する企業です。そのAuthlete社のクラウドサービスが、OAuth/OIDCの新仕様である CIBAに対応したということで、そのクラウドサービスの紹介もかねた勉強会でした。
わたくしOAuthはともかく、CIBAは初見だったので、むちゃくちゃ勉強になりました。勉強会を主催されたAuthlete社の方々、ありがとうございました。
今回はFAPI(Financial-grade API) とCIBA(Client Initiated Backchannel Authentication) についてだったのですが、FAPIに関しては「金融業界などのよりセキュリティ要件が高いところでOAuth/OIDCを使うときの制約事項」が仕様化されたモノと考えればよさそう。わかりやすい例だと、トークンを取るときのOAuth Clientのクライアントの認証はBasic認証はダメでX.509の証明書を用意してね、などなど。。
またCIBAとは、OpenID Connect Client Initiated Backchannel Authentication Flow - Core 1.0 のことで、いままでのOAuth/OIDC(いわゆるRFC 6749)が WEBブラウザのリダイレクトを用いる前提の処理シーケンスだったのに対し1、CIBAではバックチャネル認証エンドポイントに認証リクエストを送ると、事前に登録していたユーザの認証デバイスに「同意する?」って通知がとぶという処理シーケンスになってるのが特徴です。。
これによってたとえば、
- Alexaに「xxxを買うから¥3,000払っといて!」というと、Alexaアプリが認可サーバに認証リクエストを送り
- 認可サーバは「許可する?」って通知を認証デバイスに(スマホアプリ)に通知して
- ユーザがスマホでOKを押せば、決済が完了する
なんていう、ブラウザのリダイレクトが不要な処理シーケンスが実現できるというわけです。うーん、なるほど便利です。
具体的な処理シーケンスは、だいたいこんな感じになりそうですね。。
イントロ
さて以前、とある開発案件でAuthleteサービスをさわれる機会がありまして、そのときの構築手順をHelloWorld程度ですがQiita に投稿しました。
今回はラッキーにもCIBA対応したAuthleteサービスをさわる機会をいただきましたので、備忘として構築手順を記録しておきます。(感謝です!)
前提知識
下記の構築手順を読む上での前提知識ですが、ある程度OAuth/OIDC とCIBAをしってる前提で話を進めます。たとえば、Authorization Code Grant Flow やCIBA Flowの処理シーケンスをだいたい知ってる、などです。
さて下記は、Authorization Code Grant Flow いわゆる OAuth Danceな処理シーケンスです。Authleteの位置づけも一緒に書いています。
- Authorization Code Grant Flow の処理シーケンス
今回はCIBA対応されたAuthleteをさわるってことで、下記のような(さきほど載せた) CIBA Flowの処理シーケンスを実装してアクセストークンを取得するところまでをやってみます。認証デバイスについては、認証デバイスのシミュレータサイトがあるので、そのサイトを使用します。
- CIBA Flowの処理シーケンス
また「ユーザが認証デバイスでオペした結果(認可・拒否・タイムアウトetc.)」をOAuth Clientが知る方法としてpoll,pingもしくはpushという3つのモードがあるのですが、今回は「poll」を使います。このモードはOAuth Clientが認可サーバへポーリングすることで、ユーザのオペ結果を知るという方法です。ping,pushはまだ試してないのですが、pingの場合は認可サーバから「取りに来てイイよ」っていうHTTPSでの通知が来る、pushはそのままアクセストークンが送られてくるようですね。(参考: 9. Client Notification Endpoint )
前提環境
今回構築する環境まわりは以下の通りです。
$ sw_vers
ProductName: Mac OS X
ProductVersion: 10.14.4
BuildVersion: 18E226
$ java -version
java version "1.8.0_91"
Java(TM) SE Runtime Environment (build 1.8.0_91-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.91-b14, mixed mode)
$ mvn --version
Apache Maven 3.6.0 (97c98ec64a1fdfee7767ce5ffb20918da4f719f3; 2018-10-25T03:41:47+09:00)
$
$ curl --version
curl 7.54.0 (x86_64-apple-darwin18.0) libcurl/7.54.0 LibreSSL/2.6.5 zlib/1.2.11 nghttp2/1.24.1
$
$ jq --version
jq-1.6
$
上記でやってますが、mavenとcurl, jq (とgit)が動けば、OS含めなんでもOKだと思います。
準備
また、各種サーバなど登場人物ですが、前回記事では
用途 | サーバ名 | URL |
---|---|---|
認可サーバ。ユーザIDごとの認可情報を管理するサーバ。 | java-oauth-server | http://oauth.example.com:8080/ |
リソースサーバ。ユーザIDごとのデータや機能をもったサーバ。 | java-resource-server | http://resource.example.com:8081/ |
Webアプリケーション。リソースサーバのリソースを使用するWebアプリ | java-oauth-client | http://client.example.com:8082/ |
としていたのですが、今回は、アクセストークン(やid_token)を取得するまでの記事にしているので、リソースサーバは省略、またOAuth Clientとしてcurlコマンドを使用します。従って、下記のようなシンプルな構成になります。
用途 | サーバ名 | URL |
---|---|---|
認可サーバ。ユーザIDごとの認可情報を管理するサーバ。 | java-oauth-server | http://localhost:8080/ |
リソースサーバ。ユーザIDごとのデータや機能をもったサーバ。 | 省略 | - |
OAuth Client。リソースサーバのリソースを使用するアプリ。 | 今回はcurl | - |
おおまかな流れ
おおまかな流れについても、基本的にいままでのAuthleteの使用方法と変わらず、
- Authleteへのサインアップ
- 認可サーバの登録・クライアントの登録
- 認可サーバの構築、リソースサーバの構築(省略)
- Webアプリケーションからの疎通(アクセストークン取るまで)
となるのですが、Authlete を使って CIBA 対応の認可サーバーを作る (中のヒトかな)の記事にあるように、アカウントのサインアップはAuthlete社の方に問い合わせる必要がありそうです。
したがって今回の記事では、アカウントのサインアップが済んでいて、CIBA対応のAuthleteのサイト(通常だと https://so.authlete.com/ だけどCIBA対応版は別URL )で下記の情報は取得済、という前提で話を進めます。
- そのアカウントにひもづく認可サーバの、API キー/API シークレット
API キー | API シークレット |
---|---|
116xxxxxxxx | sZUkxxxxxxxxxxxxxxxxxxx |
- その認可サーバに登録されたOAuth Clientの クライアント ID/クライアントシークレット
クライアント ID | クライアントシークレット |
---|---|
249xxxxxxx | WUItxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx |
ちなみに、認可サーバのAPI キー/API シークレット は、下記の サービス一覧 >> サービス詳細より 確認できます。
また、認可サーバに登録されたOAuth Clientの クライアント ID/クライアントシークレット は、クライアントアプリ開発者コンソール の、アプリ一覧 >> アプリ詳細より 確認できます。
環境設定
さて、AuthleteのWEBサイトでCIBAを使用可能にするための環境設定をいくつか。
認可サーバ側の設定
さきほどの、認可サーバ側の API キー/API シークレット を確認したサイト(サービス管理者コンソール)
にて「サービス一覧 >> サービス詳細 >> CIBA」タブに遷移し、
項目 | 値 |
---|---|
サポートするトークンデリバリーモード | POLL,PING,PUSH |
ユーザーコードのサポート | サポートする |
を選択します。
OAuth Client側の設定
OAuth Clientの クライアント ID/クライアントシークレット を確認したサイト(クライアントアプリ開発者コンソール)にて「アプリ一覧 >> アプリ詳細」に遷移。
「基本情報」タブにて
項目 | 値 |
---|---|
クライアントタイプ | CONFIDENTIAL |
を選択します。
「認可」タブにて
項目 | 値 |
---|---|
クライアント認証方式 | CLIENT_SECRET_BASIC |
を選択します。
「CIBA」タブにて
項目 | 値 |
---|---|
トークンデリバリーモード | POLL |
ユーザーコードの要求 | 要求する |
を選択します。
以上で、POLLモードで動かすときの認可サーバ、OAuth Clientの環境設定は完了です。
動かしてみる
認可サーバを構築
まずは、認可サーバを構築します。GitHubのリポジトリをcloneしてきて、設定ファイル(authlete.properties)に API キー、API シークレット、base_urlを設定すればOKです。
$ git clone https://github.com/authlete/java-oauth-server.git
$ cd java-oauth-server
$ cat authlete.properties
service.api_key = 116xxxxxxxx ← 正しい API キー に変更してください
service.api_secret = sZUkxxxxxxxxxxxxxxxxxxx ← 正しい API シークレット に変更してください
base_url = https://api.authlete.com ← CIBA 対応のURLに変更してください(値はAuthlete社さんに要問い合わせ)
起動します。
$ mvn clean jetty:run -Dauthlete.ad.workspace=masatomix/testProject ← 引数は後ででてくるシミュレータで設定する値
以上で、認可サーバの構築は完了です。
認証デバイスの準備
バックチャネル認証リクエストを受信した認可サーバは、認証デバイスに対して「許可してよい?」という通知を送りますが、cloneしてきた認可サーバのコードはデフォルトでAuthlete CIBA Simulator へ通知を送るようになっています。なのでシミュレータのセットアップをおこないます。
https://cibasim.authlete.com/ へアクセスし、さきほどの引数に設定した、
Namespace | Project |
---|---|
masatomix | testProject |
を指定し「Create」をクリック。(下記画面キャプチャは作成した後のもので「Open」になってますが。。)
すると、Authentication Device(AD)のシミュレータ画面に遷移します。Authleteさん提供の認可サーバはダミーのユーザ認証がコーディングされていて、
User ID | User Code |
---|---|
1001 | 675325 |
でログイン可能となっています。従って、キャプチャにあるとおり「1001」というUser IDを指定して「Launch AD simulator」をクリック。
シミュレータが待ち状態になりました。この画面でOAuthの認証/認可を行うので、画面は閉じずに出しておいてください(認可サーバがバックチャネル認証リクエストを受信したときにここにWebSocketでプッシュ通知がくるっぽい)。
ちなみに、この画面をスマホとかで開いておくと、わりと臨場感がでます。全然関係ないPC上のcurlから認証リクエストを送ると、スマホに通知が来るようなイメージです。
バックチャネル認証リクエストを送信
さてさっそく認可サーバへ、認証リクエストを送ってみます。
$ clientId=249xxxxxxx ← クライアント ID
$ clientSecret=WUItxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ← クライアントシークレット
$ curl http://localhost:8080/api/backchannel/authentication -X POST \
--user ${clientId}:${clientSecret} \
-d 'login_hint=1001' \
-d 'user_code=675325' \
-d 'scope=openid'
{
"auth_req_id": "Xe250q9AoSdUL_xohrPm8txYNUo8VlddhLv1ENUY6SM",
"interval": 5,
"expires_in": 600
}
$
auth_req_id (認証リクエストID)が得られました。後でつかうので記録しておいてください。
認証デバイスで、認証処理
認証リクエストを送信すると、さきのほどのシミュレータにOAuthでおなじみの認可画面が表示されていると思います。今回は「Allow」を選択。
トークンリクエストを送信
ユーザの認可が得られたので、さきほどの auth_req_id が使用可能になっています。トークンを取得する準備が出来ていますので、トークンリクエストを送信します。
$ clientId=249xxxxxxx ← クライアント ID
$ clientSecret=WUItxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ← クライアントシークレット
$ curl http://localhost:8080/api/token -X POST \
--user ${clientId}:${clientSecret} \
-H 'Content-type: application/x-www-form-urlencoded' \
-d 'auth_req_id=Xe250q9AoSdUL_xohrPm8txYNUo8VlddhLv1ENUY6SM' \
-d 'grant_type=urn:openid:params:grant-type:ciba'
{
"access_token":"ldofpBo8hO67CNr4sE_Cibt1FYZ8RpQCIPVlUaeokks",
"refresh_token":"U0JyhKdXh-h3h6hDLiu3eart9RpEwnSClMtgGjAFu5o",
"scope":"openid",
"id_token":
"eyJhbGciOiJIUzI1NiJ9.eyJhdF9oYXNoIjoiRkxwdFBEaWlMbUcwdkVLWFZndjBUZyIsInN1YiI6IjEwMDEiLCJhdWQiOiIyNDkzOTMxNjU5OTciLCJhdXRoX3RpbWUiOjE1NTM4NTE3MjQsImlzcyI6Imh0dHBzOi8vYXV0aGxldGUuY29tIiwiZXhwIjoxNTUzOTM4MTM0LCJpYXQiOjE1NTM4NTE3MzR9.7G_VfqlSlDd0cOSjZaRorSrbcH3PoKneM_YalCpFHig","token_type":"Bearer","expires_in":86400
}
アクセストークンが取得できましたね!今回はscopeを openid にしているのでid_tokenも得られています。
$ echo eyJhdF9oYXNoIjoiRkxwdFBEaWlMbUcwdkVLWFZndjBUZyIsInN1YiI6IjEwMDEiLCJhdWQiOiIyNDkzOTMxNjU5OTciLCJhdXRoX3RpbWUiOjE1NTM4NTE3MjQsImlzcyI6Imh0dHBzOi8vYXV0aGxldGUuY29tIiwiZXhwIjoxNTUzOTM4MTM0LCJpYXQiOjE1NTM4NTE3MzR9 | base64 -D | jq
{
"at_hash": "FLptPDiiLmG0vEKXVgv0Tg",
"sub": "1001",
"aud": "249393165997",
"auth_time": 1553851724,
"iss": "https://authlete.com",
"exp": 1553938134,
"iat": 1553851734
}
実際はポーリングする
今回の処理は push,ping,poll のモードのうちpollでやっているので、認証リクエストがOKされたかどうかの通知は来ません。なので実際は、認証リクエストを送信してからアプリがポーリングする必要がありそうです。
たとえば下記のようなシェルを実行してみます。
$ cat ciba_request.sh
#!/bin/bash
clientId=249xxxxxxx ← クライアント ID
clientSecret=WUItxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ← クライアントシークレット
login_hint=1001
user_code=675325
backchannelReq=`cat << EOS
curl http://localhost:8080/api/backchannel/authentication -X POST \
--user ${clientId}:${clientSecret} \
-d 'login_hint=${login_hint}' \
-d 'user_code=${user_code}' \
-d 'scope=openid' -s | jq .auth_req_id -r
EOS
`
auth_req_id=`eval "${backchannelReq}"`
echo ${auth_req_id}
for i in {0..3}; do
sleep 3
tokenReq=`cat << EOS
curl http://localhost:8080/api/token -X POST \
--user ${clientId}:${clientSecret} \
-H 'Content-type: application/x-www-form-urlencoded' \
-d 'auth_req_id=${auth_req_id}' \
-d 'grant_type=urn:openid:params:grant-type:ciba' -s
EOS
`
token=`eval "${tokenReq}"`
error=`echo ${token} | jq 'select(.error_description==null)'`
echo ${token} | jq
if [ -n "$error" ]; then
break;
fi
done
$ ./ciba_request.sh
lrHy0QdUalqpL7K2MRMfV_uhCLuvolCk5hS70LEb7R8
{
"error_description": "[A200308] The end-user has not been authenticated yet.",
"error": "authorization_pending",
"error_uri": "https://www.authlete.com/documents/apis/result_codes#A200308"
}
{
"error_description": "[A200308] The end-user has not been authenticated yet.",
"error": "authorization_pending",
"error_uri": "https://www.authlete.com/documents/apis/result_codes#A200308"
}
// ココでシミュレータでAllowを押下したら。。
{
"access_token": "RexqI1mrrCpVI9fiYJWhxuJMSSOfD6j1ijNXs-cXOts",
"refresh_token": "x0CuGD-CPK1q2b8WzuU5WYBeCoIxdkBg5aaenLCeeWQ",
"scope": "openid",
"id_token": "eyJhbGciOiJIUzI1NiJ9.eyJhdF9oYXNoIjoidVBRWVVnT1BmenVaQ1Jab0E1b21XUSIsInN1YiI6IjEwMDEiLCJhdWQiOiIyNDkzOTMxNjU5OTciLCJhdXRoX3RpbWUiOjE1NTQyNTc4NzksImlzcyI6Imh0dHBzOi8vYXV0aGxldGUuY29tIiwiZXhwIjoxNTU0MzQ0MjgxLCJpYXQiOjE1NTQyNTc4ODF9.1mdVZ2hub3GzwGNNxaL1HxlQHdIioSvLyp0UJfdMDog",
"token_type": "Bearer",
"expires_in": 86400
}
$
よさそうですね。。
まとめ
今回はAuthleteサービスを用いて、CIBA対応の認可サーバ(pollモード)を構築してみました。
まだまださわりを構築しただけで、全体感の理解とはほぼ遠いのですが、Authleteを用いることで容易に認可サーバを立ち上げることが出来ました。
おつかれさまでした。
関連リンク
- OpenID Connect Client Initiated Backchannel Authentication Flow - Core 1.0 draft-02
- 【2019年版】世界最先端の認証認可技術、実装者による『CIBA』解説
- Authlete を使って CIBA 対応の認可サーバーを作る
- FAPI (Financial-grade API) and CIBA (Client Initiated Backchannel Authentication)
- 認可サービス Authlete、オープン API の適用シーンを広げる新仕様「CIBA」に対応
- Authlete CIBA Simulator
- Authleteを使った認可サーバの構築と、OAuthクライアント(Webアプリケーション)からの疎通
- OIDC Client Initiated Backchannel Authentication Flow (CIBA)とは - 概要編
-
登壇されていた方が この処理シーケンスのことをOAuth Dance と説明されてたのが印象的 :-) ↩