7
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Authleteを使った認可サーバの構築手順(CIBA対応版)

Posted at

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を押せば、決済が完了する

なんていう、ブラウザのリダイレクトが不要な処理シーケンスが実現できるというわけです。うーん、なるほど便利です。

具体的な処理シーケンスは、だいたいこんな感じになりそうですね。。

image.png

イントロ

さて以前、とある開発案件で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 の処理シーケンス

image.png

今回はCIBA対応されたAuthleteをさわるってことで、下記のような(さきほど載せた) CIBA Flowの処理シーケンスを実装してアクセストークンを取得するところまでをやってみます。認証デバイスについては、認証デバイスのシミュレータサイトがあるので、そのサイトを使用します。

  • CIBA Flowの処理シーケンス

image.png

また「ユーザが認証デバイスでオペした結果(認可・拒否・タイムアウト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の使用方法と変わらず、

  1. Authleteへのサインアップ
  2. 認可サーバの登録・クライアントの登録
  3. 認可サーバの構築、リソースサーバの構築(省略)
  4. 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 シークレット は、下記の サービス一覧 >> サービス詳細より 確認できます。
image.png

また、認可サーバに登録されたOAuth Clientの クライアント ID/クライアントシークレット は、クライアントアプリ開発者コンソール の、アプリ一覧 >> アプリ詳細より 確認できます。
image.png

環境設定

さて、AuthleteのWEBサイトでCIBAを使用可能にするための環境設定をいくつか。

認可サーバ側の設定

さきほどの、認可サーバ側の API キー/API シークレット を確認したサイト(サービス管理者コンソール)
にて「サービス一覧 >> サービス詳細 >> CIBA」タブに遷移し、

項目
サポートするトークンデリバリーモード POLL,PING,PUSH
ユーザーコードのサポート サポートする

を選択します。

image.png

OAuth Client側の設定

OAuth Clientの クライアント ID/クライアントシークレット を確認したサイト(クライアントアプリ開発者コンソール)にて「アプリ一覧 >> アプリ詳細」に遷移。

「基本情報」タブにて

項目
クライアントタイプ CONFIDENTIAL

を選択します。

image.png

「認可」タブにて

項目
クライアント認証方式 CLIENT_SECRET_BASIC

を選択します。

image.png

「CIBA」タブにて

項目
トークンデリバリーモード POLL
ユーザーコードの要求 要求する

を選択します。

image.png

以上で、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」になってますが。。)

image.png

すると、Authentication Device(AD)のシミュレータ画面に遷移します。Authleteさん提供の認可サーバはダミーのユーザ認証がコーディングされていて、

User ID User Code
1001 675325

でログイン可能となっています。従って、キャプチャにあるとおり「1001」というUser IDを指定して「Launch AD simulator」をクリック。

image.png

シミュレータが待ち状態になりました。この画面でOAuthの認証/認可を行うので、画面は閉じずに出しておいてください(認可サーバがバックチャネル認証リクエストを受信したときにここにWebSocketでプッシュ通知がくるっぽい)。

image.png

ちなみに、この画面をスマホとかで開いておくと、わりと臨場感がでます。全然関係ないPC上のcurlから認証リクエストを送ると、スマホに通知が来るようなイメージです。
image.png

バックチャネル認証リクエストを送信

さてさっそく認可サーバへ、認証リクエストを送ってみます。

$ 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」を選択。
image.png

トークンリクエストを送信

ユーザの認可が得られたので、さきほどの 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を用いることで容易に認可サーバを立ち上げることが出来ました。

おつかれさまでした。

関連リンク

  1. 登壇されていた方が この処理シーケンスのことをOAuth Dance と説明されてたのが印象的 :-)

7
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?