OpenID Connect (OIDC) と OAuth2 クライアントおよびフィルター
Quarkus エクステンションは、トークンの取得、更新、伝播を対象に、OpenID Connect および OAuth 2.0 アクセストークンの管理に使用できます。
これには、以下が含まれます。
-
quarkus-oidc-client
および、quarkus-rest-client-oidc-filter
、quarkus-resteasy-client-oidc-filter
エクステンションを使用して、OpenID Connect や Keycloak などの OAuth 2.0 準拠の認可サーバーからアクセストークンを取得し、更新する。 -
quarkus-rest-client-oidc-token-propagation
とquarkus-resteasy-client-oidc-token-propagation
のエクステンションを使用して、現在のBearer
またはAuthorization Code Flow
アクセストークンを伝播する。
これらのエクステンションで管理されているアクセストークンをHTTP Authorization ベアラートークンとして使用して、リモートサービスにアクセスすることができます。
OpenID Connect Clientとトークン伝搬クイックスタート も参照してください。
OidcClient
以下の依存関係を追加します。
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-oidc-client</artifactId>
</dependency>
quarkus-oidc-client
エクステンションは、SmallRye Mutiny Uni
および Vert.xWebClient
を使用してトークンを取得および更新するために使用できるリアクティブな io.quarkus.oidc.client.OidcClient
を提供します。
OidcClient
はビルド時に IDP トークンエンドポイント URL (自動検出または手動設定) で初期化され、このエンドポイントを使用して client_credentials
や password
などのトークングラントを使用してアクセストークンを取得し、refresh_token
グラントを使用してトークンを更新できます。
トークンのエンドポイント設定
デフォルトでは、トークンのエンドポイントアドレスは、設定された 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
の設定は必要ありません。
サポートされるトークングラント
OidcClient
がトークンを取得するために使用できる主なトークングラントは、client_credentials
(デフォルト) と password
グラントです。
クライアントクレデンシャル・グラント
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
パスワード・グラント
OidcClient
が password
グラントを使用するように設定する方法は以下の通りです。
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
という設定プレフィックスを使用して、クライアント認証のグラントをカスタマイズする方法と同様に、カスタマイズすることが可能です。
その他のグラント
OidcClient
は設定にはない特別な入力パラメーターを必要とするグラントを使用して、トークンの取得を支援することもできます。これらのグラントとは、 refresh_token
(外部リフレッシュトークン付き)、authorization_code
、および現在のアクセストークンを交換するために使用できる 2 つのグラント urn:ietf:params:oauth:grant-type:token-exchange
および `urn:ietf:params:oauth:grant-type:jwt-bearer`です。
アクセストークンを取得する必要があり、既存のリフレッシュトークンを現在の Quarkus エンドポイントに POST している場合は、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
メソッドと提供されたリフレッシュトークンを使用して、アクセストークンを取得できます。
Using the urn:ietf:params:oauth:grant-type:token-exchange
or urn:ietf:params:oauth:grant-type:jwt-bearer
grants might be required if you are building a complex microservices application and want to avoid the same Bearer
token be propagated to and used by more than one service. See Token Propagation for Quarkus REST and Token Propagation for RESTEasy Classic for more details.
何らかの理由で 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
メソッドで追加プロパティーの Map を受け取り、現在の 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
メソッドを使用して追加プロパティーの Map を受け入れ、auth_req_id
パラメーターを渡してトークン認証コードを交換できます。
OidcClient を直接使用する
OidcClient
を直接使用してアクセストークンを取得して HTTP Authorization
ヘッダーに Bearer
スキーム値として設定できます。
たとえば、Quarkus エンドポイントがユーザー名を返すマイクロサービスにアクセスする必要があるとします。 まず、REST クライアントを作成します。
package org.acme.security.openid.connect.client;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import io.smallrye.mutiny.Uni;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.HeaderParam;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
@RegisterRestClient
@Path("/")
public interface RestClientWithTokenHeaderParam {
@GET
@Produces("text/plain")
@Path("userName")
Uni<String> getUserName(@HeaderParam("Authorization") String authorization);
}
次に、OidcClient
を使用してトークンを取得し、伝播します。
package org.acme.security.openid.connect.client;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import io.quarkus.oidc.client.runtime.TokensHelper;
import io.quarkus.oidc.client.OidcClient;
import io.smallrye.mutiny.Uni;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
@Path("/service")
public class OidcClientResource {
@Inject
OidcClient client;
TokensHelper tokenHelper = new TokensHelper(); (1)
@Inject
@RestClient
RestClientWithTokenHeaderParam restClient;
@GET
@Path("user-name")
@Produces("text/plain")
public Uni<String> getUserName() {
return tokenHelper.getTokens(client).onItem()
.transformToUni(tokens -> restClient.getUserName("Bearer " + tokens.getAccessToken()));
}
}
1 | io.quarkus.oidc.client.runtime.TokensHelper は、アクセストークンの取得と更新を管理します。 |
トークンの注入
内部で OidcClient
を使用する Tokens
を注入できます。 Tokens
はアクセストークンを取得し、必要に応じて更新するために使用できます。
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
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
}
}
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 org.eclipse.microprofile.rest.client.inject.RestClient;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import io.smallrye.mutiny.Uni;
import io.quarkus.oidc.client.OidcClient;
import io.quarkus.oidc.client.OidcClients;
import io.quarkus.oidc.client.runtime.TokensHelper;
@Path("/clients")
public class OidcClientResource {
@Inject
OidcClients clients;
TokensHelper tokenHelper = new TokensHelper();
@Inject
@RestClient
RestClientWithTokenHeaderParam restClient; (1)
@GET
@Path("user-name")
@Produces("text/plain")
public Uni<String> getUserName() {
OidcClient client = clients.getClient("jwt-secret");
return tokenHelper.getTokens(client).onItem()
.transformToUni(tokens -> restClient.getUserName("Bearer " + tokens.getAccessToken()));
}
}
1 | OidcClient を直接使用する セクションの RestClientWithTokenHeaderParam 宣言を参照してください。 |
OIDCマルチテナンシー も使用し、各 OIDC テナントに独自の関連付けられた
|
プログラムで新しい OidcClient
を作成することもできます。たとえば、起動時に作成する必要があるとします。
package org.acme.security.openid.connect.client;
import java.util.Map;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import io.quarkus.oidc.client.OidcClient;
import io.quarkus.oidc.client.OidcClientConfig;
import io.quarkus.oidc.client.OidcClientConfig.Grant.Type;
import io.quarkus.oidc.client.OidcClients;
import io.quarkus.runtime.StartupEvent;
import io.smallrye.mutiny.Uni;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.event.Observes;
import jakarta.inject.Inject;
@ApplicationScoped
public class OidcClientCreator {
@Inject
OidcClients oidcClients;
@ConfigProperty(name = "quarkus.oidc.auth-server-url")
String oidcProviderAddress;
private volatile OidcClient oidcClient;
public void startup(@Observes StartupEvent event) {
createOidcClient().subscribe().with(client -> {oidcClient = client;});
}
public OidcClient getOidcClient() {
return oidcClient;
}
private Uni<OidcClient> createOidcClient() {
OidcClientConfig cfg = new OidcClientConfig();
cfg.setId("myclient");
cfg.setAuthServerUrl(oidcProviderAddress);
cfg.setClientId("backend-service");
cfg.getCredentials().setSecret("secret");
cfg.getGrant().setType(Type.PASSWORD);
cfg.setGrantOptions(Map.of("password",
Map.of("username", "alice", "password", "alice")));
return oidcClients.newClient(cfg);
}
}
このクライアントは次のように使用できます。
import org.eclipse.microprofile.rest.client.inject.RestClient;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import io.smallrye.mutiny.Uni;
import io.quarkus.oidc.client.runtime.TokensHelper;
@Path("/clients")
public class OidcClientResource {
@Inject
OidcClientCreator oidcClientCreator;
TokensHelper tokenHelper = new TokensHelper();
@Inject
@RestClient
RestClientWithTokenHeaderParam restClient; (1)
@GET
@Path("user-name")
@Produces("text/plain")
public Uni<String> getUserName() {
return tokenHelper.getTokens(oidcClientCreator.getOidcClient()).onItem()
.transformToUni(tokens -> restClient.getUserName("Bearer " + tokens.getAccessToken()));
}
}
1 | OidcClient を直接使用する セクションの RestClientWithTokenHeaderParam 宣言を参照してください。 |
名前の付いた OidcClient とトークンの注入
複数の OidcClient
オブジェクトが設定されている場合、OidcClients
を使用する代わりに、追加の修飾子 @NamedOidcClient
で OidcClient
の注入ターゲットを指定できます。
package org.acme.security.openid.connect.client;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import io.smallrye.mutiny.Uni;
import io.quarkus.oidc.client.NamedOidcClient;
import io.quarkus.oidc.client.OidcClient;
import io.quarkus.oidc.client.runtime.TokensHelper;
@Path("/clients")
public class OidcClientResource {
@Inject
@NamedOidcClient("jwt-secret")
OidcClient client;
TokensHelper tokenHelper = new TokensHelper();
@Inject
@RestClient
RestClientWithTokenHeaderParam restClient; (1)
@GET
@Path("user-name")
@Produces("text/plain")
public Uni<String> getUserName() {
return tokenHelper.getTokens(client).onItem()
.transformToUni(tokens -> restClient.getUserName("Bearer " + tokens.getAccessToken()));
}
}
1 | OidcClient を直接使用する セクションの RestClientWithTokenHeaderParam 宣言を参照してください。 |
同じ修飾子を使用して、Tokens
注入に使用する OidcClient
を指定することができます。
import java.io.IOException;
import jakarta.annotation.Priority;
import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.Priorities;
import jakarta.ws.rs.client.ClientRequestContext;
import jakarta.ws.rs.client.ClientRequestFilter;
import jakarta.ws.rs.core.HttpHeaders;
import jakarta.ws.rs.ext.Provider;
import io.quarkus.oidc.client.NamedOidcClient;
import io.quarkus.oidc.client.Tokens;
@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());
}
}
RestClient の Reactive ClientFilter で OidcClient を使用する
以下の Maven 依存関係を追加します。
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-client-oidc-filter</artifactId>
</dependency>
It will also bring io.quarkus:quarkus-oidc-client .
|
quarkus-rest-client-oidc-filter
エクステンションは、io.quarkus.oidc.client.filter.OidcClientRequestReactiveFilter
を提供します。
It works similarly to the way OidcClientRequestFilter
does (see Use OidcClient in MicroProfile RestClient client filter) - it uses OidcClient
to acquire the access token, refresh it if needed, and set it as an HTTP Authorization Bearer
scheme value. The difference is that it works with Reactive RestClient and implements a non-blocking client filter that does not block the current IO thread when acquiring or refreshing the tokens.
OidcClientRequestReactiveFilter
は、IO スレッドのブロックを回避するために、最初のトークンの取得が実行されるまで遅延します。
io.quarkus.oidc.client.reactive.filter.OidcClientFilter
または org.eclipse.microprofile.rest.client.annotation.RegisterProvider
アノテーションを使用して、OidcClientRequestReactiveFilter
を選択的に登録できます。
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import io.quarkus.oidc.client.filter.OidcClientFilter;
import io.smallrye.mutiny.Uni;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
@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;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
@RegisterRestClient
@RegisterProvider(OidcClientRequestReactiveFilter.class)
@Path("/")
public interface ProtectedResourceService {
@GET
Uni<String> getUserName();
}
OidcClientRequestReactiveFilter
は、デフォルトの OidcClient
を使用します。名前付きの OidcClient
は、quarkus.rest-client-oidc-filter.client-name
設定プロパティーで選択できます。
@OidcClientFilter
アノテーションの value
属性を設定して OidcClient
を選択することもできます。アノテーションを使用して設定されたクライアント名は、quarkus.rest-client-oidc-filter.client-name
設定プロパティーよりも優先されます。
たとえば、この jwt-secret
という名前の OIDC クライアント宣言では、このクライアントを次のように参照できます。
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import io.quarkus.oidc.client.filter.OidcClientFilter;
import io.smallrye.mutiny.Uni;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
@RegisterRestClient
@OidcClientFilter("jwt-secret")
@Path("/")
public interface ProtectedResourceService {
@GET
Uni<String> getUserName();
}
RestClient の ClientFilter で OidcClient を使用する
以下の Maven 依存関係を追加します。
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-client-oidc-filter</artifactId>
</dependency>
It will also bring io.quarkus:quarkus-oidc-client .
|
quarkus-resteasy-client-oidc-filter
extension provides io.quarkus.oidc.client.filter.OidcClientRequestFilter
Jakarta REST ClientRequestFilter which uses OidcClient
to acquire the access token, refresh it if needed, and set it as an HTTP Authorization Bearer
scheme value.
デフォルトでは、このフィルタは初期化時に 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;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
@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;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
@RegisterRestClient
@RegisterProvider(OidcClientRequestFilter.class)
@Path("/")
public interface ProtectedResourceService {
@GET
String getUserName();
}
あるいは、quarkus.resteasy-client-oidc-filter.register-filter=true
プロパティーが設定されている場合、すべての MP Rest または Jakarta REST クライアントで OidcClientRequestFilter
を自動的に登録することもできます。
OidcClientRequestFilter
は、デフォルトの OidcClient
を使用します。名前付きの OidcClient
は、quarkus.resteasy-client-oidc-filter.client-name
設定プロパティーで選択できます。
@OidcClientFilter
アノテーションの value
属性を設定して OidcClient
を選択することもできます。アノテーションを使用して設定されたクライアント名は、quarkus.resteasy-client-oidc-filter.client-name
設定プロパティーよりも優先されます。
たとえば、this jwt-secret
という名前の OIDC クライアント宣言では、このクライアントを次のように参照できます。
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import io.quarkus.oidc.client.filter.OidcClientFilter;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
@RegisterRestClient
@OidcClientFilter("jwt-secret")
@Path("/")
public interface ProtectedResourceService {
@GET
String getUserName();
}
カスタムの RestClient ClientFilter を使用する
ご希望の場合は、独自のカスタムフィルターを使用して、 Tokens
を注入することができます。
import java.io.IOException;
import jakarta.annotation.Priority;
import jakarta.inject.Inject;
import jakarta.ws.rs.Priorities;
import jakarta.ws.rs.client.ClientRequestContext;
import jakarta.ws.rs.client.ClientRequestFilter;
import jakarta.ws.rs.core.HttpHeaders;
import jakarta.ws.rs.ext.Provider;
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とトークンの注入 を参照してください。
アクセストークンを更新する
OidcClientRequestReactiveFilter
、OidcClientRequestFilter
、および Tokens
プロデューサーは、リフレッシュトークンが利用可能な場合、現在の期限切れのアクセストークンを更新します。
さらに、HTTP 401 エラーの原因となる可能性のある期限切れに近いアクセストークンの送信を回避するために、quarkus.oidc-client.refresh-token-time-skew
プロパティーを使用して事前にアクセストークンを更新できます。たとえば、このプロパティーが 3S
に設定されている場合、アクセストークンが 3 秒未満に失効し、このトークンは自動的に更新されます。
アクセストークンの更新が必要なのにリフレッシュトークンがない場合は、 client_credentials
のように設定されたグラントを使って新しいトークンの取得を試みます。
OpenID Connect プロバイダーによっては、 client_credentials
グラントレスポンスでリフレッシュトークンを返さないものがあることに注意してください。たとえば、Keycloak 12 以降では、 client_credentials
に対してデフォルトでリフレッシュトークンが返されません。また、プロバイダーはリフレッシュトークンの使用回数を制限している場合があります。
アクセストークンを失効する
Keycloak などの OpenId Connect プロバイダーがトークンの失効エンドポイントをサポートしている場合、OidcClient#revokeAccessToken
を使うことで現在のアクセストークンを失効させることができます。失効エンドポイントの URL は、トークン要求 URI と共に検出されるか、または quarkus.oidc-client.revoke-path
で設定することができます。
このトークンを REST クライアントで使用すると HTTP 401
ステータスコードで失敗するか、アクセストークンがすでに長期間使用されていて更新する場合は、アクセストークンを取り消してください。
リフレッシュトークンを使用してトークンのリフレッシュを要求することができます。リフレッシュトークンが利用できない場合は、まずそれを失効させてから、新しいアクセストークンをリクエストすることで更新できます。
OidcClient 認証
OidcClient
は、client_credentials
および他のグラントリクエストを成功させるために OpenID Connect プロバイダーに対して認証する必要があります。 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
または、クレデンシャルプロバイダー から取得したシークレットを使用:
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
# This is the keyring provided to the CredentialsProvider when looking up the secret, set only if required by the CredentialsProvider implementation
quarkus.oidc.credentials.client-secret.provider.keyring-name=oidc
# 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
# This is the keyring provided to the CredentialsProvider when looking up the secret, set only if required by the CredentialsProvider implementation
quarkus.oidc.credentials.client-secret.provider.keyring-name=oidc
# Set it only if more than one CredentialsProvider can be registered
quarkus.oidc-client.credentials.jwt.secret-provider.name=oidc-credentials-provider
private_key_jwt
with the PEM key inlined in application.properties, and where the signature algorithm is 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=Base64-encoded private key representation
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.pkcs12
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
認証方法を使用することで、クライアントシークレットが漏れることはありません。
JWT認証の追加オプション
client_secret_jwt
と private_key_jwt
のいずれかの認証方法を使用する場合、JWT 署名アルゴリズム、鍵識別子、オーディエンス、サブジェクト、発行者などをカスタマイズすることが可能です。
# 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
JWT ベアラー
RFC7523 では、JWT ベアラートークンを使用してクライアントを認証する方法について説明しています。詳細は、クライアント認証での JWT の使用 セクションを参照してください。
有効にするには、以下を実行します。
quarkus.oidc-client.auth-server-url=${auth-server-url}
quarkus.oidc-client.client-id=quarkus-app
quarkus.oidc-client.credentials.jwt.source=bearer
次に、JWT ベアラートークンを client_assertion
パラメーターとして OIDC クライアントに提供する必要があります。
追加のグラントパラメーターを受け入れるトークンを取得または更新するには、OidcClient
メソッドを使用できます (例: oidcClient.getTokens(Map.of("client_assertion", "ey…"))
)。
OIDC クライアントフィルターを使用する場合は、このアサーションを提供するカスタムフィルターを登録する必要があります。
以下は、Quarkus REST (旧称 RESTEasy Reactive) カスタムフィルターの例です。
package io.quarkus.it.keycloak;
import java.util.Map;
import io.quarkus.oidc.client.reactive.filter.runtime.AbstractOidcClientRequestReactiveFilter;
import io.quarkus.oidc.common.runtime.OidcConstants;
import jakarta.annotation.Priority;
import jakarta.ws.rs.Priorities;
@Priority(Priorities.AUTHENTICATION)
public class OidcClientRequestCustomFilter extends AbstractOidcClientRequestReactiveFilter {
@Override
protected Map<String, String> additionalParameters() {
return Map.of(OidcConstants.CLIENT_ASSERTION, "ey...");
}
}
Here is an example of the RESTEasy Classic custom filter:
package io.quarkus.it.keycloak;
import java.util.Map;
import io.quarkus.oidc.client.filter.runtime.AbstractOidcClientRequestFilter;
import io.quarkus.oidc.common.runtime.OidcConstants;
import jakarta.annotation.Priority;
import jakarta.ws.rs.Priorities;
@Priority(Priorities.AUTHENTICATION)
public class OidcClientRequestCustomFilter extends AbstractOidcClientRequestFilter {
@Override
protected Map<String, String> additionalParameters() {
return Map.of(OidcConstants.CLIENT_ASSERTION, "ey...");
}
}
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}
相互 TLS
一部の OpenID Connect プロバイダーでは、相互 TLS (mTLS
) 認証プロセスの一部としてクライアントを認証する必要がある場合があります。
mTLS
をサポートする為に quarkus-oidc-client
は以下のように設定出来ます :
quarkus.oidc-client.tls.tls-configuration-name=oidc-client
# configure hostname verification if necessary
#quarkus.tls.oidc-client.hostname-verification-algorithm=NONE
# Keystore configuration
quarkus.tls.oidc-client.key-store.p12.path=client-keystore.p12
quarkus.tls.oidc-client.key-store.p12.password=${key-store-password}
# Add more keystore properties if needed:
#quarkus.tls.oidc-client.key-store.p12.alias=keyAlias
#quarkus.tls.oidc-client.key-store.p12.alias-password=keyAliasPassword
# Truststore configuration
quarkus.tls.oidc-client.trust-store.p12.path=client-truststore.p12
quarkus.tls.oidc-client.trust-store.p12.password=${trust-store-password}
# Add more truststore properties if needed:
#quarkus.tls.oidc-client.trust-store.p12.alias=certAlias
OIDC Client SPI
When your custom extension must acquire OIDC tokens using one of the OIDC token grants supported by OIDC client, this extension can depend on the OIDC Client SPI only and let OIDC client itself acquire and refresh access tokens as necessary.
以下の依存関係を追加します。
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-oidc-client-spi</artifactId>
</dependency>
Next update your extension to use io.quarkus.oidc.client.spi.TokenProvider
CDI bean as required, for example:
package org.acme.extension;
import jakarta.inject.Inject;
import io.quarkus.oidc.client.spi.TokenProvider;
public class ExtensionOAuth2Support {
@Inject
TokenProvider tokenProvider;
public Uni<String> getAccessToken() {
return tokenProvider.getAccessToken();
}
}
Currently, io.quarkus.oidc.client.spi.TokenProvider
is only available for default OIDC clients, since custom extensions are unlikely to be aware of multiple named OIDC clients.
テスト
テストプロジェクトに以下の依存関係を追加することから始めます。
<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>
Wiremock
テストプロジェクトに以下の依存関係を追加します。
<dependency>
<groupId>org.wiremock</groupId>
<artifactId>wiremock</artifactId>
<scope>test</scope>
<version>${wiremock.version}</version> (1)
</dependency>
1 | 適切なWiremockバージョンを使用してください。利用可能なすべてのバージョンは、 こちら をご覧ください。 |
以下のように、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 テストエンドポイントを用意します。注入された MP REST クライアントを登録された OidcClient フィルターで使用するテスト用フロントエンドエンドポイントが、トークンをエコーバックするダウンストリームエンドポイントを呼び出すことができます。例として、 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:replaced-by-test-resource}
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
アクセストークンが更新されたことが確認できます。
Keycloak
If you work with Keycloak, you can use the same approach described in the OpenID Connect Bearer Token Integration testing Keycloak section.
ログでエラーを確認する方法
トークン取得および更新エラーの詳細を確認するには、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
OIDC リクエストフィルター
You can filter OIDC requests made by OIDC client to the OIDC provider by registering one or more OidcRequestFilter
implementations, which can update or add new request headers, or analyze the request body.
You can have a single filter intercepting requests to all OIDC provider endpoints, or use an @OidcEndpoint
annotation to apply this filter to requests to specific endpoints only. For example:
package io.quarkus.it.keycloak;
import jakarta.enterprise.context.ApplicationScoped;
import io.quarkus.arc.Unremovable;
import io.quarkus.oidc.common.OidcEndpoint;
import io.quarkus.oidc.common.OidcRequestFilter;
import io.vertx.core.http.HttpMethod;
@ApplicationScoped
@OidcEndpoint(value = Type.TOKEN)
@Unremovable
public class OidcRequestCustomizer implements OidcRequestFilter {
@Override
public void filter(OidcRequestContext requestContext) {
HttpMethod method = requestContext.request().method();
String uri = requestContext.request().uri();
if (method == HttpMethod.POST && uri.endsWith("/token") && requestContext.requestBody() != null) {
requestContext.request().putHeader("Digest", calculateDigest(requestContext.requestBody().toString()));
}
}
private String calculateDigest(String bodyString) {
// Apply the required digest algorithm to the body string
}
}
OidcRequestContextProperties
can be used to access request properties.
Currently, you can use a client_id
key to access the client tenant id and a grant_type
key to access the grant type which the OIDC client uses to acquire tokens.
OIDC response filters
You can filter responses to the OIDC client requests by registering one or more OidcResponseFilter
implementations, which can check the response status, headers and body, in order to log them or perform other actions.
You can have a single filter intercepting responses to all OIDC client requests, or use an @OidcEndpoint
annotation to apply this filter to the responses to the specific OIDC client requests only. For example:
package io.quarkus.it.keycloak;
import jakarta.enterprise.context.ApplicationScoped;
import org.jboss.logging.Logger;
import io.quarkus.arc.Unremovable;
import io.quarkus.oidc.common.OidcEndpoint;
import io.quarkus.oidc.common.OidcEndpoint.Type;
import io.quarkus.oidc.common.OidcResponseFilter;
import io.quarkus.oidc.common.runtime.OidcConstants;
@ApplicationScoped
@Unremovable
@OidcEndpoint(value = Type.TOKEN) (1)
public class TokenEndpointResponseFilter implements OidcResponseFilter {
private static final Logger LOG = Logger.getLogger(TokenEndpointResponseFilter.class);
@Override
public void filter(OidcResponseContext rc) {
String contentType = rc.responseHeaders().get("Content-Type"); (2)
if (contentType.equals("application/json")
&& "refresh_token".equals(rc.requestProperties().get(OidcConstants.GRANT_TYPE)) (3)
&& rc.responseBody().toJsonObject().containsKey("refresh_token")) { (4)
LOG.debug("Tokens have been refreshed");
}
}
}
1 | Restrict this filter to requests targeting the OIDC token endpoint only. |
2 | Check the response Content-Type header. |
3 | Use OidcRequestContextProperties request properties to confirm it is a refresh_grant token grant response. |
4 | Confirm the response JSON contains a refresh_token property. |
Token Propagation for Quarkus REST
The quarkus-rest-client-oidc-token-propagation
extension provides a REST Client filter, io.quarkus.oidc.token.propagation.reactive.AccessTokenRequestReactiveFilter
, that simplifies the propagation of authentication information. This client propagates the bearer token present in the currently active request or the token acquired from the authorization code flow mechanism as the HTTP Authorization
header’s Bearer
scheme value.
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;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
@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;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
@RegisterRestClient
@RegisterProvider(AccessTokenRequestReactiveFilter.class)
@Path("/")
public interface ProtectedResourceService {
@GET
String getUserName();
}
さらに、 AccessTokenRequestReactiveFilter
、トークンを伝播する前に交換する必要がある複雑なアプリケーションをサポートすることができます。
If you work with Keycloak or another OIDC provider that supports a Token Exchange token grant, then you can configure AccessTokenRequestReactiveFilter
to exchange the token like this:
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.resteasy-client-oidc-token-propagation.exchange-token=true (1)
1 | OidcClient 名が io.quarkus.oidc.token.propagation.AccessToken#exchangeTokenClient アノテーション属性で設定されている場合、exchange-token 設定プロパティーは無視されることに注意してください。 |
AccessTokenRequestReactiveFilter will use OidcClient to exchange the current token, and you can use quarkus.oidc-client.grant-options.exchange to set the additional exchange properties expected by your OpenID Connect Provider.
|
Azure
のように、JWTベアラートークングラント を使用して現在のトークンを交換する 必要がある プロバイダーを使用する場合、 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.resteasy-client-oidc-token-propagation.exchange-token=true
AccessTokenRequestReactiveFilter
uses a default OidcClient
by default. A named OidcClient
can be selected with a quarkus.rest-client-oidc-token-propagation.client-name
configuration property or with the io.quarkus.oidc.token.propagation.AccessToken#exchangeTokenClient
annotation attribute.
Token Propagation for RESTEasy Classic
quarkus-resteasy-client-oidc-token-propagation
エクステンションは、認証情報の伝播を簡素化する 2 つの Jakarta REST jakarta.ws.rs.client.ClientRequestFilter
クラス実装を提供します。
io.quarkus.oidc.token.propagation.AccessTokenRequestFilter
は、現在アクティブなリクエストに存在する Bearer token または Authorization code flow mechanism から取得したトークンを、HTTP Authorization
ヘッダーの Bearer
スキーム値として伝播します。
io.quarkus.oidc.token.propagation.JsonWebTokenRequestFilter
は同じ機能を提供しますが、さらに JWT トークンのサポートも提供します。
現在の認可コードフローアクセストークンを伝播する必要がある場合、即時トークン伝播が適切に機能します。これは、 (ID トークンではなく) コードフローアクセストークンが、現在認証されているユーザーに代わってリモートサービスにアクセスするために現在の Quarkus エンドポイントに伝播されるように想定されているためです。
ただし、エンドツーエンドのベアラートークンの直接的な伝播は避ける必要があります。たとえば、Client → Service A → Service B
の場合、Service B
は Client
から Service A
に送信されたトークンを受信します。このような場合、Service B
はトークンが Service A
から送信されたのか、直接 Client
から送信されたのかを区別できません。Service B
で、トークンの送信元が Service A
であることを確認するには、新しい発行者とオーディエンスのクレームをアサートできる必要があります。
さらに、複雑なアプリケーションでは、トークンを伝搬する前に交換または更新する必要がある場合があります。たとえば、Service A
が Service B
にアクセスするとき、アクセスコンテキストが異なる場合があります。この例では、Service B
にアクセスするために Service A
に付与されるスコープは、範囲が狭いか、完全に異なる場合があります。
以下のセクションでは、AccessTokenRequestFilter
と JsonWebTokenRequestFilter
がどのように役立つかを説明します。
RestClient AccessTokenRequestFilter
AccessTokenRequestFilter
は、すべてのトークンをStringとして扱うため、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;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
@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;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
@RegisterRestClient
@RegisterProvider(AccessTokenRequestFilter.class)
@Path("/")
public interface ProtectedResourceService {
@GET
String getUserName();
}
Alternatively, AccessTokenRequestFilter
can be registered automatically with all MP Rest or Jakarta REST clients if the quarkus.resteasy-client-oidc-token-propagation.register-filter
property is set to true
and quarkus.resteasy-client-oidc-token-propagation.json-web-token
property is set to false
(which is a default value).
伝播前にトークンを交換する
If the current access token needs to be exchanged before propagation and you work with Keycloak or other OpenID Connect Provider which supports a Token Exchange token grant, then you can configure AccessTokenRequestFilter
like this:
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.resteasy-client-oidc-token-propagation.exchange-token=true
Azure
のように、 JWTベアラートークングラント を使用して現在のトークンを交換する 必要がある プロバイダーを使用する場合、 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.resteasy-client-oidc-token-propagation.exchange-token=true
AccessTokenRequestFilter will use OidcClient to exchange the current token, and you can use quarkus.oidc-client.grant-options.exchange to set the additional exchange properties expected by your OpenID Connect Provider.
|
AccessTokenRequestFilter
uses a default OidcClient
by default. A named OidcClient
can be selected with a quarkus.resteasy-client-oidc-token-propagation.client-name
configuration property.
RestClient JsonWebTokenRequestFilter
JsonWebTokenRequestFilter
の使用は、Bearer JWT トークンを扱う場合に推奨します。これらのトークンは issuer
や audience
などのクレームを変更でき、更新したトークンを再度セキュア (例: 再署名) に保つことができます。これは注入された org.eclipse.microprofile.jwt.JsonWebToken
を想定しているため、不透明なトークンでは動作しません。また、OpenID Connect プロバイダーが Token Exchange プロトコルをサポートしている場合は、代わりに AccessTokenRequestFilter
を使用することを推奨します。JWT と不透明な bearer トークンの両方を AccessTokenRequestFilter
で安全に交換できます。
JsonWebTokenRequestFilter
makes it easy for Service A
implementations to update the injected org.eclipse.microprofile.jwt.JsonWebToken
with the new issuer
and audience
claim values and secure the updated token again with a new signature. The only difficult step is ensuring that Service A
has a signing key which should be provisioned from a secure file system or remote secure storage such as 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;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
@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;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
@RegisterRestClient
@RegisterProvider(JsonWebTokenRequestFilter.class)
@Path("/")
public interface ProtectedResourceService {
@GET
String getUserName();
}
Alternatively, JsonWebTokenRequestFilter
can be registered automatically with all MicroProfile REST or Jakarta REST clients if both quarkus.resteasy-client-oidc-token-propagation.register-filter
and quarkus.resteasy-client-oidc-token-propagation.json-web-token
properties are set to true
.
伝播前にトークンを更新する
注入されたトークンの iss
(issuer) や aud
(audience) のクレームを更新して、新しい署名で保護する必要がある場合は、次のように JsonWebTokenRequestFilter
を設定できます。
quarkus.resteasy-client-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 またはトークン交換プロトコルをサポートする OpenID Connect プロバイダーを使用する場合は、AccessTokenRequestFilter
を使用します。
テスト
Typically, you must prepare two REST test endpoints. The first endpoint uses the injected MP REST client with a registered token propagation filter to call the second endpoint.
To learn how it can be done, please follow the OpenID Connect client and token propagation quickstart, and its Testing section in particular.
GraphQL クライアントインテグレーション
quarkus-oidc-client-graphql
エクステンションは、REST クライアントで使用されるアプローチと並行して、OIDC クライアントを GraphQL クライアント に統合する方法を提供します。
このエクステンションがアクティブな場合、プロパティーを通じて設定された GraphQL クライアント (ビルダーによってプログラム的にではなく) は OIDC クライアントを使用してアクセストークンを取得して Authorization
ヘッダー値として設定します。
この OIDC クライアントは期限切れのアクセストークンも更新します。
以下のように、GraphQL クライアントが使用する OIDC クライアントを設定するには、 quarkus.oidc-client-graphql.client-name
プロパティーが指定された設定済みの OIDC クライアントの 1 つを選択します。
quarkus.oidc-client-graphql.client-name=oidc-client-for-graphql # OIDC クライアント自体の宣言例 quarkus.oidc-client.oidc-client-for-graphql.auth-server-url=${keycloak.url} quarkus.oidc-client.oidc-client-for-graphql.grant.type=password quarkus.oidc-client.oidc-client-for-graphql.grant-options.password.username=${username} quarkus.oidc-client.oidc-client-for-graphql.grant-options.password.password=${password} quarkus.oidc-client.oidc-client-for-graphql.client-id=${quarkus.oidc.client-id} quarkus.oidc-client.oidc-client-for-graphql.credentials.client-secret.value=${keycloak.credentials.secret} quarkus.oidc-client.oidc-client-for-graphql.credentials.client-secret.method=POST
`quarkus.oidc-client-graphql.client-name`プロパティーを指定しない場合は、 GraphQL クライアントは、デフォルトの OIDC クライアント (明示的な名前なし) を使用します。 |
以下のように、特に型安全な GraphQL クライアントの場合、
GraphQLClientApi
インターフェイスに @io.quarkus.oidc.client.filter.OidcClientFilter
のアノテーションを付けることで、これをクライアントごとにオーバーライドできます。
@GraphQLClientApi(configKey = "order-client")
@OidcClientFilter("oidc-client-for-graphql")
public interface OrdersGraphQLClient {
// Queries, mutations, and subscriptions go here.
}
これをプログラムで作成された GraphQL クライアントで使用できるようにするために、
両方のビルダー (VertxDynamicGraphQLClientBuilder
と
VertxTypesafeGraphQLClientBuilder
) は dynamicHeader(String,
Uni<String>
) メソッドを含んでおり、変更される可能性のあるヘッダーをプラグインできます。
OIDC クライアントを接続するには、以下を使用します。
@Inject
OidcClients oidcClients;
VertxTypesafeGraphQLClientBuilder builder = ....;
Uni<String> tokenUni = oidcClients.getClient("OIDC_CLIENT_NAME")
.getTokens().map(t -> "Bearer " + t.getAccessToken());
builder.dynamicHeader("Authorization", tokenUni);
VertxDynamicGraphQLClient client = builder.build();
設定リファレンス
OIDC クライアント
ビルド時に固定される構成プロパティ - 他のすべての構成プロパティは実行時にオーバーライド可能
Configuration property |
型 |
デフォルト |
---|---|---|
If the OIDC client extension is enabled. Environment variable: Show more |
boolean |
|
The base URL of the OpenID Connect (OIDC) server, for example, Environment variable: Show more |
string |
|
Discovery of the OIDC endpoints. If not enabled, you must configure the OIDC endpoint URLs individually. Environment variable: Show more |
boolean |
|
The relative path or absolute URL of the OIDC dynamic client registration endpoint. Set if Environment variable: Show more |
string |
|
The duration to attempt the initial connection to an OIDC server. For example, setting the duration to Environment variable: Show more |
||
The number of times to retry re-establishing an existing OIDC connection if it is temporarily lost. Different from Environment variable: Show more |
int |
|
The number of seconds after which the current OIDC connection request times out. Environment variable: Show more |
|
|
Whether DNS lookup should be performed on the worker thread. Use this option when you can see logged warnings about blocked Vert.x event loop by HTTP requests to OIDC server. Environment variable: Show more |
boolean |
|
The maximum size of the connection pool used by the WebClient. Environment variable: Show more |
int |
|
The host name or IP address of the Proxy. Environment variable: Show more |
string |
|
The port number of the Proxy. The default value is Environment variable: Show more |
int |
|
The username, if the Proxy needs authentication. Environment variable: Show more |
string |
|
The password, if the Proxy needs authentication. Environment variable: Show more |
string |
|
The name of the TLS configuration to use. If a name is configured, it uses the configuration from The default TLS configuration is not used by default. Environment variable: Show more |
string |
|
The OIDC token endpoint that issues access and refresh tokens; specified as a relative path or absolute URL. Set if Environment variable: Show more |
string |
|
The relative path or absolute URL of the OIDC token revocation endpoint. Environment variable: Show more |
string |
|
The client id of the application. Each application has a client id that is used to identify the application. Setting the client id is not required if Environment variable: Show more |
string |
|
The client name of the application. It is meant to represent a human readable description of the application which you may provide when an application (client) is registered in an OpenId Connect provider’s dashboard. For example, you can set this property to have more informative log messages which record an activity of the given client. Environment variable: Show more |
string |
|
The client secret used by the Environment variable: Show more |
string |
|
The client secret value. This value is ignored if Environment variable: Show more |
string |
|
The CredentialsProvider bean name, which should only be set if more than one CredentialsProvider is registered Environment variable: Show more |
string |
|
The CredentialsProvider keyring name. The keyring name is only required when the CredentialsProvider being used requires the keyring name to look up the secret, which is often the case when a CredentialsProvider is shared by multiple extensions to retrieve credentials from a more dynamic source like a vault instance or secret manager Environment variable: Show more |
string |
|
The CredentialsProvider client secret key Environment variable: Show more |
string |
|
The authentication method. If the Environment variable: Show more |
|
|
JWT token source: OIDC provider client or an existing JWT bearer token. Environment variable: Show more |
|
|
If provided, indicates that JWT is signed using a secret key. It is mutually exclusive with Environment variable: Show more |
string |
|
The CredentialsProvider bean name, which should only be set if more than one CredentialsProvider is registered Environment variable: Show more |
string |
|
The CredentialsProvider keyring name. The keyring name is only required when the CredentialsProvider being used requires the keyring name to look up the secret, which is often the case when a CredentialsProvider is shared by multiple extensions to retrieve credentials from a more dynamic source like a vault instance or secret manager Environment variable: Show more |
string |
|
The CredentialsProvider client secret key Environment variable: Show more |
string |
|
String representation of a private key. If provided, indicates that JWT is signed using a private key in PEM or JWK format. It is mutually exclusive with Environment variable: Show more |
string |
|
If provided, indicates that JWT is signed using a private key in PEM or JWK format. It is mutually exclusive with Environment variable: Show more |
string |
|
If provided, indicates that JWT is signed using a private key from a keystore. It is mutually exclusive with Environment variable: Show more |
string |
|
A parameter to specify the password of the keystore file. Environment variable: Show more |
string |
|
The private key id or alias. Environment variable: Show more |
string |
|
The private key password. Environment variable: Show more |
string |
|
The JWT audience ( Environment variable: Show more |
string |
|
The key identifier of the signing key added as a JWT Environment variable: Show more |
string |
|
The issuer of the signing key added as a JWT Environment variable: Show more |
string |
|
Subject of the signing key added as a JWT Environment variable: Show more |
string |
|
Additional claims. Environment variable: Show more |
Map<String,String> |
|
The signature algorithm used for the Environment variable: Show more |
string |
|
The JWT lifespan in seconds. This value is added to the time at which the JWT was issued to calculate the expiration time. Environment variable: Show more |
int |
|
If true then the client authentication token is a JWT bearer grant assertion. Instead of producing 'client_assertion' and 'client_assertion_type' form properties, only 'assertion' is produced. This option is only supported by the OIDC client extension. Environment variable: Show more |
boolean |
|
A unique OIDC client identifier. It must be set when OIDC clients are created dynamically and is optional in all other cases. Environment variable: Show more |
string |
|
If this client configuration is enabled. Environment variable: Show more |
boolean |
|
List of access token scopes Environment variable: Show more |
list of string |
|
Refresh token time skew. If this property is enabled then the configured duration is converted to seconds and is added to the current time when checking whether the access token should be refreshed. If the sum is greater than this access token’s expiration time then a refresh is going to happen. Environment variable: Show more |
||
Access token expiration period relative to the current time. This property is only checked when an access token grant response does not include an access token expiration property. Environment variable: Show more |
||
If the access token 'expires_in' property should be checked as an absolute time value as opposed to a duration relative to the current time. Environment variable: Show more |
boolean |
|
Grant type Environment variable: Show more |
|
|
Access token property name in a token grant response Environment variable: Show more |
string |
|
Refresh token property name in a token grant response Environment variable: Show more |
string |
|
Access token expiry property name in a token grant response Environment variable: Show more |
string |
|
Refresh token expiry property name in a token grant response Environment variable: Show more |
string |
|
Grant options Environment variable: Show more |
Map<String,Map<String,String>> |
|
Requires that all filters which use 'OidcClient' acquire the tokens at the post-construct initialization time, possibly long before these tokens are used. This property should be disabled if the access token may expire before it is used for the first time and no refresh token is available. Environment variable: Show more |
boolean |
|
Custom HTTP headers which have to be sent to the token endpoint Environment variable: Show more |
Map<String,String> |
|
型 |
デフォルト |
|
The base URL of the OpenID Connect (OIDC) server, for example, Environment variable: Show more |
string |
|
Discovery of the OIDC endpoints. If not enabled, you must configure the OIDC endpoint URLs individually. Environment variable: Show more |
boolean |
|
The relative path or absolute URL of the OIDC dynamic client registration endpoint. Set if Environment variable: Show more |
string |
|
The duration to attempt the initial connection to an OIDC server. For example, setting the duration to Environment variable: Show more |
||
The number of times to retry re-establishing an existing OIDC connection if it is temporarily lost. Different from Environment variable: Show more |
int |
|
The number of seconds after which the current OIDC connection request times out. Environment variable: Show more |
|
|
Whether DNS lookup should be performed on the worker thread. Use this option when you can see logged warnings about blocked Vert.x event loop by HTTP requests to OIDC server. Environment variable: Show more |
boolean |
|
The maximum size of the connection pool used by the WebClient. Environment variable: Show more |
int |
|
The host name or IP address of the Proxy. Environment variable: Show more |
string |
|
The port number of the Proxy. The default value is Environment variable: Show more |
int |
|
The username, if the Proxy needs authentication. Environment variable: Show more |
string |
|
The password, if the Proxy needs authentication. Environment variable: Show more |
string |
|
The name of the TLS configuration to use. If a name is configured, it uses the configuration from The default TLS configuration is not used by default. Environment variable: Show more |
string |
|
The OIDC token endpoint that issues access and refresh tokens; specified as a relative path or absolute URL. Set if Environment variable: Show more |
string |
|
The relative path or absolute URL of the OIDC token revocation endpoint. Environment variable: Show more |
string |
|
The client id of the application. Each application has a client id that is used to identify the application. Setting the client id is not required if Environment variable: Show more |
string |
|
The client name of the application. It is meant to represent a human readable description of the application which you may provide when an application (client) is registered in an OpenId Connect provider’s dashboard. For example, you can set this property to have more informative log messages which record an activity of the given client. Environment variable: Show more |
string |
|
The client secret used by the Environment variable: Show more |
string |
|
The client secret value. This value is ignored if Environment variable: Show more |
string |
|
The CredentialsProvider bean name, which should only be set if more than one CredentialsProvider is registered Environment variable: Show more |
string |
|
The CredentialsProvider keyring name. The keyring name is only required when the CredentialsProvider being used requires the keyring name to look up the secret, which is often the case when a CredentialsProvider is shared by multiple extensions to retrieve credentials from a more dynamic source like a vault instance or secret manager Environment variable: Show more |
string |
|
The CredentialsProvider client secret key Environment variable: Show more |
string |
|
The authentication method. If the Environment variable: Show more |
|
|
JWT token source: OIDC provider client or an existing JWT bearer token. Environment variable: Show more |
|
|
If provided, indicates that JWT is signed using a secret key. It is mutually exclusive with Environment variable: Show more |
string |
|
The CredentialsProvider bean name, which should only be set if more than one CredentialsProvider is registered Environment variable: Show more |
string |
|
The CredentialsProvider keyring name. The keyring name is only required when the CredentialsProvider being used requires the keyring name to look up the secret, which is often the case when a CredentialsProvider is shared by multiple extensions to retrieve credentials from a more dynamic source like a vault instance or secret manager Environment variable: Show more |
string |
|
The CredentialsProvider client secret key Environment variable: Show more |
string |
|
String representation of a private key. If provided, indicates that JWT is signed using a private key in PEM or JWK format. It is mutually exclusive with Environment variable: Show more |
string |
|
If provided, indicates that JWT is signed using a private key in PEM or JWK format. It is mutually exclusive with Environment variable: Show more |
string |
|
If provided, indicates that JWT is signed using a private key from a keystore. It is mutually exclusive with Environment variable: Show more |
string |
|
A parameter to specify the password of the keystore file. Environment variable: Show more |
string |
|
The private key id or alias. Environment variable: Show more |
string |
|
The private key password. Environment variable: Show more |
string |
|
The JWT audience ( Environment variable: Show more |
string |
|
The key identifier of the signing key added as a JWT Environment variable: Show more |
string |
|
The issuer of the signing key added as a JWT Environment variable: Show more |
string |
|
Subject of the signing key added as a JWT Environment variable: Show more |
string |
|
Additional claims. Environment variable: Show more |
Map<String,String> |
|
The signature algorithm used for the Environment variable: Show more |
string |
|
The JWT lifespan in seconds. This value is added to the time at which the JWT was issued to calculate the expiration time. Environment variable: Show more |
int |
|
If true then the client authentication token is a JWT bearer grant assertion. Instead of producing 'client_assertion' and 'client_assertion_type' form properties, only 'assertion' is produced. This option is only supported by the OIDC client extension. Environment variable: Show more |
boolean |
|
A unique OIDC client identifier. It must be set when OIDC clients are created dynamically and is optional in all other cases. Environment variable: Show more |
string |
|
If this client configuration is enabled. Environment variable: Show more |
boolean |
|
List of access token scopes Environment variable: Show more |
list of string |
|
Refresh token time skew. If this property is enabled then the configured duration is converted to seconds and is added to the current time when checking whether the access token should be refreshed. If the sum is greater than this access token’s expiration time then a refresh is going to happen. Environment variable: Show more |
||
Access token expiration period relative to the current time. This property is only checked when an access token grant response does not include an access token expiration property. Environment variable: Show more |
||
If the access token 'expires_in' property should be checked as an absolute time value as opposed to a duration relative to the current time. Environment variable: Show more |
boolean |
|
Grant type Environment variable: Show more |
|
|
Access token property name in a token grant response Environment variable: Show more |
string |
|
Refresh token property name in a token grant response Environment variable: Show more |
string |
|
Access token expiry property name in a token grant response Environment variable: Show more |
string |
|
Refresh token expiry property name in a token grant response Environment variable: Show more |
string |
|
Grant options Environment variable: Show more |
Map<String,Map<String,String>> |
|
Requires that all filters which use 'OidcClient' acquire the tokens at the post-construct initialization time, possibly long before these tokens are used. This property should be disabled if the access token may expire before it is used for the first time and no refresh token is available. Environment variable: Show more |
boolean |
|
Custom HTTP headers which have to be sent to the token endpoint Environment variable: Show more |
Map<String,String> |
期間フォーマットについて
To write duration values, use the standard 数字で始まる簡略化した書式を使うこともできます:
その他の場合は、簡略化されたフォーマットが解析のために
|
OIDCトークンの伝搬
ビルド時に固定される構成プロパティ - 他のすべての構成プロパティは、実行時にオーバーライド可能
Configuration property |
タイプ |
デフォルト |
---|---|---|
If the OIDC Token Reactive Propagation is enabled. Environment variable: Show more |
ブーリアン |
|
Whether the token propagation is enabled during the For example, you may need to use a REST client from Note, this feature relies on a duplicated context. More information about Vert.x duplicated context can be found in this guide. Environment variable: Show more |
ブーリアン |
|
Exchange the current token with OpenId Connect Provider for a new token using either "urn:ietf:params:oauth:grant-type:token-exchange" or "urn:ietf:params:oauth:grant-type:jwt-bearer" token grant before propagating it. Environment variable: Show more |
ブーリアン |
|
Name of the configured OidcClient. Note this property is only used if the Environment variable: Show more |
string |