Kong GatewayではOAuth2の機能をGatewayに追加するのにOAuth2.0 Authentication Pluginというプラグインを提供している。
今回はこれの動作確認を行う。
なおここではOAuth2.0 Authentication Pluginを以降OAuth2 Pluginと省略する。OAuthという名前を含むプラグインは他にOAuth2.0 Introspection PluginやUpstream OAuth Pluginなどがあるが、そことは混同しないようご注意を。
OAuth2 Pluginとは
OAuth2 PluginはKong Gatewayを介して行うリクエスト・レスポンスの流れの中にOAuth2のレイヤーを追加し、認可機能を付与するものになる。
OAuth2 Pluginでは以下のグラントタイプを許可している。
- 認可コードグラント
- クライアントクレデンシャル
- インプリシットグラント
- リソースオーナーパスワードクレデンシャルグラント
注意点としては以下がある。
- DB-lessおよびHybridモード(Konnectも含む)では利用不可
- Kong GatewayのプロキシはHTTPSで動作する必要あり
検証
ここでは一番使われるであろう認可コードグラントで試してみる。
事前準備
Consumer、Service、Routeの作成とPluginの適用
検証用のConsumer、Service、Routeを作成する。
curl -X POST http://localhost:8001/consumers/ \
-d "username=myuser"
curl -X POST http://localhost:8001/services/ \
-d "name=myservice" \
-d "url=https://httpbin.konghq.com"
curl -X POST http://localhost:8001/services/myservice/routes \
-d "name=myroute" \
-d "paths=/httpbin"
ServiceにOAuth2 Pluginを設定する。
curl -X POST http://localhost:8001/services/myservice/plugins \
-H "Content-Type: application/json" \
-d '{
"name": "oauth2",
"config": {
"scopes": ["email", "phone", "address"],
"mandatory_scope": true,
"enable_authorization_code": true
}
}'
Pluginを適用するとトークンなしではアクセスに失敗するようになる。
$ curl localhost:8000/httpbin
{"error_description":"The access token is missing","error":"invalid_request"}
ちなみにこの時点で公式ドキュメントにあるEndpointが利用可能になるが、このエンドポイントの実装先はAdmin APIではなくプロキシを使ったRouteの先にあるという点に注意。
例えば今回で言うとhttps://localhost:8443/httpbin/oauth2/authorizeが認可コードの発行依頼をするエンドポイントとなる。
Consumer側の認証設定
Consumerの認証設定をAPIで行うために、ConsumerのIDを取得する。
CONSUMER_ID=$(curl -s localhost:8001/consumers/myuser | jq -r .id)
取得したIDを用いてConsumerにOAuth2 Pluginの認証情報を有効化する。
curl -X POST http://localhost:8001/consumers/${CONSUMER_ID}/oauth2 \
-d "name=myapp" \
-d "redirect_uris=http://localhost:8000/httpbin"
Client IDやClient Secretは指定することも出来るが、指定しない場合は自動生成される。
自動生成されたClient IDとClient Secretを環境変数に設定する。
CLIENT_ID=$(curl -sX GET http://localhost:8001/consumers/${CONSUMER_ID}/oauth2 | jq -r ".data[].client_id")
CLIENT_SECRET=$(curl -sX GET http://localhost:8001/consumers/${CONSUMER_ID}/oauth2 | jq -r ".data[].client_secret")
OAuth2 Plugin設定からProvision Keyを取得する。
PROVISION_KEY=$(curl -sX GET http://localhost:8001/services/myservice/plugins | jq -r '.data[] | select(.name == "oauth2").config.provision_key')
OAuth2 Pluginを利用して認可コードグラントを進める場合、Provision Keyは後に認可コードを貰うために必要となる。
認可フローの確認
ここで改めて認可コードグラントのフローを確認しておく。
ここではユーザとクライアントを人力で行い、認可サーバをOAuth2 Pluginに担ってもらい、リソースサーバーを先程設定したServiceという形で検証していく。
認可コードの取得まで
最初に認可コードのリクエストを行う。
まず、せっかくまっさらなので今発行されているトークンが存在しないことを確認する。
$ curl -sX GET http://localhost:8001/oauth2_tokens/
{"data":[],"next":null}
認可コードの発行を認可サーバーに依頼する。
curl -k -X POST https://localhost:8443/httpbin/oauth2/authorize \
-d "client_id=$CLIENT_ID" \
-d "response_type=code" \
-d "scope=email" \
-d "provision_key=$PROVISION_KEY" \
-d "authenticated_userid=myuser"
Plugin作成時にも記載したが、アクセス先はAdmin APIではなくRouteのパスの下になる点に注意。
response_mode
は認可コードリクエスト時は基本的にcodeになり、scope
はPlugin有効化時に指定したものである。
provision_key
、authenticated_userid
はOAuth2 Plugin独自のもので、それぞれ必須のパラメータとなる。
provision_key
はプラグインが割り当てられた時に生成されたキーであり、authenticated_userid
はアプリケーション側で認証したユーザIDを指定する。
実行すると以下のような値が返ってくる。
{"redirect_uri":"http://localhost:8000/httpbin?code=MUUxbON7R4K4ZSgTgVlFwCfNKifNVWbI"}
クエリのcode=
部分が認可コードになるので、これを使ってアクセストークンを取得する。
$ curl -k -X POST https://localhost:8443/httpbin/oauth2/token \
-d "grant_type=authorization_code" \
-d "client_id=$CLIENT_ID" \
-d "client_secret=$CLIENT_SECRET" \
-d "code=MUUxbON7R4K4ZSgTgVlFwCfNKifNVWbI"
トークンの取得はアクセスパスがRouteのパスの下の/oauth2/token
になる。
grant_type
で認可コードグラントであるauthorization_code
を指定し、code
に先程取得した認可コードを指定した。
結果は以下のようになる。
{"expires_in":7200,"token_type":"bearer","access_token":"PXMLx2XC1DZVUpfUL3eHERdffYT9uI77","refresh_token":"Z5kUVu7ecICVd5ZCVnvEOtl33MFmu25N"}
このaccess_token
の方のトークンを使ってアクセスする。
$ curl -k -X GET https://localhost:8443/httpbin/user-agent \
-H "Authorization: Bearer PXMLx2XC1DZVUpfUL3eHERdffYT9uI77"
{
"user-agent": "curl/8.7.1"
}
無事にアクセス出来た。
また先程Admin APIでトークンの一覧を確認した際は空だったが、今は見ると発行したトークンが確認できる。
$ curl -sX GET http://localhost:8001/oauth2_tokens/ | jq
{
"data": [
{
"credential": {
"id": "d12972b3-0be2-47b9-b9f6-a27c9d64e6a6"
},
"expires_in": 7200,
"token_type": "bearer",
"access_token": "PXMLx2XC1DZVUpfUL3eHERdffYT9uI77",
"refresh_token": "Z5kUVu7ecICVd5ZCVnvEOtl33MFmu25N",
"ttl": 1209285,
"service": {
"id": "e888cca8-a812-4897-b3e9-3b8c1f74779f"
},
"created_at": 1739518711,
"scope": "email",
"id": "4bf240b9-6bd2-4e8d-a38c-1dd284561bca",
"authenticated_userid": "myuser"
}
],
"next": null
}
以上より、OAuth2 Pluginを使って認可サーバを立てることなくトークンを発行してアクセス出来ることが確認できた。
なお、今回は認可フローを確認するためにクライアントアプリケーションを用意せずにcurlで確認したが、クライアントアプリケーションを使ったサンプルはOAuth 2.0 Hello World for Kongというものがあるので、これを参考にすると良いと思う。