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()); 1 // Metadata URI is absolute, request URI value is relative if (metadata.getTokenUri().endsWith(request.uri())) { 2 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) 1
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. ダウンストリームサービスへのトークンの伝播
ダウンストリームサービスへの認可コードフローアクセストークンの伝播に関する詳細は、トークンの伝播 セクションを参照してください。