OpenID Connect (OIDC) クライアントとトークンの伝播
概要
Red Hat build of Quarkus ドキュメントへのフィードバックの提供
エラーを報告したり、ドキュメントを改善したりするには、Red Hat Jira アカウントにログインし、課題を送信してください。Red Hat Jira アカウントをお持ちでない場合は、アカウントを作成するように求められます。
手順
- 次のリンクをクリックして チケットを作成します。
- Summary に課題の簡単な説明を入力します。
- Description に課題や機能拡張の詳細な説明を入力します。問題があるドキュメントのセクションへの URL も記載してください。
- Submit をクリックすると、課題が作成され、適切なドキュメントチームに転送されます。
多様性を受け入れるオープンソースの強化
Red Hat では、コード、ドキュメント、Web プロパティーにおける配慮に欠ける用語の置き換えに取り組んでいます。まずは、マスター (master)、スレーブ (slave)、ブラックリスト (blacklist)、ホワイトリスト (whitelist) の 4 つの用語の置き換えから始めます。この取り組みは膨大な作業を要するため、用語の置き換えは、今後の複数のリリースにわたって段階的に実施されます。詳細は、Red Hat CTO である Chris Wright のメッセージ をご覧ください。
第1章 OpenID Connect (OIDC) と OAuth2 クライアントおよびフィルター
Quarkus エクステンションは、トークンの取得、更新、伝播に重点を置いて、OpenID Connect および OAuth 2.0 アクセストークンの管理に使用できます。
これには、以下のパラメーターが含まれます。
-
quarkus-oidc-client
、quarkus-oidc-client-reactive-filter
、およびquarkus-oidc-client-filter
エクステンションを使用して、OpenID Connect および Keycloak などの OAuth 2.0 準拠の認可サーバーからアクセストークンを取得および更新します。 -
quarkus-oidc-token-propagation-reactive
およびquarkus-oidc-token-propagation
エクステンションを使用して、現在のBearer
またはAuthorization Code Flow
アクセストークンを伝播します。
これらのエクステンションによって管理されるアクセストークンは、リモートサービスにアクセスするための HTTP 認証ベアラートークンとして使用できます。
OpenID Connect クライアントとトークンの伝播クイックスタート も参照してください。
1.1. OidcClient
次の依存関係を追加します。
<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-oidc-client</artifactId> </dependency>
quarkus-oidc-client
エクステンションは、SmallRye Mutiny Uni
および Vert.x WebClient
を使用してトークンを取得および更新するのに使用できるリアクティブ io.quarkus.oidc.client.OidcClient
を提供します。
OidcClient
はビルド時に IDP トークンエンドポイント URL を使用して初期化されます。これは自動検出または手動で設定できます。OidcClient
はこのエンドポイントを使用して、client_credentials
や password
などのトークングラントを使用してアクセストークンを取得し、refresh_token
グラントを使用してトークンを更新します。
1.1.1. トークンエンドポイントの設定
デフォルトでは、トークンエンドポイントアドレスは、設定された quarkus.oidc-client.auth-server-url
に /.well-known/openid-configuration
パスを追加することによって検出されます。
たとえば、次の Keycloak URL があるとします。
quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus
OidcClient
は、トークンエンドポイント URL が http://localhost:8180/auth/realms/quarkus/protocol/openid-connect/tokens
であることを検出します。
また、検出エンドポイントが使用できない場合、または検出エンドポイントのラウンドトリップを節約したい場合は、検出を無効にして、相対パス値を使用してトークンエンドポイントアドレスを設定できます。以下に例を示します。
quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus quarkus.oidc-client.discovery-enabled=false # Token endpoint: http://localhost:8180/auth/realms/quarkus/protocol/openid-connect/tokens quarkus.oidc-client.token-path=/protocol/openid-connect/tokens
検出なしでトークンエンドポイント URL を設定するよりコンパクトな方法は、quarkus.oidc-client.token-path
を絶対 URL に設定することです。
quarkus.oidc-client.token-path=http://localhost:8180/auth/realms/quarkus/protocol/openid-connect/tokens
この場合、quarkus.oidc-client.auth-server-url
と quarkus.oidc-client.discovery-enabled
を設定する必要はありません。
1.1.2. サポートされているトークングラント
OidcClient
がトークンを取得するために使用できる主なトークングラントは、client_credentials
グラント (デフォルト) と password
グラントです。
1.1.2.1. クライアントクレデンシャルのグラント
OidcClient
が client_credentials
グラントを使用するように設定する方法は次のとおりです。
quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/ quarkus.oidc-client.client-id=quarkus-app quarkus.oidc-client.credentials.secret=secret
client_credentials
グラントにより、quarkus.oidc-client.grant-options.client.<param-name>=<value>
を使用してトークンリクエストの追加パラメーターを設定できます。audience
パラメーターを使用して、対象のトークンの受信者を設定する方法は次のとおりです。
quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/ quarkus.oidc-client.client-id=quarkus-app quarkus.oidc-client.credentials.secret=secret # 'client' is a shortcut for `client_credentials` quarkus.oidc-client.grant.type=client quarkus.oidc-client.grant-options.client.audience=https://example.com/api
1.1.2.2. パスワードグラント
password
グラントを使用するように OidcClient
を設定する方法は次のとおりです。
quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/ quarkus.oidc-client.client-id=quarkus-app quarkus.oidc-client.credentials.secret=secret quarkus.oidc-client.grant.type=password quarkus.oidc-client.grant-options.password.username=alice quarkus.oidc-client.grant-options.password.password=alice
クライアントクレデンシャルのグラントをカスタマイズする方法と同様に、quarkus.oidc-client.grant-options.password
設定接頭辞を使用してさらにカスタマイズできます。
1.1.2.3. その他のグラント
OidcClient
は、設定でキャプチャーできない追加の入力パラメーターを必要とする許可を使用してトークンを取得するのにも役立ちます。これらの許可は、refresh_token
(外部更新トークンを使用)、authorization_code
、および現在のアクセストークンを交換するために使用できる 2 つの許可、つまり urn:ietf:params:oauth:grant-type:token-exchange
および urn:ietf:params:oauth:grant-type:jwt-bearer
です。
アクセストークンを取得する必要があり、既存の更新トークンを現在の Quarkus エンドポイントに送信している場合は、refresh_token
グラントを使用する必要があります。この許可では、帯域外更新トークンを使用して新しいトークンセットを取得します。この場合は、OidcClient
を次のように設定します。
quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/ quarkus.oidc-client.client-id=quarkus-app quarkus.oidc-client.credentials.secret=secret quarkus.oidc-client.grant.type=refresh
次に、提供された更新トークンを使用して OidcClient.refreshTokens
メソッドを使用してアクセストークンを取得できます。
複雑なマイクロサービスアプリケーションを構築していて、同じ Bearer
トークンが複数のサービスに伝播されて使用されるのを避ける必要がある場合、urn:ietf:params:oauth:grant-type:token-exchange
または urn:ietf:params:oauth:grant-type:jwt-bearer
グラントの使用が必要になることがあります。詳細は、MicroProfile RestClient Reactive フィルターでのトークンの伝播 および MicroProfile RestClient フィルターでのトークンの伝播 を参照してください。
何らかの理由で、Quarkus OIDC エクステンション を使用して認可コードフローをサポートできない場合は、OidcClient
を使用した authorization code
グラントのサポートが必要になる場合があります。認可コードフローを実装する十分な理由がある場合は、次のように OidcClient
を設定できます。
quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/ quarkus.oidc-client.client-id=quarkus-app quarkus.oidc-client.credentials.secret=secret quarkus.oidc-client.grant.type=code
次に、OidcClient.accessTokens
メソッドを使用して、追加プロパティーのマップを受け入れ、現在の code
と redirect_uri
パラメーターを渡して、トークンの認可コードを交換できます。
OidcClient
は、urn:openid:params:grant-type:ciba
グラントもサポートします。
quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/ quarkus.oidc-client.client-id=quarkus-app quarkus.oidc-client.credentials.secret=secret quarkus.oidc-client.grant.type=ciba
次に、OidcClient.accessTokens
メソッドを使用して追加プロパティーのマップを受け入れ、auth_req_id
パラメーターを渡してトークン認可コードを交換できます。
1.1.2.4. スコープの付与
発行されたアクセストークンに特定のスコープのセットを関連付けるように要求しないといけない場合があります。専用の quarkus.oidc-client.scopes
リストプロパティーを使用します (例: quarkus.oidc-client.scopes=email,phone
)。
1.1.3. OidcClient の直接使用
次のように OidcClient
を直接使用できます。
import jakarta.inject.PostConstruct; import jakarta.inject.Inject; import jakarta.ws.rs.GET; import io.quarkus.oidc.client.OidcClient; import io.quarkus.oidc.client.Tokens; @Path("/service") public class OidcClientResource { @Inject OidcClient client; volatile Tokens currentTokens; @PostConstruct public void init() { currentTokens = client.getTokens().await().indefinitely(); } @GET public String getResponse() { Tokens tokens = currentTokens; if (tokens.isAccessTokenExpired()) { // Add @Blocking method annotation if this code is used with Reactive RestClient tokens = client.refreshTokens(tokens.getRefreshToken()).await().indefinitely(); currentTokens = tokens; } // Use tokens.getAccessToken() to configure MP RestClient Authorization header/etc } }
1.1.4. トークンの注入
OidcClient
を内部的に使用する Tokens
を注入できます。Tokens
を使用してアクセストークンを取得し、必要に応じて更新することができます。
import jakarta.inject.PostConstruct; import jakarta.inject.Inject; import jakarta.ws.rs.GET; import io.quarkus.oidc.client.Tokens; @Path("/service") public class OidcClientResource { @Inject Tokens tokens; @GET public String getResponse() { // Get the access token, which might have been refreshed. String accessToken = tokens.getAccessToken(); // Use the access token to configure MP RestClient Authorization header/etc } }
1.1.5. OidcClients の使用
io.quarkus.oidc.client.OidcClients
は OidcClient
のコンテナーです。デフォルトの OidcClient
と、次のように設定できる名前付きクライアントが含まれています。
quarkus.oidc-client.client-enabled=false quarkus.oidc-client.jwt-secret.auth-server-url=http://localhost:8180/auth/realms/quarkus/ quarkus.oidc-client.jwt-secret.client-id=quarkus-app quarkus.oidc-client.jwt-secret.credentials.jwt.secret=AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow
この場合、デフォルトのクライアントは client-enabled=false
プロパティーによって無効になります。jwt-secret
クライアントには次のようにアクセスできます。
import jakarta.inject.Inject; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import io.quarkus.oidc.client.OidcClient; import io.quarkus.oidc.client.OidcClients; @Path("/clients") public class OidcClientResource { @Inject OidcClients clients; @GET public String getResponse() { OidcClient client = clients.getClient("jwt-secret"); //Use this client to get the token } }
OIDC マルチテナンシー も使用しており、各 OIDC テナントに独自の関連付けたれた OidcClient
がある場合は、Vert.x RoutingContext
tenantId
属性を使用できます。以下に例を示します。
import jakarta.inject.Inject; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import io.quarkus.oidc.client.OidcClient; import io.quarkus.oidc.client.OidcClients; import io.vertx.ext.web.RoutingContext; @Path("/clients") public class OidcClientResource { @Inject OidcClients clients; @Inject RoutingContext context; @GET public String getResponse() { String tenantId = context.get("tenantId"); // named OIDC tenant and client configurations use the same key: OidcClient client = clients.getClient(tenantId); //Use this client to get the token } }
必要に応じて、次のようにプログラムで新しい OidcClient
を作成することもできます。
import jakarta.inject.Inject; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import io.quarkus.oidc.client.OidcClient; import io.quarkus.oidc.client.OidcClients; import io.quarkus.oidc.client.OidcClientConfig; import io.smallrye.mutiny.Uni; @Path("/clients") public class OidcClientResource { @Inject OidcClients clients; @GET public String getResponse() { OidcClientConfig cfg = new OidcClientConfig(); cfg.setId("myclient"); cfg.setAuthServerUrl("http://localhost:8081/auth/realms/quarkus/"); cfg.setClientId("quarkus"); cfg.getCredentials().setSecret("secret"); Uni<OidcClient> client = clients.newClient(cfg); //Use this client to get the token } }
1.1.6. 名前付き OidcClient とトークンの注入
複数の OidcClient
オブジェクトが設定されている場合は、OidcClients
を使用する代わりに、追加の修飾子 @NamedOidcClient
によって OidcClient
注入ターゲットを指定できます。
package io.quarkus.oidc.client; import jakarta.inject.Inject; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; @Path("/clients") public class OidcClientResource { @Inject @NamedOidcClient("jwt-secret") OidcClient client; @GET public String getResponse() { //Use the client to get the token } }
同じ修飾子を使用して、Tokens
の注入に使用される OidcClient
を指定できます。
@Provider @Priority(Priorities.AUTHENTICATION) @RequestScoped public class OidcClientRequestCustomFilter implements ClientRequestFilter { @Inject @NamedOidcClient("jwt-secret") Tokens tokens; @Override public void filter(ClientRequestContext requestContext) throws IOException { requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, "Bearer " + tokens.getAccessToken()); } }
1.1.7. RestClient Reactive ClientFilter での OidcClient の使用
以下の Maven 依存関係を追加します。
<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-oidc-client-reactive-filter</artifactId> </dependency>
io.quarkus:quarkus-oidc-client
も導入されることに注意してください。
quarkus-oidc-client-reactive-filter
エクステンションは、io.quarkus.oidc.client.filter.OidcClientRequestReactiveFilter
を提供します。
これは、OidcClientRequestFilter
と同様に機能します (MicroProfile RestClient クライアントフィルターで OidcClient を使用する を参照)。OidcClient
を使用してアクセストークンを取得し、必要に応じて更新し、HTTP Authorization
Bearer
スキーム値として設定します。違いは、Reactive RestClient と連携し、トークンを取得または更新するときに現在の IO スレッドをブロックしない非ブロッキングクライアントフィルターを実装することです。
OidcClientRequestReactiveFilter
は、IO スレッドのブロックを回避するために、最初のトークンの取得が実行されるまで遅延します。
io.quarkus.oidc.client.reactive.filter.OidcClientFilter
または org.eclipse.microprofile.rest.client.annotation.RegisterProvider
アノテーションを使用して、OidcClientRequestReactiveFilter
を選択的に登録できます。
import org.eclipse.microprofile.rest.client.annotation.RegisterProvider; import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; import io.quarkus.oidc.client.filter.OidcClientFilter; import io.smallrye.mutiny.Uni; @RegisterRestClient @OidcClientFilter @Path("/") public interface ProtectedResourceService { @GET Uni<String> getUserName(); }
あるいは、以下のような場合もあります。
import org.eclipse.microprofile.rest.client.annotation.RegisterProvider; import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; import io.quarkus.oidc.client.reactive.filter.OidcClientRequestReactiveFilter; import io.smallrye.mutiny.Uni; @RegisterRestClient @RegisterProvider(OidcClientRequestReactiveFilter.class) @Path("/") public interface ProtectedResourceService { @GET Uni<String> getUserName(); }
OidcClientRequestReactiveFilter
はデフォルトの OidcClient
を使用します。名前付き OidcClient
は、quarkus.oidc-client-reactive-filter.client-name
設定プロパティーを使用して選択できます。@OidcClientFilter
アノテーションの value
属性を設定して、OidcClient
を選択することもできます。アノテーションを通じて設定されたクライアント名は、quarkus.oidc-client-reactive-filter.client-name
設定プロパティーよりも優先されます。たとえば、OIDC クライアント宣言という名前の こちら の jwt-secret
がある場合、このクライアントを次のように参照できます。
import org.eclipse.microprofile.rest.client.annotation.RegisterProvider; import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; import io.quarkus.oidc.client.filter.OidcClientFilter; import io.smallrye.mutiny.Uni; @RegisterRestClient @OidcClientFilter("jwt-secret") @Path("/") public interface ProtectedResourceService { @GET Uni<String> getUserName(); }
1.1.8. RestClient ClientFilter での OidcClient の使用
以下の Maven 依存関係を追加します。
<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-oidc-client-filter</artifactId> </dependency>
io.quarkus:quarkus-oidc-client
も導入されることに注意してください。
quarkus-oidc-client-filter
エクステンションは、OidcClient
を使用してアクセストークンを取得し、必要に応じて更新し、HTTP Authorization
Bearer
スキーム値として設定する io.quarkus.oidc.client.filter.OidcClientRequestFilter
Jakarta REST ClientRequestFilter を提供します。
デフォルトでは、このフィルターは、初期化時に OidcClient
がアクセストークンと更新トークンの最初のペアを取得するようにします。アクセストークンの有効期間が短く、更新トークンが利用できない場合は、quarkus.oidc-client.early-tokens-acquisition=false
を使用してトークンの取得を遅らせる必要があります。
io.quarkus.oidc.client.filter.OidcClientFilter
または org.eclipse.microprofile.rest.client.annotation.RegisterProvider
のいずれかのアノテーションを使用して、OidcClientRequestFilter
を選択的に登録できます。
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; import io.quarkus.oidc.client.filter.OidcClientFilter; @RegisterRestClient @OidcClientFilter @Path("/") public interface ProtectedResourceService { @GET String getUserName(); }
あるいは、以下のような場合もあります。
import org.eclipse.microprofile.rest.client.annotation.RegisterProvider; import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; import io.quarkus.oidc.client.filter.OidcClientRequestFilter; @RegisterRestClient @RegisterProvider(OidcClientRequestFilter.class) @Path("/") public interface ProtectedResourceService { @GET String getUserName(); }
または、quarkus.oidc-client-filter.register-filter=true
プロパティーが設定されていると、OidcClientRequestFilter
はすべての MP Rest または Jakarta REST クライアントに自動的に登録できます。
OidcClientRequestFilter
はデフォルトの OidcClient
を使用します。名前付き OidcClient
は、quarkus.oidc-client-filter.client-name
設定プロパティーを使用して選択できます。@OidcClientFilter
アノテーションの value
属性を設定して、OidcClient
を選択することもできます。アノテーションを通じて設定されたクライアント名は、quarkus.oidc-client-filter.client-name
設定プロパティーよりも優先されます。たとえば、OIDC クライアント宣言という名前の こちら の jwt-secret
がある場合、このクライアントを次のように参照できます。
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; import io.quarkus.oidc.client.filter.OidcClientFilter; @RegisterRestClient @OidcClientFilter("jwt-secret") @Path("/") public interface ProtectedResourceService { @GET String getUserName(); }
1.1.9. カスタム RestClient ClientFilter を使用する
必要に応じて、独自のカスタムフィルターを使用して Tokens
を注入することもできます。
import io.quarkus.oidc.client.Tokens; @Provider @Priority(Priorities.AUTHENTICATION) public class OidcClientRequestCustomFilter implements ClientRequestFilter { @Inject Tokens tokens; @Override public void filter(ClientRequestContext requestContext) throws IOException { requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, "Bearer " + tokens.getAccessToken()); } }
Tokens
プロデューサーはトークンを取得して更新し、カスタムフィルターはトークンの使用方法とタイミングを決定します。
名前付きの Tokens
を注入することもできます。名前付きの OidcClient とトークンの注入 を参照してください。
1.1.10. アクセストークンの更新
OidcClientRequestReactiveFilter
、OidcClientRequestFilter
、および Tokens
プロデューサーは、更新トークンが利用可能な場合、現在の期限切れのアクセストークンを更新します。さらに、quarkus.oidc-client.refresh-token-time-skew
プロパティーを使用して、アクセストークンをプリエンプティブに更新し、HTTP 401 エラーの原因となる可能性のある期限切れに近いアクセストークンの送信を回避することもできます。たとえば、このプロパティーが 3S
に設定され、アクセストークンの有効期限が 3 秒未満の場合、このトークンは自動的に更新されます。
アクセストークンを更新する必要があるが、更新トークンが利用できない場合は、client_credentials
などの設定された許可を使用して新しいトークンを取得しようとします。
一部の OpenID Connect プロバイダーは、client_credentials
グラントのレスポンスでリフレッシュトークンを返しません。たとえば、Keycloak 12 以降では、client_credentials
に対して更新トークンがデフォルトで返されなくなります。プロバイダーは、更新トークンの使用回数を制限する場合もあります。
1.1.11. アクセストークンの取り消し
Keycloak などの OpenId Connect プロバイダーがトークン失効エンドポイントをサポートしている場合は、OidcClient#revokeAccessToken
を使用して現在のアクセストークンを取り消すことができます。失効エンドポイント URL は、トークン要求 URI と一緒に検出されるか、quarkus.oidc-client.revoke-path
を使用して設定できます。
このトークンを REST クライアントで使用すると HTTP 401
ステータスコードで失敗した場合、またはアクセストークンがすでに長期間使用されていて更新したい場合は、アクセストークンの取り消しが必要になる場合があります。
これは、更新トークンを使用して、トークンの更新をリクエストすることによって実現できます。ただし、更新トークンが利用できない場合は、まず更新トークンを取り消してから新しいアクセストークンを要求することで更新できます。
1.1.12. OidcClient 認証
OidcClient
は、client_credentials
およびその他の許可要求が成功するために、OpenID Connect Provider に対して認証する必要があります。すべての OIDC クライアント認証 オプションがサポートされています。以下に例を示します。
client_secret_basic
:
quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/ quarkus.oidc-client.client-id=quarkus-app quarkus.oidc-client.credentials.secret=mysecret
あるいは、以下のような場合もあります。
quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/ quarkus.oidc-client.client-id=quarkus-app quarkus.oidc-client.credentials.client-secret.value=mysecret
または、CredentialsProvider から取得したシークレットを使用します。
quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/ quarkus.oidc-client.client-id=quarkus-app # This key is used to retrieve a secret from the map of credentials returned from CredentialsProvider quarkus.oidc-client.credentials.client-secret.provider.key=mysecret-key # Set it only if more than one CredentialsProvider can be registered quarkus.oidc-client.credentials.client-secret.provider.name=oidc-credentials-provider
client_secret_post
:
quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/ quarkus.oidc-client.client-id=quarkus-app quarkus.oidc-client.credentials.client-secret.value=mysecret quarkus.oidc-client.credentials.client-secret.method=post
client_secret_jwt
: 署名アルゴリズムは HS256
です。
quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/ quarkus.oidc-client.client-id=quarkus-app quarkus.oidc-client.credentials.jwt.secret=AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow
または、CredentialsProvider から取得したシークレットの場合、署名アルゴリズムは HS256
です。
quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/ quarkus.oidc-client.client-id=quarkus-app # This is a key that will be used to retrieve a secret from the map of credentials returned from CredentialsProvider quarkus.oidc-client.credentials.jwt.secret-provider.key=mysecret-key # Set it only if more than one CredentialsProvider can be registered quarkus.oidc-client.credentials.jwt.secret-provider.name=oidc-credentials-provider
PEM キーファイルを使用した private_key_jwt
: 署名アルゴリズムは RS256
です。
quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/ quarkus.oidc-client.client-id=quarkus-app quarkus.oidc-client.credentials.jwt.key-file=privateKey.pem
キーストアファイルを含む private_key_jwt
: 署名アルゴリズムは RS256
です。
quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/ quarkus.oidc-client.client-id=quarkus-app quarkus.oidc-client.credentials.jwt.key-store-file=keystore.jks quarkus.oidc-client.credentials.jwt.key-store-password=mypassword quarkus.oidc-client.credentials.jwt.key-password=mykeypassword # Private key alias inside the keystore quarkus.oidc-client.credentials.jwt.key-id=mykeyAlias
client_secret_jwt
または private_key_jwt
認証方法を使用すると、クライアントシークレットがネットワーク上に送信されなくなります。
1.1.12.1. 追加の JWT 認証オプション
client_secret_jwt
または private_key_jwt
のいずれかの認証方法を使用する場合は、JWT 署名アルゴリズム、キー識別子、audience、サブジェクト、発行者をカスタマイズできます。次に例を示します。
# private_key_jwt client authentication quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/ quarkus.oidc-client.client-id=quarkus-app quarkus.oidc-client.credentials.jwt.key-file=privateKey.pem # This is a token key identifier 'kid' header - set it if your OpenID Connect provider requires it. # Note that if the key is represented in a JSON Web Key (JWK) format with a `kid` property, then # using 'quarkus.oidc-client.credentials.jwt.token-key-id' is unnecessary. quarkus.oidc-client.credentials.jwt.token-key-id=mykey # Use the RS512 signature algorithm instead of the default RS256 quarkus.oidc-client.credentials.jwt.signature-algorithm=RS512 # The token endpoint URL is the default audience value; use the base address URL instead: quarkus.oidc-client.credentials.jwt.audience=${quarkus.oidc-client.auth-server-url} # custom subject instead of the client ID: quarkus.oidc-client.credentials.jwt.subject=custom-subject # custom issuer instead of the client ID: quarkus.oidc-client.credentials.jwt.issuer=custom-issuer
1.1.12.2. Apple POST JWT
Apple OpenID Connect プロバイダーは client_secret_post
メソッドを使用します。ここで、シークレットは private_key_jwt
認証メソッドで生成された JWT ですが、Apple アカウント固有の発行者およびサブジェクトプロパティーを持ちます。
quarkus-oidc-client
は非標準の client_secret_post_jwt
認証方式をサポートしており、次のように設定できます。
quarkus.oidc-client.auth-server-url=${apple.url} quarkus.oidc-client.client-id=${apple.client-id} quarkus.oidc-client.credentials.client-secret.method=post-jwt quarkus.oidc-client.credentials.jwt.key-file=ecPrivateKey.pem quarkus.oidc-client.credentials.jwt.signature-algorithm=ES256 quarkus.oidc-client.credentials.jwt.subject=${apple.subject} quarkus.oidc-client.credentials.jwt.issuer=${apple.issuer}
1.1.12.3. 相互 TLS
一部の OpenID Connect Provider では、相互 TLS (mTLS
) 認証プロセスの一部としてクライアントが認証されることが要求されます。
quarkus-oidc-client
は、mTLS
をサポートするために次のように設定できます。
quarkus.oidc-client.tls.verification=certificate-validation # Keystore configuration quarkus.oidc-client.tls.key-store-file=client-keystore.jks quarkus.oidc-client.tls.key-store-password=${key-store-password} # Add more keystore properties if needed: #quarkus.oidc-client.tls.key-store-alias=keyAlias #quarkus.oidc-client.tls.key-store-alias-password=keyAliasPassword # Truststore configuration quarkus.oidc-client.tls.trust-store-file=client-truststore.jks quarkus.oidc-client.tls.trust-store-password=${trust-store-password} # Add more truststore properties if needed: #quarkus.oidc-client.tls.trust-store-alias=certAlias
1.1.13. テスト
まず、テストプロジェクトに次の依存関係を追加します。
<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-junit5</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.awaitility</groupId> <artifactId>awaitility</artifactId> <scope>test</scope> </dependency>
1.1.13.1. Wiremock
テストプロジェクトに次の依存関係を追加します。
<dependency> <groupId>org.wiremock</groupId> <artifactId>wiremock</artifactId> <scope>test</scope> </dependency>
Wiremock ベースの QuarkusTestResourceLifecycleManager
を記述します。以下に例を示します。
package io.quarkus.it.keycloak; import static com.github.tomakehurst.wiremock.client.WireMock.matching; import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; import java.util.HashMap; import java.util.Map; import com.github.tomakehurst.wiremock.WireMockServer; import com.github.tomakehurst.wiremock.client.WireMock; import com.github.tomakehurst.wiremock.core.Options.ChunkedEncodingPolicy; import io.quarkus.test.common.QuarkusTestResourceLifecycleManager; public class KeycloakRealmResourceManager implements QuarkusTestResourceLifecycleManager { private WireMockServer server; @Override public Map<String, String> start() { server = new WireMockServer(wireMockConfig().dynamicPort().useChunkedTransferEncoding(ChunkedEncodingPolicy.NEVER)); server.start(); server.stubFor(WireMock.post("/tokens") .withRequestBody(matching("grant_type=password&username=alice&password=alice")) .willReturn(WireMock .aResponse() .withHeader("Content-Type", "application/json") .withBody( "{\"access_token\":\"access_token_1\", \"expires_in\":4, \"refresh_token\":\"refresh_token_1\"}"))); server.stubFor(WireMock.post("/tokens") .withRequestBody(matching("grant_type=refresh_token&refresh_token=refresh_token_1")) .willReturn(WireMock .aResponse() .withHeader("Content-Type", "application/json") .withBody( "{\"access_token\":\"access_token_2\", \"expires_in\":4, \"refresh_token\":\"refresh_token_1\"}"))); Map<String, String> conf = new HashMap<>(); conf.put("keycloak.url", server.baseUrl()); return conf; } @Override public synchronized void stop() { if (server != null) { server.stop(); server = null; } } }
REST テストエンドポイントを準備します。登録された OidcClient フィルターを使用して挿入された MP REST クライアントを使用するテストフロントエンドエンドポイントで、ダウンストリームエンドポイントを呼び出すことができます。このエンドポイントはトークンをエコーバックします。たとえば、main
の Quarkus リポジトリーの integration-tests/oidc-client-wiremock
を参照してください。
application.properties
を設定します。以下に例を示します。
# Use the 'keycloak.url' property set by the test KeycloakRealmResourceManager quarkus.oidc-client.auth-server-url=${keycloak.url} quarkus.oidc-client.discovery-enabled=false quarkus.oidc-client.token-path=/tokens quarkus.oidc-client.client-id=quarkus-service-app quarkus.oidc-client.credentials.secret=secret quarkus.oidc-client.grant.type=password quarkus.oidc-client.grant-options.password.username=alice quarkus.oidc-client.grant-options.password.password=alice
最後にテストコードを記述します。上記の Wiremock ベースのリソースを考えると、最初のテスト呼び出しは access_token_1
アクセストークンを返すはずですが、これは 4 秒後に期限切れになります。awaitility
を使用して約 5 秒間待機すると、次のテスト呼び出しで access_token_2
アクセストークンが返され、期限切れの access_token_1
アクセストークンが更新されたことが確認されます。
1.1.13.2. Keycloak
Keycloak を使用する場合は、Keycloak の OpenID Connect Bearer トークン結合テスト セクションで説明されているのと同じアプローチを使用できます。
1.1.14. ログ内のエラーを確認する方法
トークンの取得と更新エラーの詳細を確認するには、io.quarkus.oidc.client.runtime.OidcClientImpl
TRACE
レベルのログ記録を有効にします。
quarkus.log.category."io.quarkus.oidc.client.runtime.OidcClientImpl".level=TRACE quarkus.log.category."io.quarkus.oidc.client.runtime.OidcClientImpl".min-level=TRACE
OidcClient 初期化エラーの詳細を表示するには、io.quarkus.oidc.client.runtime.OidcClientRecorder
TRACE
レベルのログ記録を有効にします。
quarkus.log.category."io.quarkus.oidc.client.runtime.OidcClientRecorder".level=TRACE quarkus.log.category."io.quarkus.oidc.client.runtime.OidcClientRecorder".min-level=TRACE
1.2. OIDC リクエストフィルター
1 つ以上の OidcRequestFilter
実装を登録することで、Quarkus から OIDC プロバイダーに対して行われた OIDC リクエストをフィルタリングできます。これにより、新しいリクエストヘッダーを更新または追加できます。たとえば、フィルターはリクエスト本文を分析し、そのダイジェストを新しいヘッダー値として追加できます。
package io.quarkus.it.keycloak; import jakarta.enterprise.context.ApplicationScoped; import io.quarkus.arc.Unremovable; import io.quarkus.oidc.common.OidcRequestContextProperties; import io.quarkus.oidc.common.OidcRequestFilter; import io.vertx.core.http.HttpMethod; import io.vertx.mutiny.core.buffer.Buffer; import io.vertx.mutiny.ext.web.client.HttpRequest; @ApplicationScoped @Unremovable public class OidcRequestCustomizer implements OidcRequestFilter { @Override public void filter(HttpRequest<Buffer> request, Buffer buffer, OidcRequestContextProperties contextProperties) { HttpMethod method = request.method(); String uri = request.uri(); if (method == HttpMethod.POST && uri.endsWith("/service") && buffer != null) { request.putHeader("Digest", calculateDigest(buffer.toString())); } } private String calculateDigest(String bodyString) { // Apply the required digest algorithm to the body string } }
1.3. トークン伝播リアクティブ
quarkus-oidc-token-propagation-reactive
エクステンションは、認証情報の伝播を簡素化する RestEasy Reactive Client io.quarkus.oidc.token.propagation.reactive.AccessTokenRequestReactiveFilter
を提供します。このクライアントは、現在アクティブなリクエストに存在する ベアラートークン、または 認可コードフローメカニズム から取得したトークンを、HTTP Authorization
ヘッダーの Bearer
スキーム値として伝播します。
io.quarkus.oidc.token.propagation.AccessToken
または org.eclipse.microprofile.rest.client.annotation.RegisterProvider
アノテーションのいずれかを使用して、AccessTokenRequestReactiveFilter
を選択的に登録できます。次に例を示します。
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; import io.quarkus.oidc.token.propagation.AccessToken; @RegisterRestClient @AccessToken @Path("/") public interface ProtectedResourceService { @GET String getUserName(); }
あるいは、以下のような場合もあります。
import org.eclipse.microprofile.rest.client.annotation.RegisterProvider; import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; import io.quarkus.oidc.token.propagation.reactive.AccessTokenRequestReactiveFilter; @RegisterRestClient @RegisterProvider(AccessTokenRequestReactiveFilter.class) @Path("/") public interface ProtectedResourceService { @GET String getUserName(); }
さらに、AccessTokenRequestReactiveFilter
は、トークンを伝播する前に交換する必要がある複雑なアプリケーションをサポートできます。
Keycloak または トークン交換 トークングラントをサポートする別の OIDC プロバイダーを使用する場合は、次のように AccessTokenRequestReactiveFilter
を設定してトークンを交換できます。
quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus
quarkus.oidc-client.client-id=quarkus-app
quarkus.oidc-client.credentials.secret=secret
quarkus.oidc-client.grant.type=exchange
quarkus.oidc-client.grant-options.exchange.audience=quarkus-app-exchange
quarkus.oidc-token-propagation.exchange-token=true 1
- 1
- OidcClient 名が
io.quarkus.oidc.token.propagation.AccessToken#exchangeTokenClient
アノテーション属性で設定されている場合、exchange-token
設定プロパティーは無視されることに注意してください。
注記: AccessTokenRequestReactiveFilter
は OidcClient
を使用して現在のトークンを交換します。quarkus.oidc-client.grant-options.exchange
を使用して、OpenID Connect Provider が期待する追加の交換プロパティーを設定できます。
現在のトークンを交換するために JWT ベアラートークングラント を 使用する必要がある Azure
などのプロバイダーを使用する場合は、次のように AccessTokenRequestReactiveFilter
を設定してトークンを交換できます。
quarkus.oidc-client.auth-server-url=${azure.provider.url} quarkus.oidc-client.client-id=quarkus-app quarkus.oidc-client.credentials.secret=secret quarkus.oidc-client.grant.type=jwt quarkus.oidc-client.grant-options.jwt.requested_token_use=on_behalf_of quarkus.oidc-client.scopes=https://graph.microsoft.com/user.read,offline_access quarkus.oidc-token-propagation-reactive.exchange-token=true
AccessTokenRequestReactiveFilter
はデフォルトの OidcClient
を使用します。名前付きの OidcClient
は、quarkus.oidc-token-propagation-reactive.client-name
設定プロパティーまたは io.quarkus.oidc.token.propagation.AccessToken#exchangeTokenClient
アノテーション属性を使用して選択できます。
1.4. トークンの伝播
quarkus-oidc-token-propagation
エクステンションは、認証情報の伝播を簡素化する 2 つの Jakarta REST jakarta.ws.rs.client.ClientRequestFilter
クラス実装を提供します。io.quarkus.oidc.token.propagation.AccessTokenRequestFilter
は、現在アクティブなリクエストに存在する ベアラートークン、または 認可コードフローメカニズム から取得されたトークンを、HTTP Authorization
ヘッダーの Bearer
スキーム値として伝播します。io.quarkus.oidc.token.propagation.JsonWebTokenRequestFilter
は同じ機能を提供しますが、さらに JWT トークンのサポートも提供します。
現在の認可コードフローアクセストークンを伝播する必要がある場合は、即時トークン伝播が適切に機能します。これは、(ID トークンではなく) コードフローアクセストークンが、現在認証されているユーザーに代わってリモートサービスにアクセスするために現在の Quarkus エンドポイントに伝播されることを意図したものであるためです。
ただし、エンドツーエンドの Bearer トークンの直接的な伝播は回避する必要があります。たとえば、Client → Service A → Service B
の場合、Service B
は Client
から Service A
に送信されたトークンを受信します。このような場合、Service B
はトークンが Service A
から来たものか、Client
から直接来たものか区別できません。トークンが Service A
から送信されたことを Service B
が確認するには、新しい発行者と audience クレームをアサートできる必要があります。
さらに、複雑なアプリケーションでは、トークンを伝播する前に交換または更新しないといけない場合があります。たとえば、Service A
が Service B
にアクセスする場合、アクセスコンテキストが異なる場合があります。この場合、Service A
には、Service B
にアクセスするための狭い範囲またはまったく異なる範囲のセットが付与される可能性があります。
次のセクションでは、AccessTokenRequestFilter
と JsonWebTokenRequestFilter
がどのように役立つかを示します。
1.4.1. RestClient AccessTokenRequestFilter
AccessTokenRequestFilter
はすべてのトークンを文字列として扱うため、JWT トークンと不透明トークンの両方で動作します。
io.quarkus.oidc.token.propagation.AccessToken
または org.eclipse.microprofile.rest.client.annotation.RegisterProvider
のいずれかを使用して、AccessTokenRequestFilter
を選択的に登録できます。次に例を示します。
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; import io.quarkus.oidc.token.propagation.AccessToken; @RegisterRestClient @AccessToken @Path("/") public interface ProtectedResourceService { @GET String getUserName(); }
あるいは、以下のような場合もあります。
import org.eclipse.microprofile.rest.client.annotation.RegisterProvider; import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; import io.quarkus.oidc.token.propagation.AccessTokenRequestFilter; @RegisterRestClient @RegisterProvider(AccessTokenRequestFilter.class) @Path("/") public interface ProtectedResourceService { @GET String getUserName(); }
または、quarkus.oidc-token-propagation.register-filter
プロパティーが true
に設定され、quarkus.oidc-token-propagation.json-web-token
プロパティーが false
(デフォルト値) に設定されている場合は、AccessTokenRequestFilter
はすべての MP Rest または Jakarta REST クライアントに自動的に登録されます。
1.4.1.1. 伝播前のトークンの交換
現在のアクセストークンを伝播前に交換する必要があり、トークン交換 トークングラントをサポートする Keycloak またはその他の OpenID Connect Provider を使用する場合は、AccessTokenRequestFilter
を次のように設定できます。
quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus quarkus.oidc-client.client-id=quarkus-app quarkus.oidc-client.credentials.secret=secret quarkus.oidc-client.grant.type=exchange quarkus.oidc-client.grant-options.exchange.audience=quarkus-app-exchange quarkus.oidc-token-propagation.exchange-token=true
現在のトークンを交換するために JWT ベアラートークングラント を 使用する必要がある Azure
などのプロバイダーを使用する場合は、次のように AccessTokenRequestFilter
を設定してトークンを交換できます。
quarkus.oidc-client.auth-server-url=${azure.provider.url} quarkus.oidc-client.client-id=quarkus-app quarkus.oidc-client.credentials.secret=secret quarkus.oidc-client.grant.type=jwt quarkus.oidc-client.grant-options.jwt.requested_token_use=on_behalf_of quarkus.oidc-client.scopes=https://graph.microsoft.com/user.read,offline_access quarkus.oidc-token-propagation.exchange-token=true
注記: AccessTokenRequestFilter
は OidcClient
を使用して現在のトークンを交換します。quarkus.oidc-client.grant-options.exchange
を使用して、OpenID Connect Provider が期待する追加の交換プロパティーを設定できます。
AccessTokenRequestFilter
はデフォルトの OidcClient
を使用します。名前付き OidcClient
は、quarkus.oidc-token-propagation.client-name
設定プロパティーを使用して選択できます。
1.4.2. RestClient JsonWebTokenRequestFilter
Bearer JWT トークンを使用する場合、issuer
や audience
などのトークンのクレームを変更したり、更新されたトークンを再度保護 (再署名など) したりできる場合は、JsonWebTokenRequestFilter
を使用することを推奨します。注入された org.eclipse.microprofile.jwt.JsonWebToken
が想定されるため、不透明なトークンでは機能しません。また、OpenID Connect Provider が Token Exchange プロトコルをサポートしている場合は、代わりに AccessTokenRequestFilter
を使用することを推奨します。AccessTokenRequestFilter
を使用すると、JWT と不透明ベアラートークンの両方を安全に交換できるためです。
JsonWebTokenRequestFilter
を使用すると、Service A
の実装で、注入された org.eclipse.microprofile.jwt.JsonWebToken
を新しい issuer
と audience
のクレーム値で簡単に更新し、更新されたトークンを新しい署名で再度保護できるようになります。唯一の難しいステップは、Service A
に署名キーがあることを確認することです。署名キーは、安全なファイルシステムまたは Vault などのリモートの安全なストレージからプロビジョニングする必要があります。
io.quarkus.oidc.token.propagation.JsonWebToken
または org.eclipse.microprofile.rest.client.annotation.RegisterProvider
のいずれかを使用して、JsonWebTokenRequestFilter
を選択的に登録できます。次に例を示します。
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; import io.quarkus.oidc.token.propagation.JsonWebToken; @RegisterRestClient @JsonWebToken @Path("/") public interface ProtectedResourceService { @GET String getUserName(); }
あるいは、以下のような場合もあります。
import org.eclipse.microprofile.rest.client.annotation.RegisterProvider; import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; import io.quarkus.oidc.token.propagation.JsonWebTokenRequestFilter; @RegisterRestClient @RegisterProvider(JsonWebTokenRequestFilter.class) @Path("/") public interface ProtectedResourceService { @GET String getUserName(); }
あるいは、quarkus.oidc-token-propagation.register-filter
プロパティーと quarkus.oidc-token-propagation.json-web-token
プロパティーの両方が true
に設定されている場合、JsonWebTokenRequestFilter
がすべての MicroProfile REST または Jakarta REST クライアントに自動的に登録できます。
1.4.2.1. 伝播前のトークンの更新
注入されたトークンの iss
(issuer) または aud
(audience) クレームを更新し、新しい署名で再度保護する必要がある場合は、次のように JsonWebTokenRequestFilter
を設定できます。
quarkus.oidc-token-propagation.secure-json-web-token=true smallrye.jwt.sign.key.location=/privateKey.pem # Set a new issuer smallrye.jwt.new-token.issuer=http://frontend-resource # Set a new audience smallrye.jwt.new-token.audience=http://downstream-resource # Override the existing token issuer and audience claims if they are already set smallrye.jwt.new-token.override-matching-claims=true
前述のように、Keycloak または Token Exchange プロトコルをサポートする OpenID Connect Provider を使用する場合は、AccessTokenRequestFilter
を使用します。
1.4.3. テスト
OpenID Connect ベアラートークン結合テスト セクションの説明に従って、トークンを生成できます。REST テストエンドポイントを準備します。登録されたトークン伝播フィルターを備えた朝中された MP REST クライアントを使用するテストフロントエンドエンドポイントで、ダウンストリームエンドポイントを呼び出すことができます。たとえば、main
Quarkus リポジトリーの integration-tests/oidc-token-propagation
を参照してください。
1.5. トークン伝播リアクティブ
以下の Maven 依存関係を追加します。
<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-oidc-token-propagation-reactive</artifactId> </dependency>
quarkus-oidc-token-propagation-reactive
エクステンションは、現在の Bearer
または Authorization Code Flow
アクセストークンを伝播するために使用できる io.quarkus.oidc.token.propagation.reactive.AccessTokenRequestReactiveFilter
を提供します。
quarkus-oidc-token-propagation-reactive
エクステンション (非リアクティブ quarkus-oidc-token-propagation
エクステンションとは対照的に) は現在、伝播前のトークンの交換または再署名をサポートしていません。ただし、これらの機能は今後追加される可能性があります。
1.6. 参考資料
第2章 OpenID Connect クライアントとトークンの伝播クイックスタート
フィルター付きの OpenID Connect (OIDC) および OAuth2 クライアントを使用して、アプリケーションでアクセストークンを取得、更新、および伝播する方法を学習します。
Quarkus での OIDC Client
と Token Propagation
のサポートの詳細は、OpenID Connect (OIDC) および OAuth2 クライアントとフィルターのリファレンスガイド を参照してください。
ベアラートークン認可を使用してアプリケーションを保護するには、OpenID Connect (OIDC) ベアラートークン認証 ガイドを参照してください。
2.1. 前提条件
このガイドを完了するには、以下が必要です。
- 約 15 分
- IDE
-
JAV_HOME
が適切に設定された状態でインストールされた JDK 17+ - Apache Maven 3.9.6
- 動作するコンテナーランタイム (Docker または Podman)
- オプションで使用する場合は Quarkus CLI
- ネイティブ実行可能ファイル (ネイティブコンテナービルドを使用する場合は Docker) をビルドする必要がある場合は、オプションで Mandrel または GraalVM がインストールされ、適切に設定されている
- jq tool
2.2. アーキテクチャー
この例では、FrontendResource
と ProtectedResource
という 2 つの Jakarta REST リソースを使用してアプリケーションがビルドされます。ここで、FrontendResource
は、2 つの方法のいずれかを使用してアクセストークンを ProtectedResource
に伝播します。
- トークンを伝播する前に、OIDC トークン伝播リアクティブフィルターを使用してトークンを取得できます。
- OIDC トークン伝播リアクティブフィルターを使用して、受信したアクセストークンを伝播できます。
FrontendResource
には 4 つのエンドポイントがあります。
-
/frontend/user-name-with-oidc-client-token
-
/frontend/admin-name-with-oidc-client-token
-
/frontend/user-name-with-propagated-token
-
/frontend/admin-name-with-propagated-token
FrontendResource
は、/frontend/user-name-with-oidc-client-token
または /frontend/admin-name-with-oidc-client-token
のいずれかが呼び出されたときに、OIDC トークン伝播 Reactive フィルターを備えた REST クライアントを使用して、ProtectedResource
へのアクセストークンを取得して伝播します。また、FrontendResource
は、/frontend/user-name-with-propagated-token
または /frontend/admin-name-with-propagated-token
のいずれかが呼び出されたときに OpenID Connect Token Propagation Reactive Filter
を備えた REST Client を使用して、現在の受信アクセストークンを ProtectedResource
に伝播します。
ProtectedResource
には 2 つのエンドポイントがあります。
-
/protected/user-name
-
/protected/admin-name
両方のエンドポイントは、FrontendResource
から ProtectedResource
に伝播された受信アクセストークンから展開されたユーザー名を返します。これらのエンドポイントの唯一の違いは、/protected/user-name
の呼び出しは現在のアクセストークンに user
ロールがある場合にのみ許可され、/protected/admin-name
の呼び出しは現在のアクセストークンに admin
ロールがある場合にのみ許可されることです。
2.3. 解決方法
次のセクションの指示に従って、アプリケーションを段階的に作成することを推奨します。ただし、完成した例に直接進むこともできます。
Git リポジトリーをクローンします: git clone https://github.com/quarkusio/quarkus-quickstarts.git -b 3.8
、または アーカイブ をダウンロードします。
ソリューションは、security-openid-connect-client-quickstart
ディレクトリー にあります。
2.4. Maven プロジェクトの作成
まず、新しいプロジェクトが必要です。次のコマンドで新しいプロジェクトを作成します。
Quarkus CLI を使用:
quarkus create app org.acme:security-openid-connect-client-quickstart \ --extension='oidc,oidc-client-reactive-filter,oidc-token-propagation-reactive,resteasy-reactive' \ --no-code cd security-openid-connect-client-quickstart
Gradle プロジェクトを作成するには、
--gradle
または--gradle-kotlin-dsl
オプションを追加します。Quarkus CLI のインストール方法と使用方法の詳細は、Quarkus CLI ガイドを参照してください。
Maven を使用:
mvn io.quarkus.platform:quarkus-maven-plugin:3.8.5:create \ -DprojectGroupId=org.acme \ -DprojectArtifactId=security-openid-connect-client-quickstart \ -Dextensions='oidc,oidc-client-reactive-filter,oidc-token-propagation-reactive,resteasy-reactive' \ -DnoCode cd security-openid-connect-client-quickstart
Gradle プロジェクトを作成するには、
-DbuildTool=gradle
または-DbuildTool=gradle-kotlin-dsl
オプションを追加します。
Windows ユーザーの場合:
-
cmd を使用する場合は、バックスラッシュ
\
を使用せず、すべてを同じ行に記述してください。 -
Powershell を使用する場合は、
-D
パラメーターを二重引用符で囲みます (例:"-DprojectArtifactId=security-openid-connect-client-quickstart"
)。
このコマンドは、oidc
、oidc-client-reactive-filter
、oidc-token-propagation-reactive-filter
、および resteasy-reactive
エクステンションをインポートする Maven プロジェクトを生成します。
Quarkus プロジェクトがすでに設定されている場合は、プロジェクトベースディレクトリーで次のコマンドを実行して、これらのエクステンションをプロジェクトに追加できます。
Quarkus CLI を使用:
quarkus extension add oidc,oidc-client-reactive-filter,oidc-token-propagation-reactive,resteasy-reactive
Maven を使用:
./mvnw quarkus:add-extension -Dextensions='oidc,oidc-client-reactive-filter,oidc-token-propagation-reactive,resteasy-reactive'
Gradle を使用する場合:
./gradlew addExtension --extensions='oidc,oidc-client-reactive-filter,oidc-token-propagation-reactive,resteasy-reactive'
このコマンドは、ビルドファイルに次のエクステンションを追加します。
Maven を使用:
<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-oidc</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-oidc-client-reactive-filter</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-oidc-token-propagation-reactive</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-resteasy-reactive</artifactId> </dependency>
Gradle を使用する場合:
implementation("io.quarkus:quarkus-oidc,oidc-client-reactive-filter,oidc-token-propagation-reactive,resteasy-reactive")
2.5. アプリケーションの作成
まず、ProtectedResource
を実装します。
package org.acme.security.openid.connect.client; import jakarta.annotation.security.RolesAllowed; import jakarta.inject.Inject; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; import io.quarkus.security.Authenticated; import io.smallrye.mutiny.Uni; import org.eclipse.microprofile.jwt.JsonWebToken; @Path("/protected") @Authenticated public class ProtectedResource { @Inject JsonWebToken principal; @GET @RolesAllowed("user") @Produces("text/plain") @Path("userName") public Uni<String> userName() { return Uni.createFrom().item(principal.getName()); } @GET @RolesAllowed("admin") @Produces("text/plain") @Path("adminName") public Uni<String> adminName() { return Uni.createFrom().item(principal.getName()); } }
ProtectedResource
は、userName()
メソッドと adminName()
メソッドの両方から名前を返します。名前は現在の JsonWebToken
から展開されます。
次に、FrontendResource
が ProtectedResource
を呼び出すために使用する 2 つの REST クライアント OidcClientRequestReactiveFilter
および AccessTokenRequestReactiveFilter
を追加します。
OidcClientRequestReactiveFilter
REST クライアントを追加します。
package org.acme.security.openid.connect.client; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; import org.eclipse.microprofile.rest.client.annotation.RegisterProvider; import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; import io.quarkus.oidc.client.reactive.filter.OidcClientRequestReactiveFilter; import io.smallrye.mutiny.Uni; @RegisterRestClient @RegisterProvider(OidcClientRequestReactiveFilter.class) @Path("/") public interface RestClientWithOidcClientFilter { @GET @Produces("text/plain") @Path("userName") Uni<String> getUserName(); @GET @Produces("text/plain") @Path("adminName") Uni<String> getAdminName(); }
RestClientWithOidcClientFilter
インターフェイスは、トークンを取得して伝播するために OidcClientRequestReactiveFilter
に依存します。
AccessTokenRequestReactiveFilter
REST クライアントを追加します。
package org.acme.security.openid.connect.client; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; import org.eclipse.microprofile.rest.client.annotation.RegisterProvider; import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; import io.quarkus.oidc.token.propagation.reactive.AccessTokenRequestReactiveFilter; import io.smallrye.mutiny.Uni; @RegisterRestClient @RegisterProvider(AccessTokenRequestReactiveFilter.class) @Path("/") public interface RestClientWithTokenPropagationFilter { @GET @Produces("text/plain") @Path("userName") Uni<String> getUserName(); @GET @Produces("text/plain") @Path("adminName") Uni<String> getAdminName(); }
RestClientWithTokenPropagationFilter
インターフェイスは、受信する既存のトークンを伝播するために AccessTokenRequestReactiveFilter
に依存します。
RestClientWithOidcClientFilter
インターフェイスと RestClientWithTokenPropagationFilter
インターフェイスは、どちらも同じであることに注意してください。これは、同じ REST Client 上で OidcClientRequestReactiveFilter
および AccessTokenRequestReactiveFilter
を組み合わせると、両方のフィルターが相互に干渉する可能性があるため、副作用が発生するためです。たとえば、OidcClientRequestReactiveFilter
は、AccessTokenRequestReactiveFilter
によって伝播されたトークンをオーバーライドできます。また、伝播できるトークンがなく、代わりに OidcClientRequestReactiveFilter
が新しいトークンを取得することが予想されるときにそれが呼び出されると、AccessTokenRequestReactiveFilter
が失敗する可能性があります。
次に、FrontendResource
を追加してアプリケーションの作成を完了します。
package org.acme.security.openid.connect.client; import jakarta.inject.Inject; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; import org.eclipse.microprofile.rest.client.inject.RestClient; import io.smallrye.mutiny.Uni; @Path("/frontend") public class FrontendResource { @Inject @RestClient RestClientWithOidcClientFilter restClientWithOidcClientFilter; @Inject @RestClient RestClientWithTokenPropagationFilter restClientWithTokenPropagationFilter; @GET @Path("user-name-with-oidc-client-token") @Produces("text/plain") public Uni<String> getUserNameWithOidcClientToken() { return restClientWithOidcClientFilter.getUserName(); } @GET @Path("admin-name-with-oidc-client-token") @Produces("text/plain") public Uni<String> getAdminNameWithOidcClientToken() { return restClientWithOidcClientFilter.getAdminName(); } @GET @Path("user-name-with-propagated-token") @Produces("text/plain") public Uni<String> getUserNameWithPropagatedToken() { return restClientWithTokenPropagationFilter.getUserName(); } @GET @Path("admin-name-with-propagated-token") @Produces("text/plain") public Uni<String> getAdminNameWithPropagatedToken() { return restClientWithTokenPropagationFilter.getAdminName(); } }
FrontendResource
は、/frontend/user-name-with-oidc-client-token
または /frontend/admin-name-with-oidc-client-token
のいずれかが呼び出されたときに、OIDC トークン伝播 Reactive フィルターを備えた REST クライアントを使用して、ProtectedResource
へのアクセストークンを取得して伝播します。また、FrontendResource
は、/frontend/user-name-with-propagated-token
または /frontend/admin-name-with-propagated-token
のいずれかが呼び出されたときに OpenID Connect Token Propagation Reactive Filter
を備えた REST Client を使用して、現在の受信アクセストークンを ProtectedResource
に伝播します。
最後に、Jakarta REST ExceptionMapper
を追加します。
package org.acme.security.openid.connect.client; import jakarta.ws.rs.core.Response; import jakarta.ws.rs.ext.ExceptionMapper; import jakarta.ws.rs.ext.Provider; import org.jboss.resteasy.reactive.ClientWebApplicationException; @Provider public class FrontendExceptionMapper implements ExceptionMapper<ClientWebApplicationException> { @Override public Response toResponse(ClientWebApplicationException t) { return Response.status(t.getResponse().getStatus()).build(); } }
この例外マッパーは、トークンに予期されるロールがない場合に ProtectedResource
が 403
を返すことをテスト中に検証するためにのみ追加されます。このマッパーがなければ、RESTEasy Reactive
は、REST クライアント呼び出しから逃れた例外を 500
に正しく変換し、ProtectedResource
などのダウンストリームリソースからの情報の漏洩を回避します。ただし、テストでは、500
が何らかの内部エラーではなく、認可例外によって発生したと断言することはできません。
2.6. アプリケーションの設定
コードを準備したら、アプリケーションを設定します。
# Configure OIDC %prod.quarkus.oidc.auth-server-url=http://localhost:8180/realms/quarkus quarkus.oidc.client-id=backend-service quarkus.oidc.credentials.secret=secret # Tell Dev Services for Keycloak to import the realm file # This property is ineffective when running the application in JVM or Native modes but only in dev and test modes. quarkus.keycloak.devservices.realm-path=quarkus-realm.json # Configure OIDC Client quarkus.oidc-client.auth-server-url=${quarkus.oidc.auth-server-url} quarkus.oidc-client.client-id=${quarkus.oidc.client-id} quarkus.oidc-client.credentials.secret=${quarkus.oidc.credentials.secret} quarkus.oidc-client.grant.type=password quarkus.oidc-client.grant-options.password.username=alice quarkus.oidc-client.grant-options.password.password=alice # Configure REST clients %prod.port=8080 %dev.port=8080 %test.port=8081 org.acme.security.openid.connect.client.RestClientWithOidcClientFilter/mp-rest/url=http://localhost:${port}/protected org.acme.security.openid.connect.client.RestClientWithTokenPropagationFilter/mp-rest/url=http://localhost:${port}/protected
この設定は Keycloak を参照します。Keycloak は、ProtectedResource
によって受信アクセストークンを検証するために使用され、OidcClient
によって password
グラントを使用してユーザー alice
のトークンを取得するために使用されます。両方の REST クライアントは `ProtectedResource の HTTP アドレスを指します。
quarkus.oidc.auth-server-url
に %prod.
プロファイル接頭辞を追加すると、アプリケーションが開発モードまたはテストモードで実行されるときに、Dev Services for Keycloak
によってコンテナーが起動されるようになります。詳細は、開発モードでのアプリケーションの実行 セクションを参照してください。
2.7. Keycloak サーバーの起動と設定
アプリケーションを開発モードまたはテストモードで実行するときは、Keycloak サーバーを起動しないでください。Dev Services for Keycloak
がコンテナーを起動します。詳細は、開発モードでのアプリケーションの実行 セクションを参照してください。レルム設定ファイル を target/classes
ディレクトリーのクラスパスに配置するようにしてください。この配置により、ファイルが開発モードで自動的にインポートされるようになります。ただし、すでに 完全なソリューション をビルドしている場合は、ビルドプロセスによってすでに実行されているため、レルムファイルをクラスパスに追加する必要はありません。
Docker を使用して次のコマンドを実行するだけで、Keycloak サーバーを起動できます。
docker run --name keycloak -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin -p 8180:8080 quay.io/keycloak/keycloak:{keycloak.version} start-dev
{keycloak.version}
を 24.0.0
以降に設定します。
Keycloak サーバーには localhost:8180 からアクセスできます。
Keycloak 管理コンソールにアクセスするには、admin
ユーザーとしてログインします。パスワードは admin
です。
新しいレルムを作成するには、レルム設定ファイル をインポートします。詳細は、新しいレルムを作成する 方法に関する Keycloak のドキュメントを参照してください。
この quarkus
レルムファイルは、frontend
クライアントと、alice
および admin
ユーザーを追加します。alice
には user
ロールがあります。admin
にはuser
と admin
の両方のロールがあります。
2.8. 開発モードでのアプリケーションの実行
開発モードでアプリケーションを実行するには、次を使用します。
Quarkus CLI を使用:
quarkus dev
Maven を使用:
./mvnw quarkus:dev
Gradle を使用する場合:
./gradlew --console=plain quarkusDev
Dev Services for Keycloak が Keycloak コンテナーを起動し、quarkus-realm.json
をインポートします。
/q/dev-ui で利用可能な Dev UI を開き、OpenID Connect Dev UI カードの Provider: Keycloak
リンクをクリックします。
求められたら、OpenID Connect Dev UI によって提供される Single Page Application
にログインします。
パスワード
alice
を使用し、alice
としてログインします。このユーザーにはuser
ロールがあります。-
/frontend/user-name-with-propagated-token
にアクセスすると、200
が返されます。 -
/frontend/admin-name-with-propagated-token
にアクセスすると、403
が返されます。
-
ログアウトし、パスワード
admin
を使用してadmin
として再度ログインします。このユーザーにはadmin
とuser
の両方のロールがあります。-
/frontend/user-name-with-propagated-token
にアクセスすると、200
が返されます。 -
/frontend/admin-name-with-propagated-token
にアクセスすると、200
が返されます。
-
この場合、FrontendResource
が OpenID Connect Dev UI からアクセストークンを伝播できることをテストしています。
2.9. JVM モードでのアプリケーションの実行
開発モードでアプリケーションを試した後、標準の Java アプリケーションとして実行できます。
まず、コンパイルします。
Quarkus CLI を使用:
quarkus build
Maven を使用:
./mvnw install
Gradle を使用する場合:
./gradlew build
次に、実行します。
java -jar target/quarkus-app/quarkus-run.jar
2.10. ネイティブモードでアプリケーションの実行
このデモはネイティブコードにコンパイルできます。変更は必要ありません。
これは、生成されたバイナリーにランタイムテクノロジーが含まれ、最小限のリソースで実行するように最適化されているため、実稼働環境に JVM をインストールする必要がなくなることを意味します。
コンパイルには時間がかかるため、この手順はデフォルトでオフになっています。再度ビルドするには、native
プロファイルを有効にします。
Quarkus CLI を使用:
quarkus build --native
Maven を使用:
./mvnw install -Dnative
Gradle を使用する場合:
./gradlew build -Dquarkus.package.type=native
しばらくしてビルドが完了すると、ネイティブバイナリーを直接実行できます。
./target/security-openid-connect-quickstart-1.0.0-SNAPSHOT-runner
2.11. アプリケーションのテスト
開発モードでアプリケーションをテストする方法の詳細は、前述の 開発モードでのアプリケーションの実行 セクションを参照してください。
curl
を使用して、JVM モードまたはネイティブモードで起動されたアプリケーションをテストできます。
alice
のアクセストークンを取得します。
export access_token=$(\ curl --insecure -X POST http://localhost:8180/realms/quarkus/protocol/openid-connect/token \ --user backend-service:secret \ -H 'content-type: application/x-www-form-urlencoded' \ -d 'username=alice&password=alice&grant_type=password' | jq --raw-output '.access_token' \ )
次に、このトークンを使用して、/frontend/user-name-with-propagated-token
および /frontend/admin-name-with-propagated-token
を呼び出します。
curl -i -X GET \ http://localhost:8080/frontend/user-name-with-propagated-token \ -H "Authorization: Bearer "$access_token
このコマンドは、ステータスコード 200
と名前 alice
を返します。
curl -i -X GET \ http://localhost:8080/frontend/admin-name-with-propagated-token \ -H "Authorization: Bearer "$access_token
対照的に、このコマンドは 403
を返します。alice
には user
ロールのみがあることを思い出してください。
次に、admin
のアクセストークンを取得します。
export access_token=$(\ curl --insecure -X POST http://localhost:8180/realms/quarkus/protocol/openid-connect/token \ --user backend-service:secret \ -H 'content-type: application/x-www-form-urlencoded' \ -d 'username=admin&password=admin&grant_type=password' | jq --raw-output '.access_token' \ )
このトークンを使用して /frontend/user-name-with-propagated-token
を呼び出します。
curl -i -X GET \ http://localhost:8080/frontend/user-name-with-propagated-token \ -H "Authorization: Bearer "$access_token
このコマンドは、ステータスコード 200
と名前 admin
を返します。
次に、このトークンを使用して /frontend/admin-name-with-propagated-token
を呼び出します。
curl -i -X GET \ http://localhost:8080/frontend/admin-name-with-propagated-token \ -H "Authorization: Bearer "$access_token
admin
に user
と admin
の両方のロールがあるため、このコマンドは、200
ステータスコードと名前 admin
も返します。
ここで、既存のトークンを伝播せず、OidcClient
を使用してトークンを取得および伝播する FrontendResource
メソッドを確認します。すでに示したように、OidcClient
は alice
ユーザーのトークンを取得するように設定されているため、次のようになります。
curl -i -X GET \ http://localhost:8080/frontend/user-name-with-oidc-client-token
このコマンドは、ステータスコード 200
と名前 alice
を返します。
curl -i -X GET \ http://localhost:8080/frontend/admin-name-with-oidc-client-token
前のコマンドとは対照的に、このコマンドは 403
ステータスコードを返します。