The English version of quarkus.io is the official project site. Translated sites are community supported on a best-effort basis.

OpenID Connect(OIDC)およびOAuth2クライアントとフィルターリファレンスガイド

このリファレンスガイドは、以下の使用方法について説明しています。

  • OpenID Connectや Keycloak などのOAuth 2.0準拠の認可サーバーからアクセストークンを取得し、リフレッシュするための、quarkus-oidc-client や、 quarkus-oidc-client-reactive-filterquarkus-oidc-client-filter エクステンション

  • 現在の Bearer または Authorization Code Flow アクセストークンを伝播する quarkus-oidc-token-propagation-reactive エクステンションと quarkus-oidc-token-propagation エクステンション

これらのエクステンションで管理されているアクセストークンをHTTP Authorization ベアラートークンとして使用して、リモートサービスにアクセスすることができます。

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_credentialspassword などのトークングラントを使用してアクセストークンを取得し、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 であることを検出します。

Alternatively, if the discovery endpoint is not available or you want to save on the discovery endpoint round-trip, you can disable the discovery and configure the token endpoint address with a relative path value, for example:

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

Setting quarkus.oidc-client.auth-server-url and quarkus.oidc-client.discovery-enabled is not required in this case.

トークングラントをサポート

OidcClient がトークンを取得するために使用できる主なトークングラントは、client_credentials (デフォルト) と password グラントです。

クライアントクレデンシャル・グラント

OidcClientclient_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

パスワード・グラント

OidcClientpassword グラントを使用するように設定する方法は以下の通りです。

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 can also help with acquiring the tokens using the grants which require some extra input parameters which cannot be captured in the configuration. These grants are refresh_token (with the external refresh token), authorization_code, as well as two grants which can be used to exchange the current access token, urn:ietf:params:oauth:grant-type:token-exchange and urn:ietf:params:oauth:grant-type:jwt-bearer.

アクセストークンを取得するために、既存のリフレッシュトークンが現在の Quarkus エンドポイントにポストされた場合、帯域外リフレッシュトークンを使用して新しいトークンセットを取得する refresh_token グラントを使用する必要があります。この場合、OidcClient を次のように設定する必要があります。

quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/
quarkus.oidc-client.client-id=quarkus-app
quarkus.oidc-client.credentials.secret=secret
quarkus.oidc-client.grant.type=refresh

次に、OidcClient.refreshTokens メソッドと提供された更新トークンを使用して、アクセストークンを取得できます。

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 in MicroProfile RestClient Reactive filter and Token Propagation in MicroProfile RestClient filter for more details.

Using OidcClient to support the authorization code grant might be required if for some reason you cannot use the Quarkus OIDC extension to support Authorization Code Flow. If there is a very good reason for you to implement Authorization Code Flow then you can configure OidcClient as follows:

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 を受け取り、現在の coderedirect_uri パラメーターを渡して認可コードとトークンを交換することが可能です。

OidcClient also supports the urn:openid:params:grant-type:ciba grant:

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

and then you can use OidcClient.accessTokens method accepting a Map of extra properties and pass auth_req_id parameter to exchange the authorization code for the tokens.

グラントスコープ

You might need to request that a specific set of scopes is associated with an issued access token. Use a dedicated quarkus.oidc-client.scopes list property, for example: quarkus.oidc-client.scopes=email,phone

OidcClient を直接使用する

以下のように OidcClient を直接使用することができます。

import jakarta.inject.PostConstruct;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;

import io.quarkus.oidc.client.OidcClient;
import io.quarkus.oidc.client.Tokens;

@Path("/service")
public class OidcClientResource {

    @Inject
    OidcClient client;

    volatile Tokens currentTokens;

    @PostConstruct
    public void init() {
        currentTokens = client.getTokens().await().indefinitely();
    }

    @GET
    public String getResponse() {

        Tokens tokens = currentTokens;
        if (tokens.isAccessTokenExpired()) {
            // Add @Blocking method annotation if this code is used with Reactive RestClient
            tokens = client.refreshTokens(tokens.getRefreshToken()).await().indefinitely();
            currentTokens = tokens;
        }
        // Use tokens.getAccessToken() to configure MP RestClient Authorization header/etc
    }
}

トークンの注入

内部で OidcClient を使用する Tokens を注入することができます。 Tokens はアクセストークンを取得し、必要に応じてリフレッシュするために使用することができます。

import jakarta.inject.PostConstruct;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;

import io.quarkus.oidc.client.Tokens;

@Path("/service")
public class OidcClientResource {

    @Inject Tokens tokens;

    @GET
    public String getResponse() {
        //  Get the access token, which might have been refreshed.
        String accessToken = tokens.getAccessToken();
        // Use the access token to configure MP RestClient Authorization header/etc
    }
}

OidcClientsの使用

io.quarkus.oidc.client.OidcClientsOidcClient のコンテナーで、デフォルトの OidcClient と、このように設定できる名前付きクライアントが含まれています。

quarkus.oidc-client.client-enabled=false

quarkus.oidc-client.jwt-secret.auth-server-url=http://localhost:8180/auth/realms/quarkus/
quarkus.oidc-client.jwt-secret.client-id=quarkus-app
quarkus.oidc-client.jwt-secret.credentials.jwt.secret=AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow

この場合、デフォルトのクライアントは client-enabled=false プロパティーで無効になっていることに注意してください。 jwt-secret クライアントは以下のようにアクセスできます。

import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;

import io.quarkus.oidc.client.OidcClient;
import io.quarkus.oidc.client.OidcClients;

@Path("/clients")
public class OidcClientResource {

    @Inject
    OidcClients clients;

    @GET
    public String getResponse() {
        OidcClient client = clients.getClient("jwt-secret");
        // use this client to get the token
    }
}

OIDCマルチテナンシー も使用し、各 OIDC テナントに独自の関連付けられた OidcClient がある場合は、Vert.x RoutingContext tenantId 属性を使用できます。次に例を示します。

import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;

import io.quarkus.oidc.client.OidcClient;
import io.quarkus.oidc.client.OidcClients;
import io.vertx.ext.web.RoutingContext;

@Path("/clients")
public class OidcClientResource {

    @Inject
    OidcClients clients;
    @Inject
    RoutingContext context;

    @GET
    public String getResponse() {
        String tenantId = context.get("tenantId");
        // named OIDC tenant and client configurations use the same key:
        OidcClient client = clients.getClient(tenantId);
        // use this client to get the token
    }
}

必要であれば、このようにプログラム的に新しい OidcClient を作成することもできます。

import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;

import io.quarkus.oidc.client.OidcClient;
import io.quarkus.oidc.client.OidcClients;
import io.quarkus.oidc.client.OidcClientConfig;

import io.smallrye.mutiny.Uni;

@Path("/clients")
public class OidcClientResource {

    @Inject
    OidcClients clients;

    @GET
    public String getResponse() {
        OidcClientConfig cfg = new OidcClientConfig();
        cfg.setId("myclient");
        cfg.setAuthServerUrl("http://localhost:8081/auth/realms/quarkus/");
        cfg.setClientId("quarkus");
        cfg.getCredentials().setSecret("secret");
        Uni<OidcClient> client = clients.newClient(cfg);
        // use this client to get the token
    }
}

名前の付いたOidcClient とトークンの注入

複数の OidcClient が設定されている場合、OidcClients を使用する代わりに、追加の修飾子 @NamedOidcClientOidcClient の注入ターゲットを指定することができます。

package io.quarkus.oidc.client;

import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;

@Path("/clients")
public class OidcClientResource {

    @Inject
    @NamedOidcClient("jwt-secret")
    OidcClient client;

    @GET
    public String getResponse() {
        // use client to get the token
    }
}

同じ修飾子を使用して、Tokens 注入に使用する OidcClient を指定することができます。

@Provider
@Priority(Priorities.AUTHENTICATION)
@RequestScoped
public class OidcClientRequestCustomFilter implements ClientRequestFilter {

    @Inject
    @NamedOidcClient("jwt-secret")
    Tokens tokens;

    @Override
    public void filter(ClientRequestContext requestContext) throws IOException {
        requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, "Bearer " + tokens.getAccessToken());
    }
}

RestClient の Reactive ClientFilter で OidcClient を使用する

以下の Maven 依存関係を追加します。

<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-oidc-client-reactive-filter</artifactId>
</dependency>

また、 io.quarkus:quarkus-oidc-client も持ってくることに注意しましょう。

quarkus-oidc-client-reactive-filter エクステンションは io.quarkus.oidc.client.filter.OidcClientRequestReactiveFilter を提供します。

これは、 OidcClientRequestFilter の方法と同様に動作します ( MicroProfile RestClient クライアントフィルターで OidcClient を使用 を参照)。 OidcClient を使用してアクセストークンを取得し、必要に応じてリフレッシュして、HTTP Authorization Bearer スキーム値としてセットします。違いは、 Reactive RestClient で動作し、トークンを取得または更新するときに現在のIOスレッドをブロックしないノンブロッキングクライアントフィルタを実装している点です。

IOスレッドのブロックを避けるために、 OidcClientRequestReactiveFilter は実行されるまで最初のトークン取得を遅らせます。

You can selectively register OidcClientRequestReactiveFilter by using either io.quarkus.oidc.client.reactive.filter.OidcClientFilter or org.eclipse.microprofile.rest.client.annotation.RegisterProvider annotations:

import org.eclipse.microprofile.rest.client.annotation.RegisterProvider;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import io.quarkus.oidc.client.filter.OidcClientFilter;
import io.smallrye.mutiny.Uni;

@RegisterRestClient
@OidcClientFilter
@Path("/")
public interface ProtectedResourceService {

    @GET
    Uni<String> getUserName();
}

or

import org.eclipse.microprofile.rest.client.annotation.RegisterProvider;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import io.quarkus.oidc.client.reactive.filter.OidcClientRequestReactiveFilter;
import io.smallrye.mutiny.Uni;

@RegisterRestClient
@RegisterProvider(OidcClientRequestReactiveFilter.class)
@Path("/")
public interface ProtectedResourceService {

    @GET
    Uni<String> getUserName();
}

OidcClientRequestReactiveFilter uses a default OidcClient by default. A named OidcClient can be selected with a quarkus.oidc-client-reactive-filter.client-name configuration property. You can also select OidcClient by setting value attribute of the @OidcClientFilter annotation. The client name set through annotation has higher priority than the quarkus.oidc-client-reactive-filter.client-name configuration property. For example, given this jwt-secret named OIDC client declaration, you can refer to this client like this:

import org.eclipse.microprofile.rest.client.annotation.RegisterProvider;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import io.quarkus.oidc.client.filter.OidcClientFilter;
import io.smallrye.mutiny.Uni;

@RegisterRestClient
@OidcClientFilter("jwt-secret")
@Path("/")
public interface ProtectedResourceService {

    @GET
    Uni<String> getUserName();
}

RestClient の ClientFilter で OidcClient を使用する

以下の Maven 依存関係を追加します。

<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-oidc-client-filter</artifactId>
</dependency>

また、 io.quarkus:quarkus-oidc-client も持ってくることに注意しましょう。

quarkus-oidc-client-filter エクステンションは、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;

@RegisterRestClient
@OidcClientFilter
@Path("/")
public interface ProtectedResourceService {

    @GET
    String getUserName();
}

or

import org.eclipse.microprofile.rest.client.annotation.RegisterProvider;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import io.quarkus.oidc.client.filter.OidcClientRequestFilter;

@RegisterRestClient
@RegisterProvider(OidcClientRequestFilter.class)
@Path("/")
public interface ProtectedResourceService {

    @GET
    String getUserName();
}

あるいは、 quarkus.oidc-client-filter.register-filter=true プロパティが設定されている場合、すべての MP Rest または Jakarta REST クライアントで OidcClientRequestFilter を自動的に登録することもできます。

OidcClientRequestFilter uses a default OidcClient by default. A named OidcClient can be selected with a quarkus.oidc-client-filter.client-name configuration property. You can also select OidcClient by setting value attribute of the @OidcClientFilter annotation. The client name set through annotation has higher priority than the quarkus.oidc-client-filter.client-name configuration property. For example, given this jwt-secret named OIDC client declaration, you can refer to this client like this:

import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import io.quarkus.oidc.client.filter.OidcClientFilter;

@RegisterRestClient
@OidcClientFilter("jwt-secret")
@Path("/")
public interface ProtectedResourceService {

    @GET
    String getUserName();
}

カスタムの RestClient ClientFilter を使用する

ご希望の場合は、独自のカスタムフィルターを使用して、 Tokens を注入することができます。

import io.quarkus.oidc.client.Tokens;

@Provider
@Priority(Priorities.AUTHENTICATION)
public class OidcClientRequestCustomFilter implements ClientRequestFilter {

    @Inject
    Tokens tokens;

    @Override
    public void filter(ClientRequestContext requestContext) throws IOException {
        requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, "Bearer " + tokens.getAccessToken());
    }
}

Tokens プロデューサーがトークンを取得・更新し、カスタムフィルターが何時、どのようにトークンを使用するかを決定します。

また、名前付きの Tokens の注入も可能です。 名前付きOidcClient とトークンの注入 を参照してください。

アクセストークンの更新

OidcClientRequestReactiveFilter, OidcClientRequestFilter and Tokens producers will refresh the current expired access token if the refresh token is available. Additionally, quarkus.oidc-client.refresh-token-time-skew property can be used for a preemptive access token refreshment to avoid sending nearly expired access tokens that might cause HTTP 401 errors. For example if this property is set to 3S and the access token will expire in less than 3 seconds then this token will be auto-refreshed.

アクセストークンの更新が必要なのにリフレッシュトークンがない場合は、 client_credentials のように設定されたグラントを使って新しいトークンの取得を試みます。

Note that some OpenID Connect Providers will not return a refresh token in a client_credentials grant response. For example, starting from Keycloak 12 a refresh token will not be returned by default for client_credentials. The providers might also restrict the number of times a refresh token can be used.

アクセストークンの失効

Keycloak などの OpenId Connect プロバイダがトークンの失効エンドポイントをサポートしている場合、OidcClient#revokeAccessToken を使うことで現在のアクセストークンを失効させることができます。失効エンドポイントの URL は、トークン要求 URI と共に検出されるか、または quarkus.oidc-client.revoke-path で設定することができます。

You might want to have the access token revoked if using this token with a REST client fails with HTTP 401 or the access token has already been used for a long time and you’d like to refresh it.

リフレッシュトークンを使用してトークンのリフレッシュを要求することができます。リフレッシュトークンが利用できない場合は、まずそれを失効させてから、新しいアクセストークンをリクエストすることでリフレッシュできます。

OidcClient 認証

OidcClient は、client_credentials および他のグラントリクエストが成功するために OpenID Connect プロバイダーに対して認証する必要があります。https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication[OIDC Client Authentication] オプションはすべてサポートされています。以下に例を示します。

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

or

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 is a key which will be used to retrieve a secret from the map of credentials returned from CredentialsProvider
quarkus.oidc-client.credentials.client-secret.provider.key=mysecret-key
# Set it only if more than one CredentialsProvider can be registered
quarkus.oidc-client.credentials.client-secret.provider.name=oidc-credentials-provider

client_secret_post:

quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/
quarkus.oidc-client.client-id=quarkus-app
quarkus.oidc-client.credentials.client-secret.value=mysecret
quarkus.oidc-client.credentials.client-secret.method=post

client_secret_jwt、署名アルゴリズムは HS256 です。

quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/
quarkus.oidc-client.client-id=quarkus-app
quarkus.oidc-client.credentials.jwt.secret=AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow

または CredentialsProvider から取得した秘密鍵で、署名アルゴリズムは HS256 です。

quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/
quarkus.oidc-client.client-id=quarkus-app

# This is a key which will be used to retrieve a secret from the map of credentials returned from CredentialsProvider
quarkus.oidc-client.credentials.jwt.secret-provider.key=mysecret-key
# Set it only if more than one CredentialsProvider can be registered
quarkus.oidc-client.credentials.jwt.secret-provider.name=oidc-credentials-provider

PEM キーファイルを使用した private_key_jwt 、署名アルゴリズムは RS256 です。

quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/
quarkus.oidc-client.client-id=quarkus-app
quarkus.oidc-client.credentials.jwt.key-file=privateKey.pem

private_key_jwt with the keystore file, 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-store-file=keystore.jks
quarkus.oidc-client.credentials.jwt.key-store-password=mypassword
quarkus.oidc-client.credentials.jwt.key-password=mykeypassword

# Private key alias inside the keystore
quarkus.oidc-client.credentials.jwt.key-id=mykeyAlias

client_secret_jwt または private_key_jwt 認証方法を使用することで、クライアントシークレットが漏れることはありません。

追加の JWT 認証オプション

もし client_secret_jwtprivate_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 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 not necessary.
quarkus.oidc-client.credentials.jwt.token-key-id=mykey

# Use 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

Apple POST JWT

Apple OpenID Connect プロバイダーは client_secret_post メソッドを使用します。ここで、secret は 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

Some OpenID Connect Providers require that a client is authenticated as part of the mutual TLS (mTLS) authentication process.

mTLS をサポートする為に quarkus-oidc-client は以下のように設定出来ます :

quarkus.oidc.tls.verification=certificate-validation

# Keystore configuration
quarkus.oidc.client.tls.key-store-file=client-keystore.jks
quarkus.oidc.client.tls.key-store-password=${key-store-password}

# Add more keystore properties if needed:
#quarkus.oidc.client.tls.key-store-alias=keyAlias
#quarkus.oidc.client.tls.key-store-alias-password=keyAliasPassword

# Truststore configuration
quarkus.oidc-client.tls.trust-store-file=client-truststore.jks
quarkus.oidc-client.tls.trust-store-password=${trust-store-password}
# Add more truststore properties if needed:
#quarkus.oidc.client.tls.trust-store-alias=certAlias

テスト

テストプロジェクトに以下の依存関係を追加することから始めます。

<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>
</dependency>

Write a Wiremock-based QuarkusTestResourceLifecycleManager, for example:

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;
        }
    }
}

Prepare the REST test endpoints. You can have the test front-end endpoint, which uses the injected MP REST client with a registered OidcClient filter, call the downstream endpoint. This endpoint echoes the token back. For example, see the integration-tests/oidc-client-wiremock in the main Quarkus repository.

application.properties を次のように設定します。

# Use 'keycloak.url' property set by the test KeycloakRealmResourceManager
quarkus.oidc-client.auth-server-url=${keycloak.url}
quarkus.oidc-client.discovery-enabled=false
quarkus.oidc-client.token-path=/tokens
quarkus.oidc-client.client-id=quarkus-service-app
quarkus.oidc-client.credentials.secret=secret
quarkus.oidc-client.grant.type=password
quarkus.oidc-client.grant-options.password.username=alice
quarkus.oidc-client.grant-options.password.password=alice

そして最後にテストコードを書きます。上記の Wiremock ベースのリソースがある場合、最初のテスト起動では access_token_1 アクセストークンが返却されますが、このアクセストークンは 4 秒で期限切れになります。 awaitility を使用して約 5 秒間待つと、次のテスト起動時に access_token_2 アクセストークンが返され、期限切れの access_token_1 アクセストークンがリフレッシュされたことが確認できます。

Keycloak

If you work with Keycloak then you can use the same approach as described in the OpenID Connect Bearer Token Integration testing Keycloak section.

ログでエラーを確認する方法

Enable io.quarkus.oidc.client.runtime.OidcClientImpl TRACE level logging to see more details about the token acquisition and refresh errors:

quarkus.log.category."io.quarkus.oidc.client.runtime.OidcClientImpl".level=TRACE
quarkus.log.category."io.quarkus.oidc.client.runtime.OidcClientImpl".min-level=TRACE

Enable io.quarkus.oidc.client.runtime.OidcClientRecorder TRACE level logging to see more details about the OidcClient initialization errors:

quarkus.log.category."io.quarkus.oidc.client.runtime.OidcClientRecorder".level=TRACE
quarkus.log.category."io.quarkus.oidc.client.runtime.OidcClientRecorder".min-level=TRACE

OIDC request customization

You can customize OIDC requests made by Quarkus to the OIDC provider by registering one or more OidcRequestFiler implementations which can update or add new request headers, for example, a filter can analyze the request body and add its digest as a new header value:

package io.quarkus.it.keycloak;

import jakarta.enterprise.context.ApplicationScoped;

import io.quarkus.arc.Unremovable;
import io.quarkus.oidc.common.OidcRequestContextProperties;
import io.quarkus.oidc.common.OidcRequestFilter;
import io.vertx.core.http.HttpMethod;
import io.vertx.mutiny.core.buffer.Buffer;
import io.vertx.mutiny.ext.web.client.HttpRequest;

@ApplicationScoped
@Unremovable
public class OidcRequestCustomizer implements OidcRequestFilter {

    @Override
    public void filter(HttpRequest<Buffer> request, Buffer buffer, OidcRequestContextProperties contextProperties) {
        HttpMethod method = request.method();
        String uri = request.uri();
        if (method == HttpMethod.POST && uri.endsWith("/service") && buffer != null) {
            request.putHeader("Digest", calculateDigest(buffer.toString()));
        }
    }

    private String calculateDigest(String bodyString) {
        // Apply the required digest algorithm to the body string
    }
}

Token Propagation Reactive

The quarkus-oidc-token-propagation-reactive extension provides RestEasy Reactive Client io.quarkus.oidc.token.propagation.reactive.AccessTokenRequestReactiveFilter that simplifies the propagation of authentication information by propagating the Bearer token present in the current 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;

@RegisterRestClient
@AccessToken
@Path("/")
public interface ProtectedResourceService {

    @GET
    String getUserName();
}

or

import org.eclipse.microprofile.rest.client.annotation.RegisterProvider;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import io.quarkus.oidc.token.propagation.reactive.AccessTokenRequestReactiveFilter;

@RegisterRestClient
@RegisterProvider(AccessTokenRequestReactiveFilter.class)
@Path("/")
public interface ProtectedResourceService {

    @GET
    String getUserName();
}

さらに、 AccessTokenRequestReactiveFilter 、トークンを伝播する前に交換する必要がある複雑なアプリケーションをサポートすることができます。

もし、現在のアクセストークンを伝播する前に交換する必要があり、かつ Keycloak やその他の Token Exchange トークングラントをサポートする OpenID Connect Provider で作業する場合は、AccessTokenRequestFilter をこのように設定することが可能です。

quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus
quarkus.oidc-client.client-id=quarkus-app
quarkus.oidc-client.credentials.secret=secret
quarkus.oidc-client.grant.type=exchange
quarkus.oidc-client.grant-options.exchange.audience=quarkus-app-exchange

quarkus.oidc-token-propagation.exchange-token=true

AccessTokenRequestReactiveFilterOidcClient を使用して現在のトークンを交換することに注意してください。また、quarkus.oidc-client.grant-options.exchange を使用して OpenID Connect プロバイダーが期待する追加の交換プロパティーを設定できます。

If you work with providers such as Azure that require using JWT bearer token grant to exchange the current token then you can configure AccessTokenRequestReactiveFilter to exchange the token like this:

quarkus.oidc-client.auth-server-url=${azure.provider.url}
quarkus.oidc-client.client-id=quarkus-app
quarkus.oidc-client.credentials.secret=secret

quarkus.oidc-client.grant.type=jwt
quarkus.oidc-client.grant-options.jwt.requested_token_use=on_behalf_of
quarkus.oidc-client.scopes=https://graph.microsoft.com/user.read,offline_access

quarkus.oidc-token-propagation-reactive.exchange-token=true

AccessTokenRequestReactiveFilter はデフォルトでデフォルトの OidcClient を使用します。名前付きの OidcClientquarkus.oidc-token-propagation-reactive.client-name 設定プロパティーで選択することができます。

トークンの伝播

The quarkus-oidc-token-propagation extension provides two Jakarta REST jakarta.ws.rs.client.ClientRequestFilter class implementations that simplify the propagation of authentication information. io.quarkus.oidc.token.propagation.AccessTokenRequestFilter propagates the Bearer token present in the current active request or the token acquired from the Authorization code flow mechanism, as the HTTP Authorization header’s Bearer scheme value. The io.quarkus.oidc.token.propagation.JsonWebTokenRequestFilter provides the same functionality, but in addition provides support for JWT tokens.

コードフローアクセストークンは (ID トークンとは異なり)、現在の Quarkus エンドポイントに伝播され、現在認証されているユーザーに代わってリモートサービスにアクセスすることを意図しているからです。

しかし、エンドツーエンドで直接 Bearer トークンを伝播することは、可能な限り避けるべきです。例えば、Client → Service A → Service B では、Client から Service A に送られたトークンを Service B が受け取ります。このような場合、Service B はトークンが Service A から来たのか、それとも Client から直接来たのかを区別することができません。Service B は、トークンが Service A から来たことを確認するために、新しい発行者とオーディエンスのクレームをアサートする必要があります。

Additionally, a complex application might need to exchange or update the tokens before propagating them. For example, the access context might be different when Service A is accessing Service B. In this case, Service A might be granted a narrow or a completely different set of scopes to access Service B.

以下のセクションでは、AccessTokenRequestFilterJsonWebTokenRequestFilter がどのように役立つかを説明します。

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;

@RegisterRestClient
@AccessToken
@Path("/")
public interface ProtectedResourceService {

    @GET
    String getUserName();
}

or

import org.eclipse.microprofile.rest.client.annotation.RegisterProvider;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import io.quarkus.oidc.token.propagation.AccessTokenRequestFilter;

@RegisterRestClient
@RegisterProvider(AccessTokenRequestFilter.class)
@Path("/")
public interface ProtectedResourceService {

    @GET
    String getUserName();
}

Alternatively, AccessTokenRequestFilter can be registered automatically with all MP Rest or Jakarta REST clients if quarkus.oidc-token-propagation.register-filter property is set to true and quarkus.oidc-token-propagation.json-web-token property is set to false (which is a default value).

伝播前のトークンを交換する

もし、現在のアクセストークンを伝播する前に交換する必要があり、かつ Keycloak やその他の Token Exchange トークン付与をサポートする OpenID Connect Provider で作業する場合は、AccessTokenRequestFilter をこのように設定することが可能です。

quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus
quarkus.oidc-client.client-id=quarkus-app
quarkus.oidc-client.credentials.secret=secret
quarkus.oidc-client.grant.type=exchange
quarkus.oidc-client.grant-options.exchange.audience=quarkus-app-exchange

quarkus.oidc-token-propagation.exchange-token=true

If you work with providers such as Azure that require using JWT bearer token grant to exchange the current token then you can configure AccessTokenRequestFilter to exchange the token like this:

quarkus.oidc-client.auth-server-url=${azure.provider.url}
quarkus.oidc-client.client-id=quarkus-app
quarkus.oidc-client.credentials.secret=secret

quarkus.oidc-client.grant.type=jwt
quarkus.oidc-client.grant-options.jwt.requested_token_use=on_behalf_of
quarkus.oidc-client.scopes=https://graph.microsoft.com/user.read,offline_access

quarkus.oidc-token-propagation.exchange-token=true

AccessTokenRequestFilterOidcClient を使用して現在のトークンを交換することに注意してください。また、quarkus.oidc-client.grant-options.exchange を使用して OpenID Connect プロバイダーが期待する追加の交換プロパティーを設定できます。

AccessTokenRequestFilter はデフォルトで OidcClient を使用します。名前付きの OidcClientquarkus.oidc-token-propagation.client-name 設定プロパティーで選択することができます。

RestClient JsonWebTokenRequestFilter

JsonWebTokenRequestFilter の使用は、Bearer JWT トークンを扱う場合に推奨します。これらのトークンは issueraudience などの claim を変更でき、更新したトークンは再度セキュリティー保護 (例えば、再署名) を受けることができます。これは注入された org.eclipse.microprofile.jwt.JsonWebToken を想定しているので、不透明なトークンでは動作しません。また、OpenID Connect プロバイダーが Token Exchange プロトコルをサポートしている場合は、代わりに AccessTokenRequestFilter を使用することを推奨します - JWT と不透明な bearer トークンの両方を AccessTokenRequestFilter で安全に交換することができます。

JsonWebTokenRequestFilter により、Service A の実装は注入された org.eclipse.microprofile.jwt.JsonWebToken を新しい issueraudience claim 値で更新し、更新したトークンを新しい署名で再度保護することが簡単にできます。唯一の難しいステップは、Service A が署名鍵を持っていることを確認することです。署名鍵は、安全なファイルシステムか、Vault のようなリモートセキュアストレージからプロビジョニングする必要があります。

たとえば、 io.quarkus.oidc.token.propagation.JsonWebToken または org.eclipse.microprofile.rest.client.annotation.RegisterProvider を使用して、 JsonWebTokenRequestFilter を選択的に登録することが可能です。

import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import io.quarkus.oidc.token.propagation.JsonWebToken;

@RegisterRestClient
@JsonWebToken
@Path("/")
public interface ProtectedResourceService {

    @GET
    String getUserName();
}

or

import org.eclipse.microprofile.rest.client.annotation.RegisterProvider;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import io.quarkus.oidc.token.propagation.JsonWebTokenRequestFilter;

@RegisterRestClient
@RegisterProvider(JsonWebTokenRequestFilter.class)
@Path("/")
public interface ProtectedResourceService {

    @GET
    String getUserName();
}

あるいは、 quarkus.oidc-token-propagation.register-filterquarkus.oidc-token-propagation.json-web-token の両方のプロパティを true に設定すると、すべての MicroProfile REST または Jakarta REST クライアントで JsonWebTokenRequestFilter が自動的に登録されます。

伝播前のトークンの更新

注入されたトークンの iss (issuer) や aud (audience) の主張を更新して、新しい署名で保護する必要がある場合は、次のように JsonWebTokenRequestFilter を設定できます。

quarkus.oidc-token-propagation.secure-json-web-token=true
smallrye.jwt.sign.key.location=/privateKey.pem
# Set a new issuer
smallrye.jwt.new-token.issuer=http://frontend-resource
# Set a new audience
smallrye.jwt.new-token.audience=http://downstream-resource
# Override the existing token issuer and audience claims if they are already set
smallrye.jwt.new-token.override-matching-claims=true

As already noted above, use AccessTokenRequestFilter if you work with Keycloak or OpenID Connect Provider which supports a Token Exchange protocol.

テスト

You can generate the tokens as described in OpenID Connect Bearer Token Integration testing section. Prepare the REST test endpoints. You can have the test front-end endpoint, which uses the injected MP REST client with a registered token propagation filter, call the downstream endpoint. For example, see the integration-tests/oidc-token-propagation in the main Quarkus repository.

Token Propagation Reactive

以下の Maven 依存関係を追加します。

<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-oidc-token-propagation-reactive</artifactId>
</dependency>

quarkus-oidc-token-propagation-reactive エクステンションは、io.quarkus.oidc.token.propagation.reactive.AccessTokenRequestReactiveFilter を提供します。これは、現在の Bearer または Authorization Code Flow アクセストークンを伝播するために使用することができます。

The quarkus-oidc-token-propagation-reactive extension (as opposed to the non-reactive quarkus-oidc-token-propagation extension) does not currently support the exchanging or resigning the tokens before the propagation. However, these features might be added in the future.

GraphQL client integration

The quarkus-oidc-client-graphql extension provides a way to integrate an OIDC client into GraphQL clients. This works similarly as with REST clients. When this extension is present, any configured (that means NOT created programmatically via the builder, but via configuration properties) GraphQL client will attempt to use the OIDC client to obtain an access token and set it as an Authorization header value. OIDC client will also refresh expired access tokens.

To configure which OIDC client should be used by GraphQL client, select one of the configured OIDC clients with the quarkus.oidc-client-graphql.client-name property, for example:

quarkus.oidc-client-graphql.client-name=oidc-client-for-graphql

# example declaration of the OIDC client itself
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
If you don’t specify the quarkus.oidc-client-graphql.client-name property, GraphQL clients will use the default OIDC client (without an explicit name).

Specifically for type-safe GraphQL clients, you can override this on a per-client basis by annotating the GraphQLClientApi interface with @io.quarkus.oidc.client.filter.OidcClientFilter. For example:

@GraphQLClientApi(configKey = "order-client")
@OidcClientFilter("oidc-client-for-graphql")
public interface OrdersGraphQLClient {
    // queries, mutations and subscriptions go here...
}

To be able to use this with a programmatically created GraphQL client, both builders (VertxDynamicGraphQLClientBuilder and VertxTypesafeGraphQLClientBuilder) contain a method dynamicHeader(String, Uni<String>) that allows you to plug in a header that might change for every request. To plug an OIDC client into it, use

@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();