OpenID Connect (OIDC) and OAuth2 Client and Filters Reference Guide

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

  • quarkus-oidc-client、 quarkus-oidc-client-reactive-filter、および quarkus-oidc-client-filter エクステンションを使用して、OpenId Connect および Keycloak などの OAuth 2.0 準拠の認可サーバーからアクセストークンを取得し、更新する方法

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

これらのエクステンションで管理されているアクセストークンを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_credentials または password トークングラントを使用してアクセストークンを取得し、 refresh_token グラントを使用してトークンを更新します。

トークンエンドポイントの設定

デフォルトでは、トークンのエンドポイントアドレスは、設定された quarkus.oidc-client.auth-server-url に /.well-known/openid-configuration パスを追加することによって検出されます。

例えば、この Keycloak の URL があるとします。

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

OidcClient は、トークンのエンドポイント URL が http://localhost:8180/auth/realms/quarkus/protocol/openid-connect/tokens であることを検出します。

また、ディスカバリーエンドポイントが利用できない場合や、ディスカバリーエンドポイントのラウンドトリップを節約したい場合は、ディスカバリーを無効にして、相対パス値でトークンエンドポイントアドレスを設定することもできます。例えば:

quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus
quarkus.oidc-client.discovery-enabled=false
# Token endpoint: http://localhost:8180/auth/realms/quarkus/protocol/openid-connect/tokens
quarkus.oidc-client.token-path=/protocol/openid-connect/tokens

ディスカバリーなしでトークンエンドポイント URL を設定する、よりコンパクトな方法は、 quarkus.oidc-client.token-path を絶対 URL に設定することです。

quarkus.oidc-client.token-path=http://localhost:8180/auth/realms/quarkus/protocol/openid-connect/tokens

この場合、'quarkus.oidc-client.auth-server-url' および 'quarkus.oidc-client.discovery-enabled' の設定は必要ありません。

サポートされるトークングラント

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

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

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

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

client_credentials グラントにより、 quarkus.oidc-client.grant-options.client.<param-name>=<value> を介してトークンリクエストに追加のパラメータを設定できます。 audience パラメータを使用して目的のトークン受信者を設定する方法は次のとおりです。

quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/
quarkus.oidc-client.client-id=quarkus-app
quarkus.oidc-client.credentials.secret=secret
# 'client' is a shortcut for `client_credentials`
quarkus.oidc-client.grant.type=client
quarkus.oidc-client.grant-options.client.audience=https://example.com/api

パスワード・グラント

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

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

これは、クライアント認証情報のグラントをカスタマイズする方法と同様に、 quarkus.oidc-client.grant-options.password 設定接頭辞を使用してさらにカスタマイズできます。

その他の助成金

OidcClient はグラントを使用してトークンの取得を支援することもできます。このグラントでは、設定に含まれない追加の入力パラメーターを必要とします。これらのグラントは refresh token (外部リフレッシュトークン)、 token exchange および authorization code です。

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

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

続いて、提供されたリフレッシュトークンを使って OidcClient.refreshTokens メソッドを使用して、アクセストークンを取得することができます。

複雑なマイクロサービスアプリケーションを構築していて、同じ Bearer トークンが複数のサービスに伝搬して使用されるのを避けたい場合、 token exchange グラントを使用する必要がある場合があります。詳しくは MicroProfile RestClient client filterにおけるトークン伝搬 を参照してください。

何らかの理由で Quarkus OpenID Connect extension を使用して認可コードフローをサポートできない場合は、 OidcClient を使用した authorization code グラントのサポートが必要な場合があります。認可コードフローを実装する正当な理由がある場合は、 OidcClient を以下のように設定することができます。

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

次に、追加のプロパティーのマップを受け入れる OidcClient.accessTokens メソッドを使用し、現在の code パラメーターと redirect_uri パラメーターを渡して、トークンの認可コードを交換できます。

グラントスコープ

スコープの特定のセットが発行されたアクセストークンに関連付けられるように要求する必要がある場合があります。専用の quarkus.oidc-client.scopes リストプロパティーを使用してください (例: quarkus.oidc-client.scopes=email,phone)。

OidcClient を直接使用する

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

import javax.inject.PostConstruct;
import javax.inject.Inject;
import javax.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 javax.inject.PostConstruct;
import javax.inject.Inject;
import javax.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 may have been refreshed.
        String accessToken = tokens.getAccessToken();
        // Use the access token to configure MP RestClient Authorization header/etc
    }
}

OidcClients を使用する

io.quarkus.oidc.client.OidcClients は、 OidcClients のコンテナーです。デフォルトの 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 javax.inject.Inject;
import javax.ws.rs.GET;
import javax.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 multitenancy を使用し、各 OIDC テナントが独自の関連する OidcClient を持っている場合は、Vert.x RoutingContext tenantId 属性を使用することができます。以下に例を示します。

import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.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 javax.inject.Inject;
import javax.ws.rs.GET;
import javax.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 とトークンの挿入

複数の設定済みの OidcClients の場合は、 OidcClients で作業する代わりに、追加の修飾子 @NamedOidcClient で OidcClient 注入ターゲットを指定できます。

package io.quarkus.oidc.client;

import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.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 と同じように機能します (Use OidcClient in MicroProfile RestClient client filter を参照) - OidcClient を使用してアクセストークンを取得し、必要に応じてリフレッシュして、HTTP Authorization Bearer スキームの値としてセットします。違いは、Reactive RestClient で機能することと、トークンの取得や更新時に現在の IO スレッドをブロックしないノンブロッキングクライアントフィルターを実装している点です。

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

io.quarkus.oidc.client.filter.OidcClientFilter または org.eclipse.microprofile.rest.client.annotation.RegisterProvider アノテーションを使用して OidcClientRequestFilter を選択的に登録することができます。

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

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

    @GET
    Uni<String> getUserName();
}

あるいは

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

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

    @GET
    Uni<String> getUserName();
}

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

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 JAX-RS 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();
}

あるいは

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 または JAX-RS クライアントで OidcClientRequestFilter を自動的に登録することができます。

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

カスタム 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 を注入することもできます。Inject named OidcClient and Tokens を参照してください。

アクセストークンの更新

OidcClientRequestReactiveFilter、 OidcClientRequestFilter および Tokens プロデューサーは、リフレッシュトークンが利用可能な場合、現在の期限切れアクセストークンをリフレッシュします。さらに、 quarkus.oidc-client.refresh-token-time-skew プロパティーは、HTTP 401 エラーの原因となる可能性のある期限切れ間近のアクセストークンの送信を避けるために、アクセストークンのリフレッシュを先取りして行うことに使用できます。たとえば、このプロパティーが 3S に設定されていて、アクセストークンが 3 秒以内に期限切れになる場合、このトークンは自動的にリフレッシュされます。

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

一部の OpenID Connect プロバイダーは、 client_credentials グラントレスポンスでリフレッシュトークンを返さない点にご注意ください。たとえば、Keycloak 12 以降、 client_credentials のリフレッシュトークンはデフォルトでは返されません。また、プロバイダーは、リフレッシュトークンを使用できる回数を制限する場合もあります。

アクセストークンの失効

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

RESTクライアントで使用するとHTTP 401 で失敗するアクセストークンを使っている場合や、長い時間使用されているアクセストークンをリフレッシュしたい場合は、アクセストークンを失効させることができます。

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

OidcClient 認証

OidcClient は、 client_credentials および他のグラントリクエストが成功するために OpenID Connect プロバイダーに対して認証する必要があります。 OIDC Client 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

あるいは

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

または、CredentialsProvider: から取得したシークレットを使用します。

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

# This 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`、署名アルゴリズムは 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_jwt または private_key_jwt のいずれかの認証方法を使用する場合は、JWT 署名アルゴリズム、鍵識別子、オーディエンス、サブジェクト、および発行者をカスタマイズすることができます。以下に例を示します。

# private_key_jwt client authentication

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

# This is a token key identifier 'kid' header - set it if your OpenID Connect provider requires it.
# Note 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

OpenID Connect プロバイダーによっては、クライアントが Mutual TLS (mTLS) 認証プロセスの一部として認証されることを要求する場合があります。

quarkus-oidc-client can be configured as follows to support mTLS:

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>com.github.tomakehurst</groupId>
    <artifactId>wiremock-jre8</artifactId>
    <scope>test</scope>
</dependency>

Wiremockベースの QuarkusTestResourceLifecycleManager を例えば以下のように書きます。

package io.quarkus.it.keycloak;

import static com.github.tomakehurst.wiremock.client.WireMock.matching;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;

import java.util.HashMap;
import java.util.Map;

import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.client.WireMock;
import com.github.tomakehurst.wiremock.core.Options.ChunkedEncodingPolicy;

import io.quarkus.test.common.QuarkusTestResourceLifecycleManager;

public class KeycloakRealmResourceManager implements QuarkusTestResourceLifecycleManager {
    private WireMockServer server;

    @Override
    public Map<String, String> start() {

        server = new WireMockServer(wireMockConfig().dynamicPort().useChunkedTransferEncoding(ChunkedEncodingPolicy.NEVER));
        server.start();

        server.stubFor(WireMock.post("/tokens")
                .withRequestBody(matching("grant_type=password&username=alice&password=alice"))
                .willReturn(WireMock
                        .aResponse()
                        .withHeader("Content-Type", "application/json")
                        .withBody(
                                "{\"access_token\":\"access_token_1\", \"expires_in\":4, \"refresh_token\":\"refresh_token_1\"}")));
        server.stubFor(WireMock.post("/tokens")
                .withRequestBody(matching("grant_type=refresh_token&refresh_token=refresh_token_1"))
                .willReturn(WireMock
                        .aResponse()
                        .withHeader("Content-Type", "application/json")
                        .withBody(
                                "{\"access_token\":\"access_token_2\", \"expires_in\":4, \"refresh_token\":\"refresh_token_1\"}")));


        Map<String, String> conf = new HashMap<>();
        conf.put("keycloak.url", server.baseUrl());
        return conf;
    }

    @Override
    public synchronized void stop() {
        if (server != null) {
            server.stop();
            server = null;
        }
    }
}

RESTテストエンドポイントを用意します。注入されたMP RESTクライアントを登録されたOidcClientフィルターで使用するテスト用フロントエンドエンドポイントが、トークンをエコーバックするダウンストリームエンドポイントを呼び出すことができます。例として、 main Quarkusリポジトリの integration-tests/oidc-client-wiremock を参照してください。

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

Keycloak を使用する場合は、OpenID Connect Bearer Token 結合テスト Keycloak セクションで説明したのと同じ方法を使用することができます。

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

トークンの取得および更新エラーの詳細を確認するには、 io.quarkus.oidc.client.runtime.OidcClientImpl TRACE レベルのロギングを有効にしてください。

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

OidcClient 初期化エラーの詳細を確認するには、 io.quarkus.oidc.client.runtime.OidcClientRecorder TRACE レベルのログを有効にしてください。

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

トークンの伝播

quarkus-oidc-token-propagation エクステンションは、認証情報の伝搬を簡素化する 2 つの JAX-RS javax.ws.rs.client.ClientRequestFilter クラスの実装を提供します。 io.quarkus.oidc.token.propagation.AccessTokenRequestFilter は、現在のアクティブなリクエストに存在する Bearer トークン、または HTTP Authorization ヘッダーの Bearer スキーム値として Authorization Code Flow から取得したトークンを伝播させます。 io.quarkus.oidc.token.propagation.JsonWebTokenRequestFilter は同じ機能を提供しますが、これに加えて JWT トークンのサポートも提供します。

現在の認可コードフローアクセストークンを伝播する必要がある場合は、即時トークン伝播はうまく機能します - コードフローアクセストークン (ID トークンとは対照的に) は、現在認証されているユーザーに代わってリモートサービスにアクセスするように、現在の Quarkus エンドポイントに伝搬されることが意図されています。

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

さらに、複雑なアプリケーションでは、トークンを伝搬する前に交換または更新する必要がある場合があります。たとえば、 Service A が Service B にアクセスするとき、アクセスコンテキストが異なるかもしれません。この場合、 Service A には Service B にアクセスするための狭いスコープまたは完全に異なるスコープのセットがグラントされることがあります。

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

RestClient AccessTokenRequestFilter

AccessTokenRequestFilter は、すべてのトークンをStringとして扱うため、JWTトークンと不透明なトークンの両方を扱うことができます。

io.quarkus.oidc.token.propagation.AccessToken または org.eclipse.microprofile.rest.client.annotation.RegisterProvider のいずれかを使用して、 AccessTokenRequestFilter を選択的に登録できます。例:

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

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

    @GET
    String getUserName();
}

あるいは

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

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

    @GET
    String getUserName();
}

または、 quarkus.oidc-token-propagation.register-filter プロパティが true に設定され、 quarkus.oidc-token-propagation.json-web-token プロパティが false に設定されている場合(デフォルト値です)、すべての MP Rest または JAX-RS クライアントで AccessTokenRequestFilter を自動的に登録することができます。

伝搬前のトークンの交換

もし、現在のアクセストークンを伝搬する前に交換する必要があり、かつ Keycloak または他の Token Exchange トークングラントをサポートする OpenID Connect プロバイダーで作業する場合は、 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

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

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

RestClient JsonWebTokenRequestFilter

JsonWebTokenRequestFilter の使用は、ベアラー JWT トークンで作業する場合に推奨されます。これらのトークンは issuer や audience などのクレームを変更し、更新されたトークンを再度保護 (再署名など) することができます。注入された org.eclipse.microprofile.jwt.JsonWebToken を想定しているため、不透明なトークンでは機能しません。また、OpenID Connect プロバイダーが Token Exchange プロトコルをサポートしている場合は、代わりに AccessTokenRequestFilter を使用することが推奨されます。JWT と不透明なベアラートークンの両方を AccessTokenRequestFilter と安全に交換することができるからです。

JsonWebTokenRequestFilter は、 Service A の実装が、注入された org.eclipse.microprofile.jwt.JsonWebToken を新しい issuer と audience のクレーム値で更新し、更新されたトークンを新しい署名で再度保護することを容易にします。唯一の難しいステップは、 Service A に署名鍵を持たせることです。署名鍵は、安全なファイルシステムまたは Vault などのリモートの安全なストレージからプロビジョニングする必要があります。

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

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

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

    @GET
    String getUserName();
}

あるいは

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

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

    @GET
    String getUserName();
}

または、 quarkus.oidc-token-propagation.register-filter プロパティーおよび quarkus.oidc-token-propagation.json-web-token プロパティーの両方が true に設定されている場合、 JsonWebTokenRequestFilter は、すべての MP Rest または JAX-RS クライアントで自動的に登録することができます。

伝搬前のトークンの更新

注入されたトークンの iss (発行者) や aud (オーディエンス) のクレームを更新して、新しい署名で保護する必要がある場合は、 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

前述のように、Token Exchange プロトコルをサポートする Keycloak または OpenID Connect プロバイダーを使用する場合は、 AccessTokenRequestFilter を使用してください。

テスト

OpenID Connect Bearer Token Integration testing のセクションで説明したように、トークンを生成することができます。REST テストエンドポイントを準備します。登録されたトークン伝搬フィルターで注入された MP REST クライアントを使用するテストフロントエンドエンドポイントは、ダウンストリームエンドポイントで呼び出すことができます。たとえば、 main Quarkus リポジトリーの integration-tests/oidc-token-propagation を参照してください。

トークン伝播リアクティブ

以下の 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 アクセストークンを伝播するために使用することができます。

(非リアクティブの quarkus-oidc-token-propagation エクステンションとは対照的に) quarkus-oidc-token-propagation-reactive エクステンションは現在、伝播の前にトークンを交換したり放棄することをサポートしていません。しかし、これらの機能は将来的に追加される可能性があります。