Table of Contents
本書では、チュートリアル「Financial-grade API Basics」(以下 “FAPI Basics”) の設定を行った Authlete サービスにリファレンス実装を統合し、 FAPI 準拠の認可フローと API リクエストの動作を確認します。
手順の実施にあたっては、以下のチュートリアルの実施とナレッジベース記事の通読を行い、 OpenID Connect と Authlete に関する基本的な理解を有していることが必須です。
また、以下のチュートリアル (FAPI Basics) の手順に従って、 Authlete が設定されている必要があります。 とくに、設定した環境が「設定完了後の実行例」の記述通りに動作することを確認してください。
本チュートリアルでは、動作確認に以下のソフトウェアを用います。 これらを実行可能な環境をご用意ください。
本セクションでは以下の作業を行います。
設定後の構成は以下の図の通りです。 Web ブラウザと curl は、それぞれ認可リクエスト・トークンリクエストを、 認可サーバーである java-oauth-server (http://localhost:8080) に送信します。
java-oauth-server のドキュメントに従い、ダウンロードと設定を行います。
git
コマンドを用いて java-oauth-server をダウンロードします。
$ git clone https://github.com/authlete/java-oauth-server.git
設定ファイル authlete.properties
の以下の行を編集します。
FAPI Basics の設定が済んでいる Authlete サービスの、API キーと API シークレットを指定してください。
service.api_key = <your API key e.g. 1450...0338>
service.api_secret = <your API key e.g. VEIl...0cEs>
mvn
コマンドを用いて java-oauth-server を起動します。
$ mvn jetty:run
Web ブラウザから http://localhost:8080 にアクセスし、以下のページが表示されることを確認します。
FAPI Basics チュートリアルの手順に従いリクエストオブジェクトを作成し、認可リクエストを組み立てます。本チュートリアルにて用いている内容は以下の通りです。
client_id
の値 (1756...3766
) を FAPI Basics にて自動生成された値に変更してください){
"redirect_uri":"https://client.example.org/cb/example.com",
"response_type":"code id_token",
"client_id":"1756...3766",
"scope":"openid payment",
"exp":15549730000,
"aud":"https://as.example.com",
"claims":{
"id_token":{
"acr":{
"essential":true,
"values":["urn:example:psd2:sca"]
}
}
},
"nonce":"n-0S6_WzA2Mj"
eyJr...YifQ.ewoi...iCn0.ztl2...tjAA
client_id
と request
パラメーターの値
(それぞれ 1756...3766
, eyJr...YifQ.ewoi...iCn0.ztl2...tjAA
)
を適宜変更してください)http://as.example.com:8080/api/authorization
?redirect_uri=https://client.example.org/cb/example.com
&scope=openid+payment
&response_type=code+id_token
&client_id=1756...3766
&nonce=n-0S6_WzA2Mj
&request=eyJr...YifQ.ewoi...iCn0.ztl2...tjAA
生成した認可リクエストの URL を、Web ブラウザのロケーションに入力・送信します。
認可ページが表示されます。
Login ID / Password に、それぞれ john
/ john
と入力し、Authorize ボタンをクリックします。
https://client.example.org/cb/example.com/#code=...
にリダイレクトされます。しかし、
ホスト client.example.org
は存在しないため、Web ブラウザはエラーページを表示します。
Web ブラウザの表示したエラーページの URL をコピーします。本チュートリアルでは以下の内容になります。
https://client.example.org/cb/example.com#code=0Io2....sdkQ&id_token=eyJr...YifQ.eyJz...aiJ9.-EPu...Dkkw
この URL から code
パラメーターを取り出します。
code=0Io2...sdkQ
先に得た code
の値を含むトークンリクエストを組み立て、java-oauth-server のトークンエンドポイント (/api/token) に送信します。
ここではまず、client_id
/ client_secret
によるクライアント認証を試みます。
本チュートリアルでの各値は 0Io2...sdkQ
, 1756...3766
, EXE7...UxHg
となっていますが、適宜変更してください。
$ curl -s -X POST http://localhost:8080/api/token \
-u '1756...3766:EXE7...UxHg' \
--data-urlencode 'grant_type=authorization_code' \
--data-urlencode 'redirect_uri=https://client.example.org/cb/example.com' \
--data-urlencode 'code=0Io2...sdkQ'
上記のトークンリクエストの結果、トークンレスポンスは以下のような内容になります。
{
"error_description": "[A244308] Because the feature of certificate-bound
access tokens is enabled, the client must establish a mutual TLS connection
to the token endpoint.",
"error": "invalid_request",
"error_uri": "https://docs.authlete.com/#A244308"
}
クライアント(本チュートリアルでは curl コマンド)と java-oauth-server との接続が相互 TLS ではないため、java-oauth-server はクライアント証明書を取得できませんでした。結果的に java-oauth-server から Authlete API (/auth/token API) へのリクエストにもクライアント証明書は含まれず、クライアント証明書にひもづくアクセストークンが発行できなかったため、Authlete が A244308 エラー を返却しています。
"error_description"
, "error_uri"
を含んでいますが、
これらは Authlete の設定により省略可能です。
詳細は Authlete ナレッジベースの記事をご参照ください。
このエラーを解消するために、次のセクションにて、java-oauth-server がクライアント証明書を受け取れるように設定していきます。
本セクションでは以下の作業を行います。
設定後の構成は以下の図の通りです。 先のセクションでは、Web ブラウザとクライアントの接続先は java-oauth-server (http://localhost:8080) でしたが、設定後は リバースプロキシーとして前段に配置した Apache (https://as.example.com:8443) に変更します。 また curl については、Apache への接続時にクライアント認証を試みます。
この後ローカル環境にインストールする Apache に対して、Web ブラウザと curl が
as.example.com
という FQDN でアクセスできるように、本チュートリアルでは /etc/hosts
に以下を追加します。
127.0.0.1 as.example.com
チュートリアルの実行環境に Apache をインストールします。
$ brew install httpd
本チュートリアルでは、Apache の設定ファイルが /usr/local/etc/httpd
以下に配置されたものとします。
OpenSSL を用いて、Apache に設定する秘密鍵と公開鍵証明書を生成します。
$ openssl req -x509 -newkey rsa:2048 \
-keyout server.key -out server.crt
-days 3650 -sha256 -nodes \
-subj '/C=JP/ST=Tokyo/L=Chiyoda-ku/O=Server/CN=as.example.com'
Generating a 2048 bit RSA private key
..........................+++
....+++
writing new private key to 'server.key'
-----
この結果、server.key
(秘密鍵)と server.crt
(公開鍵証明書)の 2 つのファイルが生成されます。
生成された 2 つのファイルを任意の場所に配置します。本チュートリアルでは配置場所を /usr/local/etc/httpd
とします。
$ sudo cp -p server.key server.crt /usr/local/etc/httpd/.
Password:
本チュートリアルでは Apache の基本的な設定として以下を行います。
設定ファイル httpd.conf
に対する具体的な変更点は以下の通りです。
TLS 関連の設定は別のファイル (extra/httpd-ssl.conf
) に記述した内容をインクルードします。
$ diff /usr/local/etc/httpd/httpd.conf.orig \
/usr/local/etc/httpd/httpd.conf
52c52
< Listen 8080
---
> #Listen 8080
92c92
< #LoadModule socache_shmcb_module lib/httpd/modules/mod_socache_shmcb.so
---
> LoadModule socache_shmcb_module lib/httpd/modules/mod_socache_shmcb.so
131c131
< #LoadModule proxy_module lib/httpd/modules/mod_proxy.so
---
> LoadModule proxy_module lib/httpd/modules/mod_proxy.so
134c134
< #LoadModule proxy_http_module lib/httpd/modules/mod_proxy_http.so
---
> LoadModule proxy_http_module lib/httpd/modules/mod_proxy_http.so
150c150
< #LoadModule ssl_module lib/httpd/modules/mod_ssl.so
---
> LoadModule ssl_module lib/httpd/modules/mod_ssl.so
524c524
< #Include /usr/local/etc/httpd/extra/httpd-ssl.conf
---
> Include /usr/local/etc/httpd/extra/httpd-ssl.conf
本チュートリアルでは Apache の TLS 関連の設定として以下を行います。
設定ファイル extra/httpd-ssl.conf
に対する具体的な変更点は以下の通りです。
$ diff /usr/local/etc/httpd/extra/httpd-ssl.conf.orig \
/usr/local/etc/httpd/extra/httpd-ssl.conf
125c125
< ServerName www.example.com:8443
---
> ServerName as.example.com:8443
289a290,302
> SSLEngine on
> SSLProtocol TLSv1.2
> SSLCipherSuite -ALL:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA256
> SSLCertificateKeyFile "/usr/local/etc/httpd/server.key"
> SSLCACertificateFile "/usr/local/etc/httpd/server.crt"
> SSLVerifyClient optional_no_ca
> SSLOptions +StdEnvVars +ExportCertData
> SSLCompression off
> RequestHeader set X-Ssl-Cipher "%{SSL_CIPHER}e" env=SSL_CIPHER
> RequestHeader set X-Ssl-Cert "%{SSL_CLIENT_CERT}e" env=SSL_CLIENT_CERT
> ProxyPreserveHost on
> ProxyPass "/" "http://localhost:8080/"
> ProxyPassReverse "/" "http://localhost:8080/"
apachectl
コマンドを用いて Apache を起動します。
$ apachectl start
起動後、Web ブラウザを用いて https://as.example.com:8443 にアクセスし、 http://localhost:8080 と同一の、java-oauth-server のトップページが表示されることを確認します。
次のセクションでは、トークンリクエストを送信するクライアント (curl) のための、 秘密鍵と公開鍵証明書を準備します。
以下のコマンドを実行し、秘密鍵と公開鍵証明書を生成します。
$ openssl req -x509 -newkey rsa:2048 \
-keyout client.key -out client.crt
-days 3650 -sha256 -nodes \
-subj '/C=JP/ST=Tokyo/L=Chiyoda-ku/O=Server/CN=client.example.org'
Generating a 2048 bit RSA private key
..........................+++
....+++
writing new private key to 'client.key'
-----
この結果、client.key
(秘密鍵)と client.crt
(公開鍵証明書)の 2 つのファイルが生成されます。
これらを、curl を用いたトークンリクエスト送信の際に用います。
FAPI 認可フローの実行 (1) と同様に認可リクエストを行います。
返却される認可レスポンスから code
パラメーターの値を抽出します。本チュートリアルでは次の値となります。
7Y5h...zp_4
トークンリクエストを組み立て、java-oauth-server のトークンエンドポイント (/api/token) に送信します。 FAPI 認可フローの実行 (1) とは以下の点が異なります。
http://localhost:8080
ではなく、https://as.example.com:8443
に接続するclient_secret
ではなく、秘密鍵 (client.key
) および公開鍵証明書 (client.crt
) を用いる結果的に、実行する curl コマンドは以下のようになります。
(client_id
, code
の値は適宜変更してください)
$ curl --insecure -X POST https://as.example.com:8443/api/token \
--key client.key --cert client.crt \
--data-urlencode 'client_id=1756...3766' \
--data-urlencode 'grant_type=authorization_code' \
--data-urlencode 'redirect_uri=https://client.example.org/cb/example.com' \
--data-urlencode 'code=7Y5h...zp_4'
上記のトークンリクエストの結果、トークンレスポンスは以下のような内容になります。
{
"access_token": "nbN3...LAU8",
"refresh_token": "SWUu...V8_w",
"scope": "openid payment",
"id_token": "eyJr...YifQ.eyJz...qIn0.GPJr...STPQ",
"token_type": "Bearer",
"expires_in": 86400
}
クライアント証明書を指定し、相互 TLS 接続を行った結果、アクセストークン(本チュートリアルでは nbN3...LAU8
)
を取得できました。この処理の裏では、Authlete が、アクセストークンとクライアント証明書をひもづけています。
次のセクションでは、アクセストークンを用いてリソースサーバーの API にアクセスを試みます。
本セクションでは以下の作業を行います。
設定後の構成は以下の図の通りです。
java-resource-server のドキュメントに従い、ダウンロードと設定を行います。
git
コマンドを用いて java-resource-server をダウンロードします。
$ git clone https://github.com/authlete/java-resource-server.git
設定ファイル authlete.properties
の以下の行を編集します。
java-oauth-server に設定したものと同じ API キーと API シークレットを指定してください。
service.api_key = <your API key e.g. 1450...0338>
service.api_secret = <your API key e.g. VEIl...0cEs>
mvn
コマンドを用いて java-resource-server を起動します。
$ mvn jetty:run
Web ブラウザから http://localhost:8081 にアクセスし、以下のページが表示されることを確認します。
curl
コマンドを用いて、java-resource-server の「カントリーエンドポイント」に、アクセストークン付きの API リクエストを試みます。
まず FAPI 認可フローの実行 (2) によりアクセストークンを取得します。
本チュートリアルでは以下の値となります。
6Ivp...LiM4
このアクセストークンを Authorization
ヘッダーにセットし、API リクエストを行います。
以下はリクエスト・レスポンスの例です。curl
コマンドに -v
オプションを付与し、
ヘッダー情報を出力しています。
$ curl -v -X GET http://localhost:8081/api/country/JP \
-H 'Authorization: Bearer 6Ivp...LiM4'
...
> GET /api/country/JP HTTP/1.1
> Host: localhost:8081
> User-Agent: curl/7.64.1
> Accept: */*
> Authorization: Bearer 6Ivp...LiM4
>
< HTTP/1.1 401 Unauthorized
< Date: Mon, 18 May 2020 02:22:32 GMT
< Cache-Control: no-store, no-transform
< Pragma: no-cache
< WWW-Authenticate: Bearer error="invalid_token",
error_description="[A065304] The client failed to present
a certificate and the access token is MTLS bound.",
error_uri="https://docs.authlete.com/#A065304"
< Content-Length: 0
< Server: Jetty(9.3.7.v20160115)
...
クライアント(本チュートリアルでは curl コマンド)と java-resource-server との接続が相互 TLS ではないため、java-resource-server はクライアント証明書を取得できませんでした。結果的に java-resource-server から Authlete API /auth/introspection API へのリクエストにもクライアント証明書は含まれず、クライアント証明書とアクセストークンとのひもづけが確認できなかったため、Authlete が A065304 エラー を返却しています。
このエラーを解消するために、次のセクションにて、java-resource-server がクライアント証明書を受け取れるように設定していきます。
本セクションでは以下の作業を行います。 基本的には、前半のセクションにて java-oauth-server に関して行った設定と同様です。
設定後の構成は以下の図の通りです。 リバースプロキシーとなる Apache (https://rs.example.com:8443) を経由して java-resource-server に接続するようにします。 また curl は Apache への接続時にクライアント認証を試みます。
Apache に対して、curl が
rs.example.com
という FQDN でアクセスできるよう、リバースプロキシーの設定 (1)
にて追加した /etc/hosts
の行に、以下のように追記します。
127.0.0.1 as.example.com rs.example.com
Apache の TLS 関連の設定として、以下を extra/httpd-ssl.conf
ファイルに追記します。
サーバー名やプロキシー先を除けば、先のリバースプロキシーの設定 (1) における内容と同様です。
具体的な追加内容は以下の通りです。
<VirtualHost _default_:8443>
DocumentRoot "/usr/local/var/www"
ServerName rs.example.com:8443
ServerAdmin you@example.com
ErrorLog "/usr/local/var/log/httpd/error_log"
TransferLog "/usr/local/var/log/httpd/access_log"
SSLEngine on
SSLProtocol TLSv1.2
SSLCipherSuite -ALL:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA256
SSLCertificateKeyFile "/usr/local/etc/httpd/server.key"
SSLCertificateFile "/usr/local/etc/httpd/server.crt"
SSLCACertificateFile "/usr/local/etc/httpd/server.crt"
SSLVerifyClient optional_no_ca
SSLOptions +StdEnvVars +ExportCertData
SSLCompression off
RequestHeader set X-Ssl-Cipher "%{SSL_CIPHER}e" env=SSL_CIPHER
RequestHeader set X-Ssl-Cert "%{SSL_CLIENT_CERT}e" env=SSL_CLIENT_CERT
ProxyPreserveHost on
ProxyPass "/" "http://localhost:8081/"
ProxyPassReverse "/" "http://localhost:8081/"
</VirtualHost>
apachectl
コマンドを用いて Apache を停止・起動します。
$ apachectl stop && apachectl start
起動後、Web ブラウザを用いて https://rs.example.com:8443 にアクセスし、 http://localhost:8081 と同一の、java-resource-server のトップページが表示されることを確認します。
API リクエストの実行 (1) と同様に、
curl
コマンドを用いて API リクエストを試みます。
まず FAPI 認可フローの実行 (2) によりアクセストークンを取得します。
本チュートリアルでは以下の値となります。
6Ivp...LiM4
このアクセストークンを Authorization ヘッダーにセットし、API リクエストを行います。 先ほどとは以下の点が異なります。
http://localhost:8081
ではなく、https://rs.example.com:8443
に接続するclient.key
) および公開鍵証明書 (client.crt
) も用いる結果的に、実行する curl コマンドは以下のようになります。
$ curl -v -X GET http://localhost:8081/api/country/JP \
-H 'Authorization: Bearer 6Ivp...LiM4' \
--key client.key --cert client.crt --insecure
...
実行後、API レスポンスとして以下の内容が返却されれば、動作確認は完了です。
{
"name": "Japan",
"alpha2": "JP",
"alpha3": "JPN",
"numeric": 392,
"currency": "JPY"
}
相互 TLS 接続を行うことにより、クライアント証明書とアクセストークンとのひもづけが確認でき、 API リクエストが期待通り処理されたことを示しています。
本チュートリアルでは、FAPI Basics の設定を行った Authlete サービスをバックエンドとし、 リファレンス実装 (java-oauth-server, java-resource-server) とリバースプロキシーを用いて、 FAPI 準拠の認可フローと API リクエストの動作を確認しました。