Quarkus エクステンションは、トークンの取得、更新、伝播に重点を置いて、OpenID Connect および OAuth 2.0 アクセストークンの管理に使用できます。
これらのエクステンションによって管理されるアクセストークンは、リモートサービスにアクセスするための HTTP 認可ベアラートークンとして使用できます。
次の依存関係を追加します。
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-oidc-client</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-oidc-client</artifactId>
</dependency>
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
quarkus-oidc-client エクステンションは、SmallRye Mutiny Uni および Vert.x WebClient を使用してトークンを取得および更新するのに使用できるリアクティブ io.quarkus.oidc.client.OidcClient を提供します。
OidcClient はビルド時に IDP トークンエンドポイント URL を使用して初期化されます。これは自動検出または手動で設定できます。OidcClient はこのエンドポイントを使用して、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
quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
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
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
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
検出なしでトークンエンドポイント 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.token-path=http://localhost:8180/auth/realms/quarkus/protocol/openid-connect/tokens
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
この場合、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
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
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
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
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
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
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.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
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
クライアントクレデンシャルのグラントをカスタマイズする方法と同様に、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 エンドポイントにすでに送信した場合は、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
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
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
次に、提供された更新トークンを使用して OidcClient.refreshTokens メソッドを使用してアクセストークンを取得できます。
複雑なマイクロサービスアプリケーションを構築していて、同じ Bearer トークンが複数のサービスに伝播されて使用されるのを避ける必要がある場合、urn:ietf:params:oauth:grant-type:token-exchange または urn:ietf:params:oauth:grant-type:jwt-bearer グラントの使用が必要になることがあります。詳細は、Quarkus REST のトークン伝播 および RESTEasy Classic のトークン伝播 を参照してください。
何らかの理由で、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
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
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
次に、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
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
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
次に、OidcClient.accessTokens メソッドを使用して追加プロパティーのマップを受け入れ、auth_req_id パラメーターを渡してトークン認可コードを交換できます。
発行されたアクセストークンに特定のスコープのセットを関連付けるように要求しないといけない場合があります。専用の quarkus.oidc-client.scopes リストプロパティーを使用します (例: quarkus.oidc-client.scopes=email,phone)。
OidcClient を直接使用してアクセストークンを取得し、それを Bearer スキーム値として HTTP Authorization ヘッダーに設定できます。
たとえば、ユーザー名を返すマイクロサービスに 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);
}
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);
}
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
次に、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();
@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()));
}
}
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()));
}
}
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
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
}
}
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
}
}
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
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
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
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
この場合、デフォルトのクライアントは 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;
@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()));
}
}
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()));
}
}
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
OIDC マルチテナンシー も使用しており、各 OIDC テナントに独自の OidcClient が関連付けられている場合は、Vert.x RoutingContext tenant-id 属性を使用できます。以下に例を示します。
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("tenant-id");
// named OIDC tenant and client configurations use the same key:
OidcClient client = clients.getClient(tenantId);
//Use this client to get the token
}
}
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("tenant-id");
// named OIDC tenant and client configurations use the same key:
OidcClient client = clients.getClient(tenantId);
//Use this client to get the token
}
}
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
プログラムで新しい OidcClient を作成することもできます。たとえば、起動時に 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.runtime.OidcClientConfig;
import io.quarkus.oidc.client.runtime.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 = OidcClientConfig
.authServerUrl(oidcProviderAddress)
.id("myclient")
.clientId("backend-service")
.credentials("secret")
.grant(Type.PASSWORD)
.grantOptions("password", Map.of("username", "alice", "password", "alice"))
.build();
return oidcClients.newClient(cfg);
}
}
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.runtime.OidcClientConfig;
import io.quarkus.oidc.client.runtime.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 = OidcClientConfig
.authServerUrl(oidcProviderAddress)
.id("myclient")
.clientId("backend-service")
.credentials("secret")
.grant(Type.PASSWORD)
.grantOptions("password", Map.of("username", "alice", "password", "alice"))
.build();
return oidcClients.newClient(cfg);
}
}
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
このクライアントは次のように使用できます。
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;
@GET
@Path("user-name")
@Produces("text/plain")
public Uni<String> getUserName() {
return tokenHelper.getTokens(oidcClientCreator.getOidcClient()).onItem()
.transformToUni(tokens -> restClient.getUserName("Bearer " + tokens.getAccessToken()));
}
}
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()));
}
}
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
複数の 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;
@GET
@Path("user-name")
@Produces("text/plain")
public Uni<String> getUserName() {
return tokenHelper.getTokens(client).onItem()
.transformToUni(tokens -> restClient.getUserName("Bearer " + tokens.getAccessToken()));
}
}
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()));
}
}
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
同じ修飾子を使用して、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());
}
}
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());
}
}
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
以下の Maven 依存関係を追加します。
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-client-oidc-filter</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-client-oidc-filter</artifactId>
</dependency>
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
io.quarkus:quarkus-oidc-client も追加されます。
quarkus-rest-client-oidc-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.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.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();
}
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
または
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();
}
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();
}
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
OidcClientRequestReactiveFilter はデフォルトの OidcClient を使用します。名前付きの OidcClient は、quarkus.rest-client-oidc-filter.client-name 設定プロパティーを使用して選択できます。@OidcClientFilter アノテーションの value 属性を設定して、OidcClient を選択することもできます。アノテーションを通じて設定されたクライアント名は、quarkus.rest-client-oidc-filter.client-name 設定プロパティーよりも優先されます。たとえば、OIDC クライアント宣言という名前の こちら の jwt-secret がある場合、このクライアントを次のように参照できます。
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();
}
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();
}
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
ProtectedResourceService#getUserName 呼び出しで 401 Unauthorized エラーが発生するたびにトークンを更新する場合は、次の例のように quarkus.rest-client-oidc-filter.refresh-on-unauthorized 設定プロパティーを使用します。
quarkus.rest-client-oidc-filter.refresh-on-unauthorized=true
quarkus.rest-client-oidc-filter.refresh-on-unauthorized=true
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
または、個々のエンドポイントに対してのみこの機能を有効にする必要がある場合は、次の例のようなカスタムフィルターを作成します。
package io.quarkus.it.keycloak;
import io.quarkus.oidc.client.reactive.filter.runtime.AbstractOidcClientRequestReactiveFilter;
import jakarta.annotation.Priority;
import jakarta.ws.rs.Priorities;
@Priority(Priorities.AUTHENTICATION)
public class OidcClientRequestCustomFilter extends AbstractOidcClientRequestReactiveFilter {
@Override
protected boolean refreshOnUnauthorized() {
return true;
}
}
package io.quarkus.it.keycloak;
import io.quarkus.oidc.client.reactive.filter.runtime.AbstractOidcClientRequestReactiveFilter;
import jakarta.annotation.Priority;
import jakarta.ws.rs.Priorities;
@Priority(Priorities.AUTHENTICATION)
public class OidcClientRequestCustomFilter extends AbstractOidcClientRequestReactiveFilter {
@Override
protected boolean refreshOnUnauthorized() {
return true;
}
}
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
以下の Maven 依存関係を追加します。
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-client-oidc-filter</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-client-oidc-filter</artifactId>
</dependency>
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
io.quarkus:quarkus-oidc-client も追加されます。
quarkus-resteasy-client-oidc-filter エクステンションは、io.quarkus.oidc.client.filter.OidcClientRequestFilter Jakarta REST ClientRequestFilter を提供します。これは、OidcClient を使用してアクセストークンを取得し、必要に応じて更新し、HTTP Authorization Bearer スキーム値として設定します。
デフォルトでは、このフィルターは、初期化時に 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.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();
}
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
または
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();
}
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();
}
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
または、quarkus.resteasy-client-oidc-filter.register-filter=true プロパティーが設定されている場合、OidcClientRequestFilter はすべての MP Rest または Jakarta REST クライアントに自動的に登録できます。
OidcClientRequestFilter はデフォルトの OidcClient を使用します。名前付きの OidcClient は、quarkus.resteasy-client-oidc-filter.client-name 設定プロパティーを使用して選択できます。@OidcClientFilter アノテーションの value 属性を設定して、OidcClient を選択することもできます。アノテーションを通じて設定されたクライアント名は、quarkus.resteasy-client-oidc-filter.client-name 設定プロパティーよりも優先されます。たとえば、OIDC クライアント宣言という名前の こちら の jwt-secret がある場合、このクライアントを次のように参照できます。
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();
}
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();
}
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
ProtectedResourceService#getUserName 呼び出しで 401 Unauthorized エラーが発生するたびにトークンを更新する場合は、次の例のように quarkus.resteasy-client-oidc-filter.refresh-on-unauthorized 設定プロパティーを使用します。
quarkus.resteasy-client-oidc-filter.refresh-on-unauthorized=true
quarkus.resteasy-client-oidc-filter.refresh-on-unauthorized=true
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
または、個々のエンドポイントに対してのみこの機能を有効にする必要がある場合は、次の例のようなカスタムフィルターを作成します。
package io.quarkus.it.keycloak;
import io.quarkus.oidc.client.filter.runtime.AbstractOidcClientRequestFilter;
import jakarta.annotation.Priority;
import jakarta.ws.rs.Priorities;
@Priority(Priorities.AUTHENTICATION)
public class OidcClientRequestCustomFilter extends AbstractOidcClientRequestFilter {
@Override
protected boolean refreshOnUnauthorized() {
return true;
}
}
package io.quarkus.it.keycloak;
import io.quarkus.oidc.client.filter.runtime.AbstractOidcClientRequestFilter;
import jakarta.annotation.Priority;
import jakarta.ws.rs.Priorities;
@Priority(Priorities.AUTHENTICATION)
public class OidcClientRequestCustomFilter extends AbstractOidcClientRequestFilter {
@Override
protected boolean refreshOnUnauthorized() {
return true;
}
}
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
必要に応じて、独自のカスタムフィルターを使用して 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());
}
}
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());
}
}
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
Tokens プロデューサーはトークンを取得して更新し、カスタムフィルターはトークンの使用方法とタイミングを決定します。
名前付きの Tokens を注入することもできます。名前付きの OidcClient とトークンの注入 を参照してください。
OidcClientRequestReactiveFilter、OidcClientRequestFilter、および Tokens プロデューサーは、更新トークンが利用可能な場合、現在の期限切れのアクセストークンを更新します。さらに、quarkus.oidc-client.refresh-token-time-skew プロパティーを使用して、アクセストークンをプリエンプティブに更新し、HTTP 401 エラーの原因となる可能性のある期限切れに近いアクセストークンの送信を回避することもできます。たとえば、このプロパティーが 3S に設定され、アクセストークンの有効期限が 3 秒未満の場合、このトークンは自動的に更新されます。
デフォルトでは、OIDC クライアントは、トークンの有効期限が切れていること、または refresh token time skew が設定されている場合は有効期限が近づいていることを検出すると、現在のリクエスト中にトークンを更新します。高いパフォーマンスが求められるアプリケーションでは、受信リクエストの処理中にトークン更新のための待機時間が発生するのを避け、代わりに非同期でのトークン更新を設定すると良いかもしれません。以下に例を示します。
quarkus.oidc-client.refresh-interval=1m
quarkus.oidc-client.refresh-interval=1m 1
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
1
現在のアクセストークンの有効期限が切れていて更新する必要があるかどうかを 1 分ごとに確認します。
アクセストークンを更新する必要があるが、更新トークンが利用できない場合は、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 は、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.secret=mysecret
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
または
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
quarkus.oidc-client.credentials.client-secret.value=mysecret
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
または、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
# 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
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
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
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
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
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
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
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
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
または、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
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
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
PEM キーが application.properties にインライン化され、署名アルゴリズムが RS256 である private_key_jwt:
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
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
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
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
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
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
キーストアファイルを含む 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
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
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
client_secret_jwt または private_key_jwt 認証方法を使用すると、クライアントシークレットがネットワーク上に送信されなくなります。
client_secret_jwt または private_key_jwt のいずれかの認証方法を使用する場合は、JWT 署名アルゴリズム、キー識別子、audience、サブジェクト、発行者をカスタマイズできます。次に例を示します。
private_key_jwt client authentication
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.
Use the RS512 signature algorithm instead of the default RS256
The token endpoint URL is the default audience value; use the base address URL instead:
custom subject instead of the client ID:
custom issuer instead of the client ID:
# 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
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
RFC7523 では、JWT ベアラートークンを使用してクライアントを認証する方法を説明しています。詳細は、Using JWTs for Client Authentication セクションを参照してください。
有効にするには、以下を実行します。
quarkus.oidc-client.auth-server-url=${auth-server-url}
quarkus.oidc-client.client-id=quarkus-app
quarkus.oidc-client.credentials.jwt.source=bearer
quarkus.oidc-client.auth-server-url=${auth-server-url}
quarkus.oidc-client.client-id=quarkus-app
quarkus.oidc-client.credentials.jwt.source=bearer
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
次に、JWT ベアラートークンを OIDC クライアントに client_assertion パラメーターとして提供する必要があります。
Quarkus はファイルシステムから JWT ベアラートークンをロードすることができます。たとえば、Kubernetes では、サービスアカウントトークンのプロジェクションを /var/run/secrets/tokens パスにマウントできます。後は JWT ベアラートークンパスを次のように設定するだけです。
quarkus.oidc-client.credentials.jwt.token-path=/var/run/secrets/tokens
quarkus.oidc-client.credentials.jwt.token-path=/var/run/secrets/tokens 1
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
1
JWT ベアラートークンへのパス。Quarkus はファイルシステムから新しいトークンをロードします。トークンの有効期限が切れるとそれを再ロードします。
他の選択肢としては、追加のグラントパラメーターを受け入れるトークンを取得または更新するための 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...");
}
}
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...");
}
}
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
以下は、RESTEasy Classic カスタムフィルターの例です。
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...");
}
}
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...");
}
}
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
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}
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}
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
一部の OpenID Connect Provider では、相互 TLS (mTLS) 認証プロセスの一部としてクライアントが認証されることが要求されます。
quarkus-oidc-client は、mTLS をサポートするために次のように設定できます。
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
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
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
カスタムのエクステンションが、OIDC クライアントでサポートされているいずれかの OIDC トークングラントを使用して OIDC トークンを取得する必要がある場合、そのエクステンションで OIDC Client SPI を利用すれば、OIDC クライアント自体にアクセストークンを必要に応じて取得および更新させることができます。
次の依存関係を追加します。
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-oidc-client-spi</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-oidc-client-spi</artifactId>
</dependency>
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
次に、必要に応じて io.quarkus.oidc.client.spi.TokenProvider CDI Bean を使用するようにエクステンションを更新します。次に例を示します。
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();
}
}
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();
}
}
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
現在、io.quarkus.oidc.client.spi.TokenProvider は、デフォルトの OIDC クライアントでのみ使用できます。カスタムエクステンションが複数の名前付き OIDC クライアントを認識することはほとんどないためです。
まず、テストプロジェクトに次の依存関係を追加します。
<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>
<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>
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
テストプロジェクトに次の依存関係を追加します。
<dependency>
<groupId>org.wiremock</groupId>
<artifactId>wiremock</artifactId>
<scope>test</scope>
<version>${wiremock.version}</version>
</dependency>
<dependency>
<groupId>org.wiremock</groupId>
<artifactId>wiremock</artifactId>
<scope>test</scope>
<version>${wiremock.version}</version> 1
</dependency>
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
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;
}
}
}
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;
}
}
}
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
REST テストエンドポイントを準備します。注入された MP REST クライアントと登録された OidcClient フィルターを使用するテストフロントエンドエンドポイントで、ダウンストリームエンドポイントを呼び出すことができます。このエンドポイントはトークンをエコーバックします。たとえば、main の Quarkus リポジトリーの integration-tests/oidc-client-wiremock を参照してください。
application.properties を設定します。以下に例を示します。
Use the 'keycloak.url' property set by the test KeycloakRealmResourceManager
# 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
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
最後にテストコードを記述します。上記の Wiremock ベースのリソースを考えると、最初のテスト呼び出しは access_token_1 アクセストークンを返すはずですが、これは 4 秒後に期限切れになります。awaitility を使用して約 5 秒間待機すると、次のテスト呼び出しで access_token_2 アクセストークンが返され、期限切れの access_token_1 アクセストークンが更新されたことが確認されます。
トークンの取得と更新エラーの詳細を確認するには、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
quarkus.log.category."io.quarkus.oidc.client.runtime.OidcClientImpl".level=TRACE
quarkus.log.category."io.quarkus.oidc.client.runtime.OidcClientImpl".min-level=TRACE
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow
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
quarkus.log.category."io.quarkus.oidc.client.runtime.OidcClientRecorder".level=TRACE
quarkus.log.category."io.quarkus.oidc.client.runtime.OidcClientRecorder".min-level=TRACE
Copy to Clipboard
Copied!
Toggle word wrap
Toggle overflow