Keycloak で OAuth 2.0 Token Exchange を試してみる

OAuth 2.0 Token Exchange の概要

マイクロサービスパターンでは、クライアントアプリケーションが呼び出している API は、実際には API ゲートウェイ経由でバックエンド API を呼び出すことで要求する処理を実行することが一般的です。

API ゲートウェイは大抵の場合、 OAuth 2.0 や OpenID Connect によって保護されます。
クライアントアプリケーションが保護された API ゲートウェイと通信し、さらにその先の他の保護されたバックエンド API とやり取りする必要がある場合に、クライアントが利用している OAuth アクセストークンをそのまま再利用してしまうと、 本来 API ゲートウェイを保護対象とするアクセストークンをバックエンド API が受け入れる必要があり、あまり良いとは言えません。

上記の問題を解決すべく、 OAuth ワーキンググループでは、OAuth 2.0 Token Exchange と呼ばれる仕様の標準化に取り組んでいます。

OAuth 2.0 Token Exchange は、ある API に対するアクセストークンを、別の API に渡すためのセキュリティトークンを OAuth 2.0 認可サーバとやりとりして取得する方法を定義しています。

OAuthTokenExchangeExample

上記は、OAuth 2.0 Token Exchange における典型的なフローを示す図です。
(引用: Delegation Patterns for OAuth 2.0(Scott Brady - Identity & Access Control)

1. まずクライアントアプリケーションが、 API ゲートウェイに対して JWT のアクセストークンを Bearer ヘッダとして付与して、API コールします。

2. API ゲートウェイは、 STS (Security Token Service) のトークンエンドポイントに対して、以下のような形式のアクセストークン交換のリクエストを送ります。(見やすさのため URL エンコードを外した形にしています)
subject_token は API ゲートウェイに権限委任する元のアクセストークンを指定し、subject_token_type はそのトークンのタイプを指定します。(アクセストークンの場合は、urn:ietf:params:oauth:token-type:access_token を指定)

3. STS は、 backend.example.com 向けの権限委任されたアクセストークンを返します。

4. API ゲートウェイは、新しく取得したアクセストークンを Bearer ヘッダに付与して、バックエンド API にリクエストを送信します。

権限委任されたアクセストークンは JWT 形式で、そのペイロード部分は以下のようになります。

act(actor) クレームは、委任が行われたことを意味するものです。
上記の例では、 admin@example.com が、 user@example.com に代わってバックエンド API を実行する、ということを示しています。

The outermost "act" claim represents the current actor while nested "act" claims represent > prior actors.

とあるように、 act クレームはネストすることが可能で委任の履歴を表現することができます。

また、may_act クレーム というものも定義されています。
このクレームは、ある当事者が別の当事者として振る舞う権限を与えられていることを意味します。

act クレームと may_act クレームの違いが分かりにくいですが、
act クレームは、user@example.com に代わって、admin@example.com として API を実行できることを示し、
may_act クレームは、admin@example.comuser@example.com の権限で API を実行できることを示していると私は解釈しています。

Keycloak で OAuth 2.0 Token Exchange を 試してみる

OAuth 2.0 Token Exchange はまだドラフト版ですが、 OSS のアイデンティティ・アクセス管理ソフトウェアである Keycloak で実装されているみたいですので、試してみました。

今回試すフロー図は以下の通りです。 Google から発行されたアクセストークンを、 Keycloak のアクセストークンに変換してみます。

Google の OAuth 認証情報取得

交換元のアクセストークン発行は、今回は Google を利用して試します。

Google Developer Console にアクセスして、
まずは、「 OAuth 同意画面」にて、アプリケーション名と、承認済みドメインを入力します。

※ 承認済みドメインは、Google との OpenID Connect Authrization Code フローにおけるリダイレクト URL に指定するドメインを指定します。リダイレクト URL に対して Authrization Code が発行されるので、試す場合でも実在するドメインがやりやすいでしょう。


次に、「認証情報」にて、 OAuth クライアント ID を作成します。
アプリケーションの種類は「ウェブアプリケーション」を選択し、承認済みのリダイレクト URI を入力します。


クライアント ID と、クライアントシークレットが発行されるので控えておきます。


Keycloak のセットアップ

次に Keycloak 側の設定を行なっていきます。
(今回は、Keycloak のバージョンは記事執筆時点(2019/09)の最新バージョンである7.0.0で試しています。)

まずはダウンロードします。

次に bin/standalone.sh の以下の行を修正します。
( Keycloak の Token Exchange 機能はテクノロジープレビュー版のため、利用できるようにするために起動オプションを付与しています。)

次に Keycloak のユーザを追加します。

bin/standalone.sh を実行して、 Keycloak を起動します。
8080番ポートで Keycloak の管理画面にアクセスできるようになりますので、ブラウザに http://localhost:8080/auth/admin にアクセスして、先ほど登録したユーザでログインします。
ログイン成功すると以下のような画面が表示されます。


次に、Keycloak 側の OAuth クライアントを登録します。

左側のメニューの Clients から新規登録します。


デフォルトでアクセスタイプが Public となっているため、 Confidential に変更します。
※ Valid Redirect URIs を一つ以上登録する必要がありますが、使用しないため今回は適当で構いません。


アクセスタイプを Confidential に変更すると、 Credentials タブが増えますので、そこでクライアントシークレットを確認します。


次に Identity Provider を登録します。
ここで先ほどの Google OAuth クライアント情報を設定していきます。


Google OAuth のクライアント ID とクライアントシークレットを入力して保存すると、 Permission タブが表示されます。
Permission を Enabled に変更すると、token-exchange というスコープが表示されます。


token-exchange をクリックして、 Token Exchange の設定を行ないます。

create policy -> client を選択して、ポリシーの登録をします。


ここで先ほど登録した Keycloak の クライアントと、 Google のクライアントを紐付けます。



交換してみる

ここまでで事前準備完了です。
実際に試していきましょう。

最初に、Google の Authorization Code フローを実行して、Google からアクセストークンを取得します。
ブラウザから以下の URL にアクセスします。(見やすさのため URL エンコードを外したり適宜改行を入れたりしていますが、実際には改行を入れずにアクセスしてください)

Google のログイン画面が表示されるので、ログインするとリダイレクト URI にリダイレクトされます。
以下のようにリダイレクト先で認可コードを確認することができると思います。(見やすさのため URL エンコードを外して適宜改行を入れています)

認可コードを取得できたので、 Google のトークンエンドポイントに curl でアクセストークン取得のリクエストを送ります。

以下のようなレスポンスを得ることができれば、 Google からのアクセストークン取得成功です。

では、 Google のアクセストークンを Keycloak のアクセストークンに交換してみましょう。
curl で以下のようにトークン交換のリクエストを送ります。

以下のようなレスポンスが返ってきます。

発行されたアクセストークンのペイロード部分はこんな感じです。
先述した act クレームや、may_act クレームがペイロードの中に現れてこないですね。

act クレームや、may_act クレームがペイロードの中に現れていない理由ですが、Keyclock のサービスガイド (原文) の以下引用にある通り、 OAuth Token Exchange の仕様を無視しているためだと考えられます。

KeycloakのToken Exchangeは、 OAuth Token Exchange の仕様の非常にルーズな実装です。
Keycloakでは、それを少し拡張し、一部を無視し、仕様の他の部分をルーズに解釈しました。

終わりに

OAuth 2.0 Token Exchange 自体がドラフトである点、また Keycloak が実装している仕様は OAuth Token Exchange の仕様の一部を無視したりしている点から、実用レベルにはまだまだ遠い印象です。
現状でこのような各クライアントからの権限委任を行なう場合は、何らかの方法でアクセストークンの交換の仕組みを自前で実装するしかないでしょう。
しかし、外部で発行されたアクセストークンを内部向け API 用に交換できるのは魅力的なので、これから議論が進み仕様が標準化されて、様々な OAuth / OpenID Connect のプロダクトで実装されるようになることを期待したいと思います。

最後に理解の助けとなる参考文献を載せておきます。

Comments are closed, but you can leave a trackback: Trackback URL.