3.2. 認可コードフローメカニズムの使用
3.2.1. OIDC プロバイダーエンドポイントへのアクセスの設定 リンクのコピーリンクがクリップボードにコピーされました!
OIDC web-app アプリケーションには、OIDC プロバイダーの認可、トークン、JsonWebKey (JWK) セット、および場合によっては UserInfo、イントロスペクション、および end-session (RP によって開始されるログアウト) エンドポイントの URL が必要です。
慣例により、設定された quarkus.oidc.auth-server-url に /.well-known/openid-configuration パスを追加することで検出されます。
あるいは、検出エンドポイントが利用できない場合、または検出エンドポイントのラウンドトリップを減らしたい場合は、エンドポイント検出を無効にして相対パス値を設定できます。以下に例を示します。
quarkus.oidc.auth-server-url=http://localhost:8180/realms/quarkus
quarkus.oidc.discovery-enabled=false
# Authorization endpoint: http://localhost:8180/realms/quarkus/protocol/openid-connect/auth
quarkus.oidc.authorization-path=/protocol/openid-connect/auth
# Token endpoint: http://localhost:8180/realms/quarkus/protocol/openid-connect/token
quarkus.oidc.token-path=/protocol/openid-connect/token
# JWK set endpoint: http://localhost:8180/realms/quarkus/protocol/openid-connect/certs
quarkus.oidc.jwks-path=/protocol/openid-connect/certs
# UserInfo endpoint: http://localhost:8180/realms/quarkus/protocol/openid-connect/userinfo
quarkus.oidc.user-info-path=/protocol/openid-connect/userinfo
# Token Introspection endpoint: http://localhost:8180/realms/quarkus/protocol/openid-connect/token/introspect
quarkus.oidc.introspection-path=/protocol/openid-connect/token/introspect
# End-session endpoint: http://localhost:8180/realms/quarkus/protocol/openid-connect/logout
quarkus.oidc.end-session-path=/protocol/openid-connect/logout
一部の OIDC プロバイダーはメタデータ検出をサポートしていますが、認可コードフローを完了したり、ユーザーログアウトなどのアプリケーション機能をサポートしたりするために必要なエンドポイント URL 値をすべて返すわけではありません。この制限を回避するには、次の例に示すように、不足しているエンドポイント URL 値をローカルで設定します。
# Metadata is auto-discovered but it does not return an end-session endpoint URL
quarkus.oidc.auth-server-url=http://localhost:8180/oidcprovider/account
# Configure the end-session URL locally.
# It can be an absolute or relative (to 'quarkus.oidc.auth-server-url') address
quarkus.oidc.end-session-path=logout
検出されたエンドポイント URL がローカル Quarkus エンドポイントで機能せず、より具体的な値が必要な場合は、この同じ設定を使用してその URL をオーバーライドできます。たとえば、グローバルおよびアプリケーション固有の end-session エンドポイントの両方をサポートするプロバイダーは、http://localhost:8180/oidcprovider/account/global-logout などのグローバル end-session URL を返します。この URL により、ユーザーは現在ログインしているすべてのアプリケーションからログアウトされます。ただし、現在のアプリケーションでユーザーを特定のアプリケーションからのみログアウトする必要がある場合は、quarkus.oidc.end-session-path=logout パラメーターを設定することで、グローバル end-session URL をオーバーライドできます。
3.2.1.1. OIDC プロバイダークライアント認証 リンクのコピーリンクがクリップボードにコピーされました!
OIDC プロバイダーでは通常、アプリケーションが OIDC エンドポイントと対話するときに、そのアプリケーションを識別して認証する必要があります。Quarkus OIDC (具体的には quarkus.oidc.runtime.OidcProviderClient クラス) は、認可コードを ID、アクセストークン、および更新トークンと交換する必要がある場合、または ID トークンとアクセストークンを更新またはイントロスペクトする必要がある場合に、OIDC プロバイダーに対して認証を行います。
通常、クライアント ID とクライアントシークレットは、特定のアプリケーションが OIDC プロバイダーに登録されるときに定義されます。すべての OIDC クライアント認証 オプションがサポートされています。以下に例を示します。
client_secret_basic の例:
quarkus.oidc.auth-server-url=http://localhost:8180/realms/quarkus/
quarkus.oidc.client-id=quarkus-app
quarkus.oidc.credentials.secret=mysecret
または、以下を実行します。
quarkus.oidc.auth-server-url=http://localhost:8180/realms/quarkus/
quarkus.oidc.client-id=quarkus-app
quarkus.oidc.credentials.client-secret.value=mysecret
次の例は、認証情報プロバイダー から取得したシークレットを示しています。
quarkus.oidc.auth-server-url=http://localhost:8180/realms/quarkus/
quarkus.oidc.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.credentials.client-secret.provider.key=mysecret-key
# Set it only if more than one CredentialsProvider can be registered
quarkus.oidc.credentials.client-secret.provider.name=oidc-credentials-provider
client_secret_post の例
quarkus.oidc.auth-server-url=http://localhost:8180/realms/quarkus/
quarkus.oidc.client-id=quarkus-app
quarkus.oidc.credentials.client-secret.value=mysecret
quarkus.oidc.credentials.client-secret.method=post
client_secret_jwt の例 (署名アルゴリズムは HS256):
quarkus.oidc.auth-server-url=http://localhost:8180/realms/quarkus/
quarkus.oidc.client-id=quarkus-app
quarkus.oidc.credentials.jwt.secret=AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow
シークレットが 認証情報プロバイダー から取得される client_secret_jwt の例:
quarkus.oidc.auth-server-url=http://localhost:8180/realms/quarkus/
quarkus.oidc.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.credentials.jwt.secret-provider.key=mysecret-key
# Set it only if more than one CredentialsProvider can be registered
quarkus.oidc.credentials.jwt.secret-provider.name=oidc-credentials-provider
PEM キーファイルを使用した private_key_jwt の例 (署名アルゴリズムは RS256):
quarkus.oidc.auth-server-url=http://localhost:8180/realms/quarkus/
quarkus.oidc.client-id=quarkus-app
quarkus.oidc.credentials.jwt.key-file=privateKey.pem
キーストアファイルを使用した private_key_jwt の例 (署名アルゴリズムは RS256):
quarkus.oidc.auth-server-url=http://localhost:8180/realms/quarkus/
quarkus.oidc.client-id=quarkus-app
quarkus.oidc.credentials.jwt.key-store-file=keystore.jks
quarkus.oidc.credentials.jwt.key-store-password=mypassword
quarkus.oidc.credentials.jwt.key-password=mykeypassword
# Private key alias inside the keystore
quarkus.oidc.credentials.jwt.key-id=mykeyAlias
client_secret_jwt または private_key_jwt 認証方法を使用すると、クライアントシークレットが OIDC プロバイダーに送信されないため、'中間者攻撃' によってシークレットが傍受されるリスクを回避できます。
3.2.1.1.1. 追加の JWT 認証オプション リンクのコピーリンクがクリップボードにコピーされました!
client_secret_jwt、private_key_jwt、または Apple post_jwt 認証方法を使用する場合は、JWT 署名アルゴリズム、キー識別子、audience、サブジェクト、発行者をカスタマイズできます。以下に例を示します。
# private_key_jwt client authentication
quarkus.oidc.auth-server-url=http://localhost:8180/realms/quarkus/
quarkus.oidc.client-id=quarkus-app
quarkus.oidc.credentials.jwt.key-file=privateKey.pem
# This is a token key identifier 'kid' header - set it if your OIDC provider requires it:
# Note if the key is represented in a JSON Web Key (JWK) format with a `kid` property, then
# using 'quarkus.oidc.credentials.jwt.token-key-id' is not necessary.
quarkus.oidc.credentials.jwt.token-key-id=mykey
# Use RS512 signature algorithm instead of the default RS256
quarkus.oidc.credentials.jwt.signature-algorithm=RS512
# The token endpoint URL is the default audience value, use the base address URL instead:
quarkus.oidc.credentials.jwt.audience=${quarkus.oidc-client.auth-server-url}
# custom subject instead of the client id:
quarkus.oidc.credentials.jwt.subject=custom-subject
# custom issuer instead of the client id:
quarkus.oidc.credentials.jwt.issuer=custom-issuer
3.2.1.1.2. Apple POST JWT リンクのコピーリンクがクリップボードにコピーされました!
Apple OIDC プロバイダーは client_secret_post メソッドを使用します。この方法では、シークレットは private_key_jwt 認証方法で生成された JWT ですが、Apple アカウント固有の発行者とサブジェクトのクレームが含まれます。
Quarkus Security では、quarkus-oidc が非標準の client_secret_post_jwt 認証方法をサポートします。この認証方法は次のように設定できます。
# Apple provider configuration sets a 'client_secret_post_jwt' authentication method
quarkus.oidc.provider=apple
quarkus.oidc.client-id=${apple.client-id}
quarkus.oidc.credentials.jwt.key-file=ecPrivateKey.pem
quarkus.oidc.credentials.jwt.token-key-id=${apple.key-id}
# Apple provider configuration sets ES256 signature algorithm
quarkus.oidc.credentials.jwt.subject=${apple.subject}
quarkus.oidc.credentials.jwt.issuer=${apple.issuer}
3.2.1.1.3. 相互 TLS (mTLS) リンクのコピーリンクがクリップボードにコピーされました!
一部の OIDC プロバイダーでは、相互 TLS 認証プロセスの一部としてクライアントが認証されることが必要になる場合があります。
次の例は、quarkus-oidc を mTLS をサポートするように設定する方法を示しています。
quarkus.oidc.tls.verification=certificate-validation
# Keystore configuration
quarkus.oidc.tls.key-store-file=client-keystore.jks
quarkus.oidc.tls.key-store-password=${key-store-password}
# Add more keystore properties if needed:
#quarkus.oidc.tls.key-store-alias=keyAlias
#quarkus.oidc.tls.key-store-alias-password=keyAliasPassword
# Truststore configuration
quarkus.oidc.tls.trust-store-file=client-truststore.jks
quarkus.oidc.tls.trust-store-password=${trust-store-password}
# Add more truststore properties if needed:
#quarkus.oidc.tls.trust-store-alias=certAlias
3.2.1.1.4. POST クエリー リンクのコピーリンクがクリップボードにコピーされました!
Strava OAuth2 プロバイダー などの一部のプロバイダーでは、クライアントクレデンシャルを HTTP POST クエリーパラメーターとして送信する必要があります。
quarkus.oidc.provider=strava
quarkus.oidc.client-id=quarkus-app
quarkus.oidc.credentials.client-secret.value=mysecret
quarkus.oidc.credentials.client-secret.method=query
3.2.1.2. イントロスペクションエンドポイント認証 リンクのコピーリンクがクリップボードにコピーされました!
一部の OIDC プロバイダーでは、Basic 認証と client_id および client_secret とは異なる認証情報を使用して、イントロスペクションエンドポイントへの認証を行う必要があります。OIDC プロバイダークライアント認証 セクションで説明されているように、client_secret_basic または client_secret_post クライアント認証メソッドのいずれかをサポートするように以前にセキュリティー認証を設定している場合は、次のように追加の設定を適用することを推奨します。
トークンをイントロスペクションする必要があり、イントロスペクションエンドポイント固有の認証メカニズムが必要な場合は、quarkus-oidc を次のように設定できます。
quarkus.oidc.introspection-credentials.name=introspection-user-name
quarkus.oidc.introspection-credentials.secret=introspection-user-secret
3.2.1.3. OIDC リクエストフィルター リンクのコピーリンクがクリップボードにコピーされました!
1 つ以上の OidcRequestFilter 実装を登録することで、Quarkus から OIDC プロバイダーに対して行われた OIDC リクエストをフィルタリングできます。これにより、新しいリクエストヘッダーを更新または追加したり、リクエストをログに記録したりすることもできます。
以下に例を示します。
package io.quarkus.it.keycloak;
import jakarta.enterprise.context.ApplicationScoped;
import io.quarkus.arc.Unremovable;
import io.quarkus.oidc.common.OidcRequestContextProperties;
import io.quarkus.oidc.common.OidcRequestFilter;
import io.vertx.mutiny.core.buffer.Buffer;
import io.vertx.mutiny.ext.web.client.HttpRequest;
@ApplicationScoped
@Unremovable
public class OidcTokenRequestCustomizer implements OidcRequestFilter {
@Override
public void filter(HttpRequest<Buffer> request, Buffer buffer, OidcRequestContextProperties contextProps) {
OidcConfigurationMetadata metadata = contextProps.get(OidcConfigurationMetadata.class.getName());
// Metadata URI is absolute, request URI value is relative
if (metadata.getTokenUri().endsWith(request.uri())) {
request.putHeader("TokenGrantDigest", calculateDigest(buffer.toString()));
}
}
private String calculateDigest(String bodyString) {
// Apply the required digest algorithm to the body string
}
}
あるいは、OidcRequestFilter.Endpoint 列挙型を使用して、このフィルターをトークンエンドポイントリクエストにのみ適用することもできます。
import jakarta.enterprise.context.ApplicationScoped;
import io.quarkus.arc.Unremovable;
import io.quarkus.oidc.common.OidcEndpoint;
import io.quarkus.oidc.common.OidcEndpoint.Type;
import io.quarkus.oidc.common.OidcRequestContextProperties;
import io.quarkus.oidc.common.OidcRequestFilter;
import io.vertx.mutiny.core.buffer.Buffer;
import io.vertx.mutiny.ext.web.client.HttpRequest;
@ApplicationScoped
@Unremovable
@OidcEndpoint(value = Type.DISCOVERY)
public class OidcDiscoveryRequestCustomizer implements OidcRequestFilter {
@Override
public void filter(HttpRequest<Buffer> request, Buffer buffer, OidcRequestContextProperties contextProps) {
request.putHeader("Discovery", "OK");
}
}
- 1
- このフィルターを、OIDC 検出エンドポイントのみを対象とするリクエストに制限します。
3.2.1.4. OIDC プロバイダーへのリダイレクトと OIDC プロバイダーからのリダイレクト リンクのコピーリンクがクリップボードにコピーされました!
ユーザーが認証のために OIDC プロバイダーにリダイレクトされる場合、リダイレクト URL には、認証が完了したときにユーザーをリダイレクトする場所をプロバイダーに指示する redirect_uri クエリーパラメーターが含まれます。この場合、これは Quarkus アプリケーションになります。
Quarkus は、デフォルトでこのパラメーターを現在のアプリケーション要求 URL に設定します。たとえば、ユーザーが http://localhost:8080/service/1 にある Quarkus サービスエンドポイントにアクセスしようとしている場合、redirect_uri パラメーターは http://localhost:8080/service/1 に設定されます。同様に、リクエスト URL が http://localhost:8080/service/2 の場合、redirect_uri パラメーターは http://localhost:8080/service/2 に設定されます。
一部の OIDC プロバイダーでは、すべてのリダイレクト URL の特定のアプリケーション (例: http://localhost:8080/service/callback) に対して redirect_uri が同じ値を持つことを要求します。このような場合は、quarkus.oidc.authentication.redirect-path プロパティーを設定する必要があります。たとえば、quarkus.oidc.authentication.redirect-path=/service/callback の場合、Quarkus は redirect_uri パラメーターを http://localhost:8080/service/callback などの絶対 URL に設定します。これは、現在のリクエスト URL に関係なく同じになります。
quarkus.oidc.authentication.redirect-path が設定されているが、ユーザーが http://localhost:8080/service/callback などの一意のコールバック URL にリダイレクトされた後に元のリクエスト URL を復元する必要がある場合は、quarkus.oidc.authentication.restore-path-after-redirect プロパティーを true に設定します。これにより、http://localhost:8080/service/1 などのリクエスト URL が復元されます。
3.2.1.5. 認証リクエストのカスタマイズ リンクのコピーリンクがクリップボードにコピーされました!
デフォルトでは、ユーザーが認証のために OIDC プロバイダーの認可エンドポイントにリダイレクトされるときに、response_type (code に設定)、scope (openid に設定)、client_id、redirect_uri、および state プロパティーのみが HTTP クエリーパラメーターとして OIDC プロバイダーの認可エンドポイントに渡されます。
quarkus.oidc.authentication.extra-params を使用して、さらにプロパティーを追加できます。たとえば、一部の OIDC プロバイダーは、リダイレクト URI のフラグメントの一部として認可コードを返すことを選択する場合があります。これにより認証プロセスが中断されます。次の例は、この問題を回避する方法を示しています。
quarkus.oidc.authentication.extra-params.response_mode=query
3.2.1.6. 認証エラーレスポンスのカスタマイズ リンクのコピーリンクがクリップボードにコピーされました!
ユーザーが OIDC 認可エンドポイントにリダイレクトされ、Quarkus アプリケーションを認証し、必要に応じて認可する場合、リダイレクト URI に無効なスコープが含まれている場合など、このリダイレクトリクエストは失敗する可能性があります。このような場合、プロバイダーは、予期される code パラメーターの代わりに、error および error_description パラメーターを使用してユーザーを Quarkus にリダイレクトします。
たとえば、プロバイダーへのリダイレクトに無効なスコープまたはその他の無効なパラメーターが含まれている場合に、これが発生する可能性があります。
このような場合、デフォルトで HTTP 401 エラーが返されます。ただし、よりユーザーフレンドリーな HTML エラーページを返すために、カスタムパブリックエラーエンドポイントを呼び出すように要求できます。これを行うには、次の例に示すように、quarkus.oidc.authentication.error-path プロパティーを設定します。
quarkus.oidc.authentication.error-path=/error
プロパティーがスラッシュ (/) 文字で始まり、パスが現在のエンドポイントのベース URI を基準としていることを確認します。たとえば、これが '/error' に設定され、現在のリクエスト URI が https://localhost:8080/callback?error=invalid_scope 場合、最終的なリダイレクトは https://localhost:8080/error?error=invalid_scope に行われます。
ユーザーがこのページにリダイレクトされて再認証されないようにするには、このエラーエンドポイントがパブリックリソースであることを確認します。
3.2.2. 認可データへのアクセス リンクのコピーリンクがクリップボードにコピーされました!
認可に関する情報にはさまざまな方法でアクセスできます。
3.2.2.1. ID とアクセストークンへのアクセス リンクのコピーリンクがクリップボードにコピーされました!
OIDC コード認証メカニズムは、認可コードフロー中に ID トークン、アクセストークン、および更新トークンの 3 つのトークンを取得します。
ID トークンは常に JWT トークンであり、JWT クレームによるユーザー認証を表します。これを使用して、発行 OIDC エンドポイント、ユーザー名、および クレーム と呼ばれるその他の情報を取得できます。IdToken 修飾子を使用して JsonWebToken を注入することで、ID トークンクレームにアクセスできます。
import jakarta.inject.Inject;
import org.eclipse.microprofile.jwt.JsonWebToken;
import io.quarkus.oidc.IdToken;
import io.quarkus.security.Authenticated;
@Path("/web-app")
@Authenticated
public class ProtectedResource {
@Inject
@IdToken
JsonWebToken idToken;
@GET
public String getUserName() {
return idToken.getName();
}
}
OIDC web-app アプリケーションは通常、アクセストークンを使用して、現在ログインしているユーザーに代わって他のエンドポイントにアクセスします。raw のアクセストークンには次のようにアクセスできます。
import jakarta.inject.Inject;
import org.eclipse.microprofile.jwt.JsonWebToken;
import io.quarkus.oidc.AccessTokenCredential;
import io.quarkus.security.Authenticated;
@Path("/web-app")
@Authenticated
public class ProtectedResource {
@Inject
JsonWebToken accessToken;
// or
// @Inject
// AccessTokenCredential accessTokenCredential;
@GET
public String getReservationOnBehalfOfUser() {
String rawAccessToken = accessToken.getRawToken();
//or
//String rawAccessToken = accessTokenCredential.getToken();
// Use the raw access token to access a remote endpoint.
// For example, use RestClient to set this token as a `Bearer` scheme value of the HTTP `Authorization` header:
// `Authorization: Bearer rawAccessToken`.
return getReservationfromRemoteEndpoint(rawAccesstoken);
}
}
AccessTokenCredential は、Quarkus web-app アプリケーションに発行されたアクセストークンが不透明 (バイナリー) で JsonWebToken に解析できない場合、または内部コンテンツがアプリケーションに必要な場合に使用されます。
JsonWebToken および AccessTokenCredential の注入は、@RequestScoped コンテキストと @ApplicationScoped コンテキストの両方でサポートされています。
Quarkus OIDC は、セッション管理 プロセスの一環として、更新トークンを使用して現在の ID とアクセストークンを更新します。
3.2.2.2. ユーザー情報 リンクのコピーリンクがクリップボードにコピーされました!
ID トークンが現在認証されているユーザーに関する十分な情報を提供しない場合は、UserInfo エンドポイントから詳細情報を取得できます。quarkus.oidc.authentication.user-info-required=true プロパティーを設定して、OIDC UserInfo エンドポイントから UserInfo JSON オブジェクトを要求します。
認可コードグラントのレスポンスで返されたアクセストークンを使用して、OIDC プロバイダーの UserInfo エンドポイントにリクエストが送信され、io.quarkus.oidc.UserInfo (単純な jakarta.json.JsonObject ラッパー) オブジェクトが作成されます。io.quarkus.oidc.UserInfo は、SecurityIdentity userinfo 属性として注入またはアクセスできます。
3.2.2.3. OIDC 設定情報へのアクセス リンクのコピーリンクがクリップボードにコピーされました!
現在のテナントの検出された OpenID Connect 設定メタデータ は、io.quarkus.oidc.OidcConfigurationMetadata によって表され、SecurityIdentity configuration-metadata 属性として注入またはアクセスできます。
エンドポイントがパブリックの場合、デフォルトのテナントの OidcConfigurationMetadata が注入されます。
3.2.2.4. トークンクレームと SecurityIdentity ロールのマッピング リンクのコピーリンクがクリップボードにコピーされました!
検証済みトークンから SecurityIdentity ロールにロールをマップする方法は、ベアラートークン の場合と同じです。唯一の違いは、ID トークン がデフォルトでロールのソースとして使用されることです。
Keycloak を使用する場合は、ID トークンに groups クレームが含まれるように microprofile-jwt クライアントスコープを設定します。詳細は、Keycloak サーバー管理ガイド を参照してください。
ただし、OIDC プロバイダーによっては、ロールがアクセストークンまたはユーザー情報に保存される場合があります。
アクセストークンにロールが含まれており、このアクセストークンがダウンストリームエンドポイントに伝播されることを意図していない場合は、quarkus.oidc.roles.source=accesstoken を設定します。
UserInfo がロールのソースである場合は、quarkus.oidc.roles.source=userinfo を設定し、必要に応じて quarkus.oidc.roles.role-claim-path を設定します。
さらに、カスタム SecurityIdentityAugmentor を使用してロールを追加することもできます。詳細は、SecurityIdentity のカスタマイズ を参照してください。HTTP セキュリティーポリシー を使用して、トークン要求から作成された SecurityIdentity ロールをデプロイメント固有のロールにマップすることもできます。
3.2.3. トークンと認証データの有効性の確保 リンクのコピーリンクがクリップボードにコピーされました!
認証プロセスの中核となる部分は、信頼チェーンと情報の有効性を確保することです。これは、トークンを信頼できることを確認することによって行われます。
3.2.3.1. トークンの検証とイントロスペクション リンクのコピーリンクがクリップボードにコピーされました!
OIDC 認可コードフロートークンの検証プロセスは、ベアラートークン認証トークンの検証およびイントロスペクションロジックに従います。詳細は、「Quarkus OpenID Connect (OIDC) ベアラートークン認証」ガイドの トークンの検証とイントロスペクション セクションを参照してください。
Quarkus web-app アプリケーションでは、アクセストークンが現在の Quarkus web-app エンドポイントへのアクセスには使用されず、このアクセストークンを必要とするサービスに伝播されることが意図されているため、デフォルトでは IdToken のみが検証されます。アクセストークンに現在の Quarkus エンドポイント (quarkus.oidc.roles.source=accesstoken) にアクセスするために必要なロールが含まれていることが予想される場合は、それも検証されます。
3.2.3.2. トークンイントロスペクションと UserInfo キャッシュ リンクのコピーリンクがクリップボードにコピーされました!
コードフローアクセストークンは、ロールのソースであると予想されない限り、イントロスペクトされません。ただし、これらは UserInfo を取得するために使用されます。トークンイントロスペクション、UserInfo、またはその両方が必要な場合は、コードフローアクセストークンを使用したリモート呼び出しが 1 回または 2 回発生します。
デフォルトのトークンキャッシュの使用またはカスタムキャッシュ実装の登録の詳細は、トークンのイントロスペクションと UserInfo キャッシュ を参照してください。
3.2.3.3. JSON Web Token のクレームの検証 リンクのコピーリンクがクリップボードにコピーされました!
iss (発行者) クレームを含むクレーム検証の詳細は、JSON Web Token のクレームの検証 セクションを参照してください。これは、ID トークンに適用されますが、web-app アプリケーションがアクセストークンの検証を要求した場合は、JWT 形式のアクセストークンにも適用されます。
3.2.3.4. Proof Key for Code Exchange (PKCE) によるセキュリティー強化 リンクのコピーリンクがクリップボードにコピーされました!
Proof Key for Code Exchange (PKCE) は、認可コードの傍受のリスクを最小限に抑えます。
PKCE は、ブラウザーで実行する SPA スクリプトなどのパブリック OIDC クライアントにとって最も重要ですが、Quarkus OIDC web-app アプリケーションに追加の保護を提供することもできます。PKCE を使用すると、Quarkus OIDC web-app アプリケーションは機密 OIDC クライアントとして機能し、クライアントシークレットを安全に保存し、それを使用してトークンのコードを交換できます。
次の例に示すように、quarkus.oidc.authentication.pkce-required プロパティーと、状態 Cookie 内の PKCE コード検証を暗号化するために必要な 32 文字のシークレットを使用して、OIDC web-app エンドポイントの PKCE を有効にできます。
quarkus.oidc.authentication.pkce-required=true
quarkus.oidc.authentication.state-secret=eUk1p7UB3nFiXZGUXi0uph1Y9p34YhBU
すでに 32 文字のクライアントシークレットがある場合は、別のシークレットキーを使用する場合を除き、quarkus.oidc.authentication.pkce-secret プロパティーを設定する必要はありません。このシークレットは、設定されていない場合、およびクライアントシークレットの長さが 16 文字未満でクライアントシークレットへのフォールバックが不可能な場合に自動生成されます。
ユーザーが code_challenge クエリーパラメーターを使用して OIDC プロバイダーにリダイレクトされ認証される間、ランダムに生成された PKCE code_verifier を暗号化するには秘密鍵が必要です。code_verifier は、ユーザーが Quarkus にリダイレクトされたときに復号化され、code、クライアントシークレット、およびその他のパラメーターとともにトークンエンドポイントに送信され、コード交換が完了します。code_verifier の SHA256 ダイジェストが認証要求時に提供された code_challenge と一致しない場合、プロバイダーはコード交換に失敗します。
3.2.4. 認証の有効期間の処理と制御 リンクのコピーリンクがクリップボードにコピーされました!
認証のもう 1 つの重要な要件は、ユーザーがリクエストごとに認証する必要なく、セッションの基になるデータが最新であることを確認することです。ログアウトイベントが明示的に要求される状況もあります。Quarkus アプリケーションを保護するための適切なバランスを見つけるには、次の重要なポイントを参考にしてください。
3.2.4.1. Cookie リンクのコピーリンクがクリップボードにコピーされました!
OIDC アダプターは、Cookie を使用してセッション、コードフロー、およびログアウト後の状態を維持します。この状態は、認証データの有効期間を制御する重要な要素です。
重複または異なるルートを持つ保護されたリソースにアクセスするときに同じ Cookie が表示されるようにするには、quarkus.oidc.authentication.cookie-path プロパティーを使用します。以下に例を示します。
-
/index.htmlおよび/web-app/service -
/web-app/service1と/web-app/service2 -
/web-app1/serviceおよび/web-app2/service
デフォルトでは、quarkus.oidc.authentication.cookie-path は / に設定されていますが、必要に応じてこれを /web-app などのより具体的なパスに変更できます。
Cookie パスを動的に設定するには、quarkus.oidc.authentication.cookie-path-header プロパティーを設定します。quarkus.oidc.authentication.cookie-path-header プロパティーを設定します。たとえば、`X-Forwarded-Prefix` HTTP ヘッダーの値を使用して Cookie パスを動的に設定するには、プロパティーを quarkus.oidc.authentication.cookie-path-header=X-Forwarded-Prefix に設定します。
quarkus.oidc.authentication.cookie-path-header が設定されているが、現在のリクエストで設定された HTTP ヘッダーが利用できない場合は、quarkus.oidc.authentication.cookie-path がチェックされます。
アプリケーションが複数のドメインにデプロイされている場合は、セッション Cookie がすべての保護された Quarkus サービスに表示されるように、quarkus.oidc.authentication.cookie-domain プロパティーを設定します。たとえば、次の 2 つのドメインに Quarkus サービスをデプロイしている場合は、quarkus.oidc.authentication.cookie-domain プロパティーを company.net に設定する必要があります。
- https://whatever.wherever.company.net/
- https://another.address.company.net/
3.2.4.2. セッション Cookie とデフォルトの TokenStateManager リンクのコピーリンクがクリップボードにコピーされました!
OIDC CodeAuthenticationMechanism は、デフォルトの io.quarkus.oidc.TokenStateManager インターフェイス実装を使用して、認可コードまたはリフレッシュグラントのレスポンスで返された ID、アクセストークン、およびリフレッシュトークンを暗号化されたセッション Cookie に保持します。
これにより、Quarkus OIDC エンドポイントは完全にステートレスになり、最高のスケーラビリティー結果を達成するにはこのストラテジーに従うことが推奨されます。
トークンを保存するための代替方法は、セッション Cookie とカスタム TokenStateManager セクションを参照してください。これは、特に標準のサーバー側ストレージが特定の要件を満たしていない場合に、トークン状態を管理するためのカスタマイズされたソリューションが必要なユーザーに最適です。
デフォルトの TokenStateManager を設定して、アクセストークンをセッション Cookie に保存しないようにし、ID トークンと更新トークンのみ、または単一の ID トークンのみを保持するようにすることができます。
アクセストークンは、エンドポイントが次のアクションを実行する必要がある場合にのみ必要です。
-
UserInfoを取得します。 - このアクセストークンを使用してダウンストリームサービスにアクセスします
- アクセストークンに関連付けられたロールを使用します。これはデフォルトでチェックされています。
このような場合は、quarkus.oidc.token-state-manager.strategy プロパティーを使用して、トークン状態ストラテジーを次のように設定します。
| 次を行う場合: | プロパティーを以下のように設定します。 |
|---|---|
| ID と更新トークンのみ保存する |
|
| ID トークンのみ保存する |
|
選択したセッション Cookie ストラテジーによってトークンが結合され、4 KB を超える大きなセッション Cookie 値が生成される場合、一部のブラウザーではそのような Cookie サイズを処理できない可能性があります。これは、ID、アクセストークン、および更新トークンが JWT トークンであり、選択されたストラテジーが keep-all-tokens である場合、またはストラテジーが id-refresh-token であるときに ID トークンと更新トークンで発生する可能性があります。この問題を回避するには、quarkus.oidc.token-state-manager.split-tokens=true を設定して、トークンごとに一意のセッショントークンを作成します。
デフォルトの TokenStateManager は、トークンをセッション Cookie に保存する前に暗号化します。次の例は、トークンを分割して暗号化するように設定する方法を示しています。
quarkus.oidc.auth-server-url=http://localhost:8180/realms/quarkus
quarkus.oidc.client-id=quarkus-app
quarkus.oidc.credentials.secret=secret
quarkus.oidc.application-type=web-app
quarkus.oidc.token-state-manager.split-tokens=true
quarkus.oidc.token-state-manager.encryption-secret=eUk1p7UB3nFiXZGUXi0uph1Y9p34YhBU
トークン暗号化シークレットの長さが 32 文字以上である必要があります。このキーが設定されていない場合は、quarkus.oidc.credentials.secret または quarkus.oidc.credentials.jwt.secret のいずれかがハッシュされ、暗号鍵が作成されます。
Quarkus が次のいずれかの認証方法を使用して OIDC プロバイダーに対して認証する場合は、quarkus.oidc.token-state-manager.encryption-secret プロパティーを設定します。
- mTLS
-
秘密の RSA または EC キーを使用して JWT トークンに署名する場合は
private_key_jwt
そうでない場合はランダムなキーが生成されますが、Quarkus アプリケーションがクラウドで実行し、複数の Pod がリクエストを管理している場合は、問題が発生する可能性があります。
quarkus.oidc.token-state-manager.encryption-required=false を設定することで、セッション Cookie でのトークン暗号化を無効化できます。
3.2.4.3. セッション Cookie とカスタムの TokenStateManager リンクのコピーリンクがクリップボードにコピーされました!
トークンをセッション Cookie に関連付ける方法をカスタマイズする場合は、カスタムの io.quarkus.oidc.TokenStateManager 実装を @ApplicationScoped CDI Bean として登録します。
たとえば、トークンをキャッシュクラスターに保持し、キーのみをセッション Cookie に保存したい場合があります。トークンを複数のマイクロサービスノードで利用できるようにする必要がある場合、このアプローチではいくつかの課題が生じる可能性があることに注意してください。
以下は簡単な例です。
package io.quarkus.oidc.test;
import jakarta.annotation.Priority;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.Alternative;
import jakarta.inject.Inject;
import io.quarkus.oidc.AuthorizationCodeTokens;
import io.quarkus.oidc.OidcTenantConfig;
import io.quarkus.oidc.TokenStateManager;
import io.quarkus.oidc.runtime.DefaultTokenStateManager;
import io.smallrye.mutiny.Uni;
import io.vertx.ext.web.RoutingContext;
@ApplicationScoped
@Alternative
@Priority(1)
public class CustomTokenStateManager implements TokenStateManager {
@Inject
DefaultTokenStateManager tokenStateManager;
@Override
public Uni<String> createTokenState(RoutingContext routingContext, OidcTenantConfig oidcConfig,
AuthorizationCodeTokens sessionContent, TokenStateManager.CreateTokenStateRequestContext requestContext) {
return tokenStateManager.createTokenState(routingContext, oidcConfig, sessionContent, requestContext)
.map(t -> (t + "|custom"));
}
@Override
public Uni<AuthorizationCodeTokens> getTokens(RoutingContext routingContext, OidcTenantConfig oidcConfig,
String tokenState, TokenStateManager.GetTokensRequestContext requestContext) {
if (!tokenState.endsWith("|custom")) {
throw new IllegalStateException();
}
String defaultState = tokenState.substring(0, tokenState.length() - 7);
return tokenStateManager.getTokens(routingContext, oidcConfig, defaultState, requestContext);
}
@Override
public Uni<Void> deleteTokens(RoutingContext routingContext, OidcTenantConfig oidcConfig, String tokenState,
TokenStateManager.DeleteTokensRequestContext requestContext) {
if (!tokenState.endsWith("|custom")) {
throw new IllegalStateException();
}
String defaultState = tokenState.substring(0, tokenState.length() - 7);
return tokenStateManager.deleteTokens(routingContext, oidcConfig, defaultState, requestContext);
}
}
暗号化されたセッション Cookie にトークンを保存するデフォルトの TokenStateManager の詳細は、セッション Cookie とデフォルトの TokenStateManager を参照してください。
3.2.4.4. ログアウトと有効期限 リンクのコピーリンクがクリップボードにコピーされました!
認証情報が期限切れになる主な方法は 2 つあります。トークンの有効期限が切れて更新されなかったか、明示的なログアウト操作がトリガーされたかです。
明示的なログアウト操作から始めましょう。
3.2.4.4.1. ユーザーが開始したログアウト リンクのコピーリンクがクリップボードにコピーされました!
ユーザーは、quarkus.oidc.logout.path プロパティーで設定された Quarkus エンドポイントログアウトパスにリクエストを送信することで、ログアウトをリクエストできます。たとえば、エンドポイントアドレスが https://application.com/webapp で、quarkus.oidc.logout.path が /logout に設定されている場合、ログアウト要求は https://application.com/webapp/logout に送信する必要があります。
このログアウトリクエストは、RP-initiated logout を開始します。ユーザーはログアウトするために OIDC プロバイダーにリダイレクトされ、そこでログアウトが本当に意図されたものであるかどうかの確認を求められる場合があります。
ログアウトが完了し、quarkus.oidc.logout.post-logout-path プロパティーが設定されている場合、ユーザーはエンドポイントのログアウト後ページに戻されます。たとえば、エンドポイントアドレスが https://application.com/webapp で、quarkus.oidc.logout.post-logout-path が /signin に設定されている場合、ユーザーは https://application.com/webapp/signin に戻されます。この URI は、OIDC プロバイダーに有効な post_logout_redirect_uri として登録されている必要があることに注意してください。
quarkus.oidc.logout.post-logout-path が設定されている場合、q_post_logout Cookie が作成され、一致する state クエリーパラメーターがログアウトリダイレクト URI に追加され、ログアウトが完了すると OIDC プロバイダーはこの state を返します。Quarkus web-app アプリケーションでは、state クエリーパラメーターが q_post_logout Cookie の値と一致するかどうかを確認することを推奨します。これは、たとえば Jakarta REST フィルターで実行できます。
OpenID Connect マルチテナンシー を使用する場合、Cookie 名が異なることに注意してください。たとえば、tenant_1 ID を持つテナントの場合は q_post_logout_tenant_1 という名前になります。
ログアウトフローを開始するように Quarkus アプリケーションを設定する方法の例を次に示します。
quarkus.oidc.auth-server-url=http://localhost:8180/realms/quarkus
quarkus.oidc.client-id=frontend
quarkus.oidc.credentials.secret=secret
quarkus.oidc.application-type=web-app
quarkus.oidc.logout.path=/logout
# Logged-out users should be returned to the /welcome.html site which will offer an option to re-login:
quarkus.oidc.logout.post-logout-path=/welcome.html
# Only the authenticated users can initiate a logout:
quarkus.http.auth.permission.authenticated.paths=/logout
quarkus.http.auth.permission.authenticated.policy=authenticated
# All users can see the Welcome page:
quarkus.http.auth.permission.public.paths=/welcome.html
quarkus.http.auth.permission.public.policy=permit
また、quarkus.oidc.authentication.cookie-path をすべてのアプリケーションリソースに共通のパス値 (この例では /) に設定することもできます。詳細は、Cookie セクションを参照してください。
一部の OIDC プロバイダーは、RP 開始ログアウト 仕様をサポートしておらず、OpenID Connect の既知の end_session_endpoint メタデータプロパティーを返しません。ただし、このような OIDC プロバイダーの特定のログアウトメカニズムは、ログアウト URL クエリーパラメーターの命名方法のみが異なるため、Quarkus にとってはこれは問題ではありません。
RP 開始ログアウト 仕様によれば、quarkus.oidc.logout.post-logout-path プロパティーは post_logout_redirect_uri クエリーパラメーターとして表されますが、この仕様をサポートしていないプロバイダーでは認識されません。
この問題を回避するには、quarkus.oidc.logout.post-logout-url-param を使用できます。quarkus.oidc.logout.extra-params を使用して、追加のログアウトクエリーパラメーターをリクエストすることもできます。たとえば、Auth0 でログアウトをサポートする方法は次のとおりです。
quarkus.oidc.auth-server-url=https://dev-xxx.us.auth0.com
quarkus.oidc.client-id=redacted
quarkus.oidc.credentials.secret=redacted
quarkus.oidc.application-type=web-app
quarkus.oidc.tenant-logout.logout.path=/logout
quarkus.oidc.tenant-logout.logout.post-logout-path=/welcome.html
# Auth0 does not return the `end_session_endpoint` metadata property. Instead, you must configure it:
quarkus.oidc.end-session-path=v2/logout
# Auth0 will not recognize the 'post_logout_redirect_uri' query parameter so ensure it is named as 'returnTo':
quarkus.oidc.logout.post-logout-uri-param=returnTo
# Set more properties if needed.
# For example, if 'client_id' is provided, then a valid logout URI should be set as the Auth0 Application property, without it - as Auth0 Tenant property:
quarkus.oidc.logout.extra-params.client_id=${quarkus.oidc.client-id}
3.2.4.4.2. バックチャネルログアウト リンクのコピーリンクがクリップボードにコピーされました!
OIDC プロバイダーは、認証データを使用してすべてのアプリケーションのログアウトを強制できます。これはバックチャネルログアウトと呼ばれます。この場合、OIDC は各アプリケーションから特定の URL を呼び出してログアウトをトリガーします。
OIDC プロバイダーは、バックチャネルログアウト を使用して、ユーザーエージェントをバイパスし、現在ログインしているすべてのアプリケーションから現在のユーザーをログアウトします。
次のようにして、バックチャネルログアウトをサポートするように Quarkus を設定できます。
quarkus.oidc.auth-server-url=http://localhost:8180/realms/quarkus
quarkus.oidc.client-id=frontend
quarkus.oidc.credentials.secret=secret
quarkus.oidc.application-type=web-app
quarkus.oidc.logout.backchannel.path=/back-channel-logout
絶対 back-channel logout URL は、現在のエンドポイント URL に quarkus.oidc.back-channel-logout.path を追加することによって計算されます (例: http://localhost:8080/back-channel-logout)。この URL は、OIDC プロバイダーの管理コンソールで設定する必要があります。
OIDC プロバイダーが現在のログアウトトークンに有効期限要求を設定していない場合は、ログアウトトークンの検証が成功するようにトークンの有効期間プロパティーを設定する必要もあります。たとえば、ログアウトトークンの iat (発行時刻) から 10 秒以上経過しないようにするには、quarkus.oidc.token.age=10S を設定します。
3.2.4.4.3. フロントチャンネルログアウト リンクのコピーリンクがクリップボードにコピーされました!
フロントチャネルログアウト を使用すると、ブラウザーなどのユーザーエージェントから現在のユーザーを直接ログアウトできます。これは バックチャネルログアウト に似ていますが、ログアウト手順は OIDC プロバイダーによってバックグラウンドで実行されるのではなく、ブラウザーなどのユーザーエージェントによって実行されます。このオプションはほとんど使用されません。
以下のように、フロントチャネルログアウトをサポートするように Quarkus を設定できます。
quarkus.oidc.auth-server-url=http://localhost:8180/realms/quarkus
quarkus.oidc.client-id=frontend
quarkus.oidc.credentials.secret=secret
quarkus.oidc.application-type=web-app
quarkus.oidc.logout.frontchannel.path=/front-channel-logout
このパスは現在のリクエストのパスと比較され、これらのパスが一致するとユーザーはログアウトされます。
3.2.4.4.4. ローカルログアウト リンクのコピーリンクがクリップボードにコピーされました!
ユーザーが開始したログアウト により、ユーザーは OIDC プロバイダーからログアウトされます。シングルサインオンとして使用する場合は、必要なものではない可能性があります。たとえば、OIDC プロバイダーが Google の場合は、Google とそのサービスからログアウトされます。代わりに、ユーザーはその特定のアプリケーションからログアウトしたいだけかもしれません。もう 1 つのユースケースとしては、OIDC プロバイダーにログアウトエンドポイントがない場合が考えられます。
OidcSession を使用すると、次の例に示すように、ローカルログアウトをサポート (つまり、ローカルセッション Cookie のみをクリア) できます。
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import io.quarkus.oidc.OidcSession;
@Path("/service")
public class ServiceResource {
@Inject
OidcSession oidcSession;
@GET
@Path("logout")
public String logout() {
oidcSession.logout().await().indefinitely();
return "You are logged out".
}
3.2.4.4.4.1. ローカルログアウトに OidcSession を使用する リンクのコピーリンクがクリップボードにコピーされました!
io.quarkus.oidc.OidcSession は現在の IdToken のラッパーであり、Local logout の実行、現在のセッションのテナント ID の取得、セッションの有効期限の確認に役立ちます。今後、さらに便利なメソッドが追加される予定です。
3.2.4.5. セッション管理 リンクのコピーリンクがクリップボードにコピーされました!
デフォルトでは、ログアウトは OIDC プロバイダーによって発行された ID トークンの有効期限に基づいて行われます。ID トークンの有効期限が切れると、Quarkus エンドポイントの現在のユーザーセッションは無効になり、ユーザーは認証のために再度 OIDC プロバイダーにリダイレクトされます。OIDC プロバイダーのセッションがまだアクティブな場合、ユーザーは認証情報を再度入力する必要なく自動的に再認証されます。
quarkus.oidc.token.refresh-expired プロパティーを有効にすると、現在のユーザーセッションを自動的に延長できます。true に設定すると、現在の ID トークンの有効期限が切れたときに、リフレッシュトークングラントを使用して、ID トークンだけでなくアクセストークンと更新トークンもリフレッシュされます。
keycloak.js などの OIDC プロバイダースクリプトが認可コードフローを管理する サービスアプリケーション用の単一ページアプリケーション がある場合、そのスクリプトは SPA 認証セッションの有効期間も制御します。
Quarkus OIDC web-app アプリケーションを使用する場合は、Quarkus OIDC コード認証メカニズムによってユーザーセッションの有効期間が管理されます。
更新トークンを使用するには、セッション Cookie の有効期間を慎重に設定する必要があります。セッションの有効期間は、ID トークンの有効期間よりも長く、更新トークンの有効期間と近いか等しい必要があります。
セッションの有効期間は、現在の ID トークンの有効期間の値と、quarkus.oidc.authentication.session-age-extension プロパティーおよび quarkus.oidc.token.lifespan-grace プロパティーの値を加算して計算します。
必要に応じて、quarkus.oidc.authentication.session-age-extension プロパティーのみを使用して、セッションの有効期間を大幅に延長します。quarkus.oidc.token.lifespan-grace プロパティーは、小さなクロックのずれを考慮する場合にのみ使用します。
現在認証されているユーザーが保護された Quarkus エンドポイントに戻り、セッション Cookie に関連付けられた ID トークンの有効期限が切れている場合、デフォルトでは、ユーザーは再認証のために OIDC 認可エンドポイントに自動的にリダイレクトされます。ユーザーとこの OIDC プロバイダー間のセッションがまだアクティブな場合、OIDC プロバイダーはユーザーに再度チャレンジする可能性があります。これは、セッションが ID トークンよりも長く続くように設定されている場合に発生する可能性があります。
quarkus.oidc.token.refresh-expired が true に設定されている場合、期限切れの ID トークン (およびアクセストークン) は、最初の認可コードグラントのレスポンスで返されたリフレッシュトークンを使用して更新されます。この更新トークンも、このプロセスの一部としてリサイクル (更新) される可能性があります。その結果、新しいセッション Cookie が作成され、セッションが延長されます。
ユーザーがあまりアクティブでない場合は、quarkus.oidc.authentication.session-age-extension プロパティーを使用して、期限切れの ID トークンを処理できます。ID トークンの有効期限が切れると、Cookie の有効期間が経過するため、次のユーザーリクエスト時にセッション Cookie が Quarkus エンドポイントに返されない可能性があります。Quarkus は、このリクエストが最初の認証リクエストであると想定します。quarkus.oidc.authentication.session-age-extension を、ほとんどアクティブでないユーザー向けに、またセキュリティーポリシーに従って、適切な 長さに設定します。
さらに一歩進んで、期限切れが近づいている ID トークンまたはアクセストークンを事前に更新することもできます。quarkus.oidc.token.refresh-token-time-skew を、更新を予測する値に設定します。現在のユーザーリクエスト中に、現在の ID トークンがこの quarkus.oidc.token.refresh-token-time-skew 内に期限切れになると計算されると、トークンは更新され、新しいセッション Cookie が作成されます。このプロパティーは、ID トークンの有効期間よりも短い値に設定する必要があります。この有効期間の値に近いほど、ID トークンの更新頻度が高くなります。
単純な JavaScript 関数を使用して Quarkus エンドポイントに定期的に ping を送信し、ユーザーアクティビティーをエミュレートすることで、このプロセスをさらに最適化できます。これにより、ユーザーが再認証される必要がある時間枠が最小限に抑えられます。
ユーザーセッションを無期限に延長することはできません。有効期限が切れた ID トークンを持つ再ログインユーザーは、更新トークンの有効期限が切れると、OIDC プロバイダーエンドポイントで再認証する必要があります。
3.2.5. GitHub および非 OIDC OAuth2 プロバイダーとの統合 リンクのコピーリンクがクリップボードにコピーされました!
GitHub または LinkedIn などの一部の周知のプロバイダーは、OpenID Connect プロバイダーではなく、authorization code flow をサポートする OAuth2 プロバイダーです。たとえば、GitHub OAuth2 や LinkedIn OAuth2 などです。OIDC は OAuth2 上に構築されていることに注意してください。
OIDC プロバイダーと OAuth2 プロバイダーの主な違いは、OIDC プロバイダーが、OAuth2 プロバイダーによって返される標準の認可コードフローの access トークンと refresh トークンに加えて、ユーザー認証を表す ID Token を返すことです。
GitHub などの OAuth2 プロバイダーは IdToken を返さず、ユーザー認証は暗黙的であり、access トークンによって間接的に表されます。この access トークンは、認証されたユーザーに代わって現在の Quarkus web-app アプリケーションが一部のデータにアクセスすることを、認証されたユーザーが認可していることを表します。
OIDC の場合は、認証の有効性の証明として ID トークンを検証しますが、OAuth2 の場合はアクセストークンを検証します。これは、アクセストークンを要求し、通常はユーザー情報を返すエンドポイントを後で呼び出すことによって実行されます。このアプローチは OIDC UserInfo アプローチに似ており、UserInfo はユーザーではなく Quarkus OIDC によって取得されます。
たとえば、GitHub を使用する場合、Quarkus エンドポイントは access トークンを取得できます。これにより、Quarkus エンドポイントは現在のユーザーの GitHub プロファイルをリクエストできます。
このような OAuth2 サーバーとの統合をサポートするには、quarkus-oidc を少し異なる方法で設定して、IdToken なしで認可コードフローの応答を許可する必要があります (quarkus.oidc.authentication.id-token-required=false)。
IdToken なしで認可コードフローをサポートするようにエクステンションを設定しても、quarkus-oidc の動作方法を標準化するために内部 IdToken が生成されます。IdToken を使用すると、認証セッションをサポートし、リクエストごとにユーザーが GitHub などのプロバイダーにリダイレクトされるのを回避できます。この場合、セッションの有効期間は 5 分に設定されていますが、セッション管理 セクションで説明されているように、さらに延長することができます。
これにより、複数の OIDC プロバイダーをサポートするアプリケーションの処理が簡素化されます。
次のステップは、返されたアクセストークンが有用であり、現在の Quarkus エンドポイントに対して有効であることを確認することです。最初の方法は、プロバイダーがそのようなエンドポイントを提供している場合、quarkus.oidc.introspection-path を設定して OAuth2 プロバイダーのイントロスペクションエンドポイントを呼び出すことです。この場合、quarkus.oidc.roles.source=accesstoken を使用して、アクセストークンをロールのソースとして使用できます。イントロスペクションエンドポイントが存在しない場合、UserInfo は少なくともアクセストークンを検証することから、代わりにプロバイダーから UserInfo のリクエストを試行できます。これを行うには、quarkus.oidc.token.verify-access-token-with-user-info=true を指定します。また、quarkus.oidc.user-info-path プロパティーを、ユーザー情報を取得する URL エンドポイント (またはアクセストークンによって保護されたエンドポイント) に設定する必要があります。GitHub の場合、イントロスペクションエンドポイントがないため、UserInfo をリクエストする必要があります。
UserInfo が必要な場合、リクエストごとにリモート呼び出しが実行されます。したがって、UserInfo データをキャッシュすることを推奨します。詳細は、「OpenID Connect (OIDC) ベアラートークン認証」ガイドの トークンイントロスペクションと UserInfo キャッシュ セクションを参照してください。
あるいは、quarkus.oidc.cache-user-info-in-idtoken=true プロパティーを使用して、UserInfo が内部で生成された IdToken に埋め込まれるようにリクエストすることを推奨します。このアプローチの利点は、デフォルトでは、キャッシュされた UserInfo 状態がエンドポイントに保持されず、代わりにセッション Cookie に保存されることです。UserInfo に機密データが含まれている場合は、IdToken の暗号化も検討してください。詳細は、TokenStateManager を使用してトークンを暗号化する を参照してください。
OAuth2 サーバーは、周知の設定エンドポイントをサポートしていない可能性があります。この場合、検出を無効にして、認可、トークン、イントロスペクション、および UserInfo エンドポイントパスを手動で設定する必要があります。
Apple、Facebook、GitHub、Google、Microsoft、Spotify、Twitter などのよく知られた OIDC または OAuth2 プロバイダーの場合、Quarkus は quarkus.oidc.provider プロパティーを使用してアプリケーションの設定を大幅に簡素化できます。GitHub OAuth アプリケーションを作成した 後、quarkus-oidc を GitHub と統合する方法は次のとおりです。Quarkus エンドポイントを次のように設定します。
quarkus.oidc.provider=github
quarkus.oidc.client-id=github_app_clientid
quarkus.oidc.credentials.secret=github_app_clientsecret
# user:email scope is requested by default, use 'quarkus.oidc.authentication.scopes' to request different scopes such as `read:user`.
# See https://docs.github.com/en/developers/apps/building-oauth-apps/scopes-for-oauth-apps for more information.
# Consider enabling UserInfo Cache
# quarkus.oidc.token-cache.max-size=1000
# quarkus.oidc.token-cache.time-to-live=5M
#
# Or having UserInfo cached inside IdToken itself
# quarkus.oidc.cache-user-info-in-idtoken=true
他のよく知られているプロバイダーの設定の詳細は、OpenID Connect プロバイダー を参照してください。
これは、GET http://localhost:8080/github/userinfo を使用して現在認証されているユーザーのプロファイルを返し、個々の UserInfo プロパティーとしてアクセスするためのエンドポイントに必要なすべてです。
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import io.quarkus.oidc.UserInfo;
import io.quarkus.security.Authenticated;
@Path("/github")
@Authenticated
public class TokenResource {
@Inject
UserInfo userInfo;
@GET
@Path("/userinfo")
@Produces("application/json")
public String getUserInfo() {
return userInfo.getUserInfoString();
}
}
OpenID Connect Multi-Tenancy を使用して複数のソーシャルプロバイダー (たとえば、IdToken を返す OIDC プロバイダーである Google と、IdToken を返さず UserInfo へのアクセスのみを許可する OAuth2 プロバイダーである GitHub) をサポートする場合は、Google フローと GitHub フローの両方で、注入された SecurityIdentity のみを使用してエンドポイントを動作させることができます。GitHub フローがアクティブで、内部で生成された IdToken で作成されたプリンシパルが UserInfo ベースのプリンシパルに置き換えられる場合は、SecurityIdentity の簡単なオーグメンテーションが必要になります。
package io.quarkus.it.keycloak;
import java.security.Principal;
import jakarta.enterprise.context.ApplicationScoped;
import io.quarkus.oidc.UserInfo;
import io.quarkus.security.identity.AuthenticationRequestContext;
import io.quarkus.security.identity.SecurityIdentity;
import io.quarkus.security.identity.SecurityIdentityAugmentor;
import io.quarkus.security.runtime.QuarkusSecurityIdentity;
import io.smallrye.mutiny.Uni;
import io.vertx.ext.web.RoutingContext;
@ApplicationScoped
public class CustomSecurityIdentityAugmentor implements SecurityIdentityAugmentor {
@Override
public Uni<SecurityIdentity> augment(SecurityIdentity identity, AuthenticationRequestContext context) {
RoutingContext routingContext = identity.getAttribute(RoutingContext.class.getName());
if (routingContext != null && routingContext.normalizedPath().endsWith("/github")) {
QuarkusSecurityIdentity.Builder builder = QuarkusSecurityIdentity.builder(identity);
UserInfo userInfo = identity.getAttribute("userinfo");
builder.setPrincipal(new Principal() {
@Override
public String getName() {
return userInfo.getString("preferred_username");
}
});
identity = builder.build();
}
return Uni.createFrom().item(identity);
}
}
これで、ユーザーが Google または GitHub を使用してアプリケーションにサインインするときに、次のコードが機能するようになります。
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import io.quarkus.security.Authenticated;
import io.quarkus.security.identity.SecurityIdentity;
@Path("/service")
@Authenticated
public class TokenResource {
@Inject
SecurityIdentity identity;
@GET
@Path("/google")
@Produces("application/json")
public String getUserName() {
return identity.getPrincipal().getName();
}
@GET
@Path("/github")
@Produces("application/json")
public String getUserName() {
return identity.getPrincipal().getUserName();
}
}
おそらく、より簡単な代替案としては、@IdToken JsonWebToken と UserInfo の両方を注入し、IdToken を返すプロバイダーを処理するときに JsonWebToken を使用し、IdToken を返さないプロバイダーでは UserInfo を使用することです。
GitHub OAuth アプリケーション設定に入力するコールバックパスが、GitHub 認証とアプリケーション認可が成功した後にユーザーをリダイレクトするエンドポイントパスと一致していることを確認する必要があります。この場合、http:localhost:8080/github/userinfo に設定する必要があります。
3.2.6. 重要な認証イベントをリッスンする リンクのコピーリンクがクリップボードにコピーされました!
重要な OIDC 認証イベントを監視する @ApplicationScoped Bean を登録できます。ユーザーが初めてログインしたり、再認証したり、セッションを更新したりすると、リスナーが更新されます。今後、さらに多くのイベントが報告される可能性があります。以下に例を示します。
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.event.Observes;
import io.quarkus.oidc.IdTokenCredential;
import io.quarkus.oidc.SecurityEvent;
import io.quarkus.security.identity.AuthenticationRequestContext;
import io.vertx.ext.web.RoutingContext;
@ApplicationScoped
public class SecurityEventListener {
public void event(@Observes SecurityEvent event) {
String tenantId = event.getSecurityIdentity().getAttribute("tenant-id");
RoutingContext vertxContext = event.getSecurityIdentity().getAttribute(RoutingContext.class.getName());
vertxContext.put("listener-message", String.format("event:%s,tenantId:%s", event.getEventType().name(), tenantId));
}
}
セキュリティーのヒントと裏技ガイドの セキュリティーイベントの監視 セクションで説明されているように、他のセキュリティーイベントをリッスンすることができます。
3.2.7. ダウンストリームサービスへのトークンの伝播 リンクのコピーリンクがクリップボードにコピーされました!
ダウンストリームサービスへの認可コードフローアクセストークンの伝播に関する詳細は、トークンの伝播 セクションを参照してください。