OpenID Connect (OIDC) ベアラートークン認証
Quarkus OpenID Connect(OIDC)エクステンションを使用することで、ベアラートークン認証を使って、アプリケーション内のJakarta REST(旧称JAX-RS)エンドポイントへのHTTPアクセスを保護できます。
Quarkusのベアラートークン認証メカニズムの概要
Quarkusは、Quarkus OpenID Connect(OIDC)エクステンションを通じて、ベアラートークン認証メカニズムをサポートしています。
ベアラートークンは、 Keycloak などのOIDCやOAuth2.0に準拠した認可サーバーで発行されます。
ベアラートークン認証は、ベアラートークンの存在と有効性に基づいて HTTP リクエストを認可するプロセスです。 ベアラートークンは、呼び出しの主体に関する情報を提供します。この情報は、HTTP リソースにアクセスできるかどうかを判断するために使用されます。
以下の図に、Quarkusのベアラートークン認証メカニズムの概要を示します:
-
Quarkus サービスが、OIDC プロバイダーから検証キーを取得します。 検証キーは、ベアラーアクセストークンの署名を検証するために使用されます。
-
Quarkus ユーザーが、シングルページアプリケーション (SPA) にアクセスします。
-
シングルページアプリケーションが、認可コードフローを使用してユーザーを認証し、OIDC プロバイダーからトークンを取得します。
-
シングルページアプリケーションが、アクセストークンを使用して、Quarkus サービスからサービスデータを取得します。
-
Quarkus サービスが、検証キーを使用してベアラーアクセストークンの署名を検証し、トークンの有効期限やその他のクレームをチェックします。トークンが有効であればリクエストの続行を許可し、シングルページアプリケーションにサービスレスポンスを返します。
-
シングルページアプリケーションが、Quarkus ユーザーに同じデータを返します。
-
Quarkus サービスが、OIDC プロバイダーから検証キーを取得します。 検証キーは、ベアラーアクセストークンの署名を検証するために使用されます。
-
クライアントが、
client_credentials
かパスワードグラントを使用して、OIDC プロバイダーからアクセストークンを取得します。client_credentials には、クライアント ID とシークレットが必要です。パスワードグラントには、クライアント ID、シークレット、ユーザー名、およびパスワードが必要です。 -
クライアントがアクセストークンを使用して、Quarkus サービスからサービスデータを取得します。
-
Quarkus サービスが、検証キーを使用してベアラーアクセストークンの署名を検証し、トークンの有効期限やその他のクレームをチェックします。トークンが有効であればリクエストの続行を許可し、サービスレスポンスをクライアントに返します。
OIDC 認可コードフローを使用してユーザーを認証および認可する必要がある場合は、Quarkus の Web アプリケーションを保護するための OpenID Connect 認可コードフローメカニズム ガイドを参照してください。 また、Keycloak とベアラートークンを使用する場合は、Quarkus Keycloak を使用した認可の一元化 ガイドを参照してください。
To learn about how you can protect service applications by using OIDC Bearer token authentication, see the following tutorial:
マルチテナントをサポートする方法については、Quarkus OpenID Connect マルチテナンシーの使用 ガイドを参照してください。
JWT クレームへのアクセス
JWT トークンクレームにアクセスする必要がある場合は、JsonWebToken
を注入できます。
package org.acme.security.openid.connect;
import org.eclipse.microprofile.jwt.JsonWebToken;
import jakarta.inject.Inject;
import jakarta.annotation.security.RolesAllowed;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
@Path("/api/admin")
public class AdminResource {
@Inject
JsonWebToken jwt;
@GET
@RolesAllowed("admin")
@Produces(MediaType.TEXT_PLAIN)
public String admin() {
return "Access for subject " + jwt.getSubject() + " is granted";
}
}
JsonWebToken
の注入は、@ApplicationScoped
、@Singleton
、および @RequestScoped
スコープでサポートされています。
ただし、個々のクレームが単純型として挿入される場合は、@RequestScoped
を使用する必要があります。
詳細は、Quarkus の「JWT RBAC の使用」ガイドの サポートされている注入スコープ セクションを参照してください。
UserInfo
OIDC UserInfo
エンドポイントから UserInfo JSON オブジェクトをリクエストする必要がある場合は、quarkus.oidc.authentication.user-info-required=true
を設定します。
OIDC プロバイダーの UserInfo
エンドポイントにリクエストが送信され、io.quarkus.oidc.UserInfo
(単純な javax.json.JsonObject
ラッパー) オブジェクトが作成されます。
io.quarkus.oidc.UserInfo
は、SecurityIdentity
userinfo
属性として注入またはアクセスできます。
quarkus.oidc.authentication.user-info-required
is automatically enabled if one of these conditions is met:
-
if
quarkus.oidc.roles.source
is set touserinfo
orquarkus.oidc.token.verify-access-token-with-user-info
is set totrue
orquarkus.oidc.authentication.id-token-required
is set tofalse
, the current OIDC tenant must support a UserInfo endpoint in these cases. -
if
io.quarkus.oidc.UserInfo
injection point is detected but only if the current OIDC tenant supports a UserInfo endpoint.
設定メタデータ
現在のテナントの検出された OpenID Connect 設定メタデータ は、io.quarkus.oidc.OidcConfigurationMetadata
で表され、SecurityIdentity
configuration-metadata
属性として注入またはアクセスできます。
エンドポイントがパブリックの場合、デフォルトのテナントの OidcConfigurationMetadata
が注入されます。
トークンクレームと SecurityIdentity ロール
SecurityIdentity
ロールは、次のように、検証済みの JWT アクセストークンからマッピングすることができます。
-
quarkus.oidc.roles.role-claim-path
プロパティーが設定されており、一致する配列または文字列のクレームが見つかった場合、そのクレームからロールが抽出されます。 たとえば、customroles
、customroles/array
、scope
、"http://namespace-qualified-custom-claim"/roles
、"http://namespace-qualified-roles"
などです。 -
groups
クレームが利用可能な場合は、その値が使用されます。 -
realm_access/roles
またはresource_access/client_id/roles
(client_id
はquarkus.oidc.client-id
プロパティーの値) クレームが利用可能な場合は、その値が使用されます。 このチェックは、Keycloak が発行するトークンをサポートします。
たとえば、次の JWT トークンには、ロールが含まれている roles
配列を含む複雑な groups
クレームがあります。
{
"iss": "https://server.example.com",
"sub": "24400320",
"upn": "jdoe@example.com",
"preferred_username": "jdoe",
"exp": 1311281970,
"iat": 1311280970,
"groups": {
"roles": [
"microprofile_jwt_user"
],
}
}
microprofile_jwt_user
ロールは、SecurityIdentity
ロールにマップする必要があります。これは、quarkus.oidc.roles.role-claim-path=groups/roles
設定で実行できます。
トークンが不透明 (バイナリー) の場合は、リモートトークンイントロスペクションレスポンスの scope
プロパティーが使用されます。
UserInfo
がロールのソースである場合は、quarkus.oidc.authentication.user-info-required=true
および quarkus.oidc.roles.source=userinfo
を設定し、必要に応じて quarkus.oidc.roles.role-claim-path
を設定します。
さらに、カスタムの SecurityIdentityAugmentor
を使用してロールを追加することもできます。
詳細は、Quarkus の「セキュリティーに関するヒントとコツ」ガイドの セキュリティーアイデンティティーのカスタマイズ セクションを参照してください。
また、HTTP セキュリティーポリシー を使用して、トークンクレームから作成された SecurityIdentity
ロールをデプロイメント固有のロールにマップすることもできます。
トークンのスコープと SecurityIdentity の権限
SecurityIdentity
権限は、io.quarkus.security.StringPermission
の形式で、source of the roles のスコープパラメーターから、同じクレームセパレーターを使用してマッピングされます。
import java.util.List;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import org.eclipse.microprofile.jwt.Claims;
import org.eclipse.microprofile.jwt.JsonWebToken;
import io.quarkus.security.PermissionsAllowed;
@Path("/service")
public class ProtectedResource {
@Inject
JsonWebToken accessToken;
@PermissionsAllowed("email") (1)
@GET
@Path("/email")
public Boolean isUserEmailAddressVerifiedByUser() {
return accessToken.getClaim(Claims.email_verified.name());
}
@PermissionsAllowed("orders_read") (2)
@GET
@Path("/order")
public List<Order> listOrders() {
return List.of(new Order("1"));
}
public static class Order {
String id;
public Order() {
}
public Order(String id) {
this.id = id;
}
public String getId() {
return id;
}
public void setId() {
this.id = id;
}
}
}
1 | OpenID Connect スコープ email を持つリクエストのみにアクセスが許可されます。 |
2 | 読み取りアクセスは、orders_read スコープを持つクライアントリクエストに制限されます。 |
io.quarkus.security.PermissionsAllowed
アノテーションの詳細は、「Web エンドポイントの認可」ガイドの 権限アノテーション セクションを参照してください。
トークンの検証とイントロスペクション
トークンが JWT トークンの場合、デフォルトでは、OIDC プロバイダーの JWK エンドポイントから取得されたローカルの JsonWebKeySet
の JsonWebKey
(JWK) キーを使用してトークンが検証されます。
トークンのキー識別子 (kid
) ヘッダー値は、一致する JWK キーを検出するために使用されます。
一致する JWK
がローカルに存在しない場合は、JWK エンドポイントから現在のキーセットが取得され、JsonWebKeySet
が更新されます。
JsonWebKeySet
の更新は、quarkus.oidc.token.forced-jwk-refresh-interval
の有効期限が切れた後にのみ繰り返すことができます。
デフォルトの有効期限は 10 分です。
一致する JWK
が更新後に利用できない場合は、JWT トークンが OIDC プロバイダーのトークンイントロスペクションエンドポイントに送信されます。
トークンが不透明である場合、つまりバイナリートークンまたは暗号化された JWT トークンである場合、そのトークンは常に OIDC プロバイダーのトークンイントロスペクションエンドポイントに送信されます。
JWT トークンのみを使用しており、一致する JsonWebKey
が常に使用可能であることが予想される場合 (たとえば、キーセットを更新した後)、次の例に示すように、トークンイントロスペクションを無効にする必要があります。
quarkus.oidc.token.allow-jwt-introspection=false
quarkus.oidc.token.allow-opaque-token-introspection=false
場合によっては、JWT トークンをイントロスペクションのみで検証しなければならないことがあります。これは、イントロスペクションエンドポイントアドレスのみを設定することで強制できます。 次のプロパティー設定は、これを Keycloak を使用して実現する方法の例を示しています。
quarkus.oidc.auth-server-url=http://localhost:8180/realms/quarkus
quarkus.oidc.discovery-enabled=false
# Token Introspection endpoint: http://localhost:8180/realms/quarkus/protocol/openid-connect/tokens/introspect
quarkus.oidc.introspection-path=/protocol/openid-connect/tokens/introspect
JWT トークンのイントロスペクションをリモートで実施することを間接的に強制することには、利点と欠点があります。 利点は、2 つのリモート呼び出し (リモート OIDC メタデータディスカバリー呼び出しと、それに続く使用されない検証キーを取得するための別のリモート呼び出し) が不要になることです。 欠点は、ユーザーがイントロスペクションエンドポイントアドレスを確認して、手動で設定する必要があることです。
別の方法としては、デフォルトオプションである OIDC メタデータディスカバリーを許可しながら、リモート JWT イントロスペクションのみを実行するよう要求する方法があります。次の例を参照してください。
quarkus.oidc.auth-server-url=http://localhost:8180/realms/quarkus
quarkus.oidc.token.require-jwt-introspection-only=true
この方法の利点は、設定がよりシンプルで理解しやすいことです。 欠点は、検証キーが取得されない場合でも、イントロスペクションエンドポイントアドレスを検出するためにリモート OIDC メタデータディスカバリー呼び出しが必要になることです。
io.quarkus.oidc.TokenIntrospection
(シンプルな jakarta.json.JsonObject
ラッパーオブジェクト) が作成されます。
このオブジェクトは、JWT または不透明トークンどちらかのイントロスペクションが成功した場合、SecurityIdentity
introspection
属性として注入またはアクセスできます。
トークンのイントロスペクションと UserInfo
キャッシュ
不透明アクセストークンは、すべてリモートでイントロスペクトする必要があります。
場合によっては、JWT アクセストークンをイントロスペクトする必要もあります。
UserInfo
も必要な場合は、OIDC プロバイダーへの後続のリモート呼び出しで同じアクセストークンが使用されます。
したがって、UserInfo
が必要で、現在のアクセストークンが不透明トークンである場合、そのトークンごとに 2 つのリモート呼び出しが行われます。1 つのリモート呼び出しはトークンをイントロスペクトするためのもので、もう 1 つは UserInfo
を取得するためのものです。
トークンが JWT の場合は、トークンをイントロスペクトする必要がなければ、UserInfo
を取得するための 1 つのリモート呼び出しだけで済みます。
場合によっては、着信ベアラーまたはコードフローアクセストークンごとに最大 2 つのリモート呼び出しを行うコストが問題になります。
本番環境の場合は、トークンイントロスペクションと UserInfo
データを短期間 (たとえば 3 分から 5 分) キャッシュすることを検討してください。
quarkus-oidc
は、quarkus.oidc.TokenIntrospectionCache
および quarkus.oidc.UserInfoCache
インターフェイスを提供します。
これらは @ApplicationScoped
キャッシュ実装に使用できます。次の例に示すように、@ApplicationScoped
キャッシュ実装を使用して、quarkus.oidc.TokenIntrospection
オブジェクトや quarkus.oidc.UserInfo
オブジェクトを保存および取得します。
@ApplicationScoped
@Alternative
@Priority(1)
public class CustomIntrospectionUserInfoCache implements TokenIntrospectionCache, UserInfoCache {
...
}
各 OIDC テナントは、quarkus.oidc.TokenIntrospection
データ、quarkus.oidc.UserInfo
データ、またはその両方の保存を許可または拒否できます。これには、ブール値の quarkus.oidc."tenant".allow-token-introspection-cache
プロパティーと quarkus.oidc."tenant".allow-user-info-cache
プロパティーを使用します。
さらに、quarkus-oidc
は、quarkus.oidc.TokenIntrospectionCache
と quarkus.oidc.UserInfoCache
の両方のインターフェイスを実装する、シンプルなデフォルトのメモリーベースのトークンキャッシュを提供します。
デフォルトの OIDC トークンキャッシュは、次のように設定すると、有効にできます。
# 'max-size' is 0 by default, so the cache can be activated by setting 'max-size' to a positive value:
quarkus.oidc.token-cache.max-size=1000
# 'time-to-live' specifies how long a cache entry can be valid for and will be used by a cleanup timer:
quarkus.oidc.token-cache.time-to-live=3M
# 'clean-up-timer-interval' is not set by default, so the cleanup timer can be activated by setting 'clean-up-timer-interval':
quarkus.oidc.token-cache.clean-up-timer-interval=1M
デフォルトのキャッシュは、トークンをキーとして使用します。各エントリーには、TokenIntrospection
、UserInfo
、またはその両方が含まれます。
最大で max-size
個のエントリーのみが保持されます。
新しいエントリーを追加するときにキャッシュがすでにいっぱいになっている場合は、期限切れのエントリーを 1 つ削除して領域を確保する試みが行われます。
さらに、クリーンアップタイマーを有効にすると、期限切れのエントリーが定期的にチェックされ、削除されます。
デフォルトのキャッシュ実装を試すことも、カスタムのキャッシュ実装を登録することもできます。
JSON Web トークンのクレーム検証
ベアラーの JWT トークンの署名が検証され、その expires at
(exp
) クレームがチェックされると、次に iss
(issuer
) クレーム値が検証されます。
デフォルトでは、iss
クレーム値は、既知のプロバイダー設定で検出された issuer
プロパティーと比較されます。
ただし、quarkus.oidc.token.issuer
プロパティーが設定されている場合は、代わりにそのプロパティーと iss
クレーム値が比較されます。
場合によっては、この iss
クレーム検証がうまくいかないことがあります。
たとえば、検出された issuer
プロパティーには内部の HTTP/IP アドレスが含まれているのに、トークン iss
クレーム値には外部の HTTP/IP アドレスが含まれている場合などです。
または、検出された issuer
プロパティーにはテンプレートテナント変数が含まれているのに、トークン iss
クレーム値には完全なテナント固有の発行者の値が含まれている場合などです。
このような場合は、quarkus.oidc.token.issuer=any
を設定して発行者の検証をスキップすることを検討してください。
発行者の検証のスキップは、他の方法を利用できない場合にのみ行ってください。
-
Keycloak を使用していて、ホストアドレスが異なるために発行者検証エラーが発生する場合は、Keycloak に
KEYCLOAK_FRONTEND_URL
プロパティーを設定して、同じホストアドレスを使用してください。 -
マルチテナントデプロイメントで
iss
プロパティーがテナント固有のものである場合は、SecurityIdentity
tenant-id
属性を使用して、エンドポイントまたはカスタム Jakarta フィルターで発行者が正しいことを確認します。 以下に例を示します。
import jakarta.inject.Inject;
import jakarta.ws.rs.container.ContainerRequestContext;
import jakarta.ws.rs.container.ContainerRequestFilter;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.Provider;
import org.eclipse.microprofile.jwt.JsonWebToken;
import io.quarkus.oidc.OidcConfigurationMetadata;
import io.quarkus.security.identity.SecurityIdentity;
@Provider
public class IssuerValidator implements ContainerRequestFilter {
@Inject
OidcConfigurationMetadata configMetadata;
@Inject JsonWebToken jwt;
@Inject SecurityIdentity identity;
public void filter(ContainerRequestContext requestContext) {
String issuer = configMetadata.getIssuer().replace("{tenant-id}", identity.getAttribute("tenant-id"));
if (!issuer.equals(jwt.getIssuer())) {
requestContext.abortWith(Response.status(401).build());
}
}
}
トークンの |
Jose4j Validator
You can register a custom Jose4j Validator to customize the JWT claim verification process, before org.eclipse.microprofile.jwt.JsonWebToken
is initialized.
For example:
package org.acme.security.openid.connect;
import static org.eclipse.microprofile.jwt.Claims.iss;
import io.quarkus.arc.Unremovable;
import jakarta.enterprise.context.ApplicationScoped;
import org.jose4j.jwt.MalformedClaimException;
import org.jose4j.jwt.consumer.JwtContext;
import org.jose4j.jwt.consumer.Validator;
@Unremovable
@ApplicationScoped
public class IssuerValidator implements Validator { (1)
@Override
public String validate(JwtContext jwtContext) throws MalformedClaimException {
if (jwtContext.getJwtClaims().hasClaim(iss.name())
&& "my-issuer".equals(jwtContext.getJwtClaims().getClaimValueAsString(iss.name()))) {
return "wrong issuer"; (2)
}
return null; (3)
}
}
1 | Register Jose4j Validator to verify JWT tokens for all OIDC tenants. |
2 | Return the claim verification error description. |
3 | Return null to confirm that this Validator has successfully verified the token. |
Use a @quarkus.oidc.TenantFeature annotation to bind a custom Validator to a specific OIDC tenant only.
|
シングルページアプリケーション
シングルページアプリケーション (SPA) は、通常、XMLHttpRequest
(XHR) と OIDC プロバイダーが提供する JavaScript ユーティリティーコードを使用してベアラートークンを取得し、Quarkus service
アプリケーションにアクセスします。
たとえば、Keycloak を使用する場合は、keycloak.js
を使用してユーザーを認証し、SPA から期限切れのトークンを更新できます。
<html>
<head>
<title>keycloak-spa</title>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="http://localhost:8180/js/keycloak.js"></script>
<script>
var keycloak = new Keycloak();
keycloak.init({onLoad: 'login-required'}).success(function () {
console.log('User is now authenticated.');
}).error(function () {
window.location.reload();
});
function makeAjaxRequest() {
axios.get("/api/hello", {
headers: {
'Authorization': 'Bearer ' + keycloak.token
}
})
.then( function (response) {
console.log("Response: ", response.status);
}).catch(function (error) {
console.log('refreshing');
keycloak.updateToken(5).then(function () {
console.log('Token refreshed');
}).catch(function () {
console.log('Failed to refresh token');
window.location.reload();
});
});
}
</script>
</head>
<body>
<button onclick="makeAjaxRequest()">Request</button>
</body>
</html>
クロスオリジンリソース共有
別のドメインで実行されているシングルページアプリケーションから OIDC service
アプリケーションを使用する予定の場合は、クロスオリジンリソース共有 (CORS) を設定する必要があります。
詳細は、「クロスオリジンリソース共有」ガイドの CORS フィルター セクションを参照してください。
プロバイダーエンドポイント設定
OIDC service
アプリケーションは、OIDC プロバイダーのトークン、JsonWebKey
(JWK) セット、場合によっては UserInfo
やイントロスペクションのエンドポイントアドレスを確認する必要があります。
デフォルトでは、これらのエンドポイントアドレスは、設定された quarkus.oidc.auth-server-url
に /.well-known/openid-configuration
パスを追加することで検出されます。
ディスカバリーエンドポイントが利用できない場合や、ディスカバリーエンドポイントのラウンドトリップのコストを節約する場合は、検出を無効にして、エンドポイントアドレスを相対パス値で設定することができます。 以下に例を示します。
quarkus.oidc.auth-server-url=http://localhost:8180/realms/quarkus
quarkus.oidc.discovery-enabled=false
# 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/tokens/introspect
quarkus.oidc.introspection-path=/protocol/openid-connect/tokens/introspect
トークンの伝播
For information about bearer access token propagation to the downstream services, see the Token propagation section of the Quarkus "OpenID Connect (OIDC) and OAuth2 client and filters reference" guide.
JWT token certificate chain
In some cases, JWT bearer tokens have an x5c
header which represents an X509 certificate chain whose leaf certificate contains a public key that must be used to verify this token’s signature.
Before this public key can be accepted to verify the signature, the certificate chain must be validated first.
The certificate chain validation involves several steps:
-
Confirm that every certificate but the root one is signed by the parent certificate.
-
Confirm the chain’s root certificate is also imported in the truststore.
-
Validate the chain’s leaf certificate. If a common name of the leaf certificate is configured then a common name of the chain’s leaf certificate must match it. Otherwise the chain’s leaf certificate must also be avaiable in the truststore, unless one or more custom
TokenCertificateValidator
implementations are registered. -
quarkus.oidc.TokenCertificateValidator
can be used to add a custom certificate chain validation step. It can be used by all tenants expecting tokens with the certificate chain or bound to specific OIDC tenants with the@quarkus.oidc.TenantFeature
annotation.
For example, here is how you can configure Quarkus OIDC to verify the token’s certificate chain, without using quarkus.oidc.TokenCertificateValidator
:
quarkus.oidc.certificate-chain.trust-store-file=truststore-rootcert.p12 (1)
quarkus.oidc.certificate-chain.trust-store-password=storepassword
quarkus.oidc.certificate-chain.leaf-certificate-name=www.quarkusio.com (2)
1 | The truststore must contain the certificate chain’s root certificate. |
2 | The certificate chain’s leaf certificate must have a common name equal to www.quarkusio.com . If this property is not configured then the truststore must contain the certificate chain’s leaf certificate unless one or more custom TokenCertificateValidator implementations are registered. |
You can add a custom certificate chain validation step by registering a custom quarkus.oidc.TokenCertificateValidator
, for example:
package io.quarkus.it.keycloak;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.List;
import jakarta.enterprise.context.ApplicationScoped;
import io.quarkus.arc.Unremovable;
import io.quarkus.oidc.OidcTenantConfig;
import io.quarkus.oidc.TokenCertificateValidator;
import io.quarkus.oidc.runtime.TrustStoreUtils;
import io.vertx.core.json.JsonObject;
@ApplicationScoped
@Unremovable
public class BearerGlobalTokenChainValidator implements TokenCertificateValidator {
@Override
public void validate(OidcTenantConfig oidcConfig, List<X509Certificate> chain, String tokenClaims) throws CertificateException {
String rootCertificateThumbprint = TrustStoreUtils.calculateThumprint(chain.get(chain.size() - 1));
JsonObject claims = new JsonObject(tokenClaims);
if (!rootCertificateThumbprint.equals(claims.getString("root-certificate-thumbprint"))) { (1)
throw new CertificateException("Invalid root certificate");
}
}
}
1 | Confirm that the certificate chain’s root certificate is bound to the custom JWT token’s claim. |
OIDC プロバイダーのクライアント認証
quarkus.oidc.runtime.OidcProviderClient
は、OIDC プロバイダーへのリモートリクエストが必要な場合に使用されます。
ベアラートークンのイントロスペクションが必要な場合は、OidcProviderClient
が OIDC プロバイダーに対して認証する必要があります。
サポートされている認証オプションの詳細は、Quarkus の「Web アプリケーションを保護するための OpenID Connect 認可コードフローメカニズム」ガイドの OIDC プロバイダーのクライアント認証 セクションを参照してください。
テスト
Keycloak 認証 を必要とする Quarkus OIDC サービスエンドポイントをテストする必要がある場合は、Keycloak 認証のテスト セクションに従ってください。 |
次の依存関係をテストプロジェクトに追加することで、テストを開始できます。
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
</dependency>
testImplementation("io.rest-assured:rest-assured")
testImplementation("io.quarkus:quarkus-junit5")
WireMock
テストプロジェクトに以下の依存関係を追加します。
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-test-oidc-server</artifactId>
<scope>test</scope>
</dependency>
testImplementation("io.quarkus:quarkus-test-oidc-server")
REST テストエンドポイントを準備し、application.properties
を設定します。
以下に例を示します。
# keycloak.url is set by OidcWiremockTestResource
quarkus.oidc.auth-server-url=${keycloak.url:replaced-by-test-resource}/realms/quarkus/
quarkus.oidc.client-id=quarkus-service-app
quarkus.oidc.application-type=service
最後にテストコードを記述します。 以下に例を示します。
import static org.hamcrest.Matchers.equalTo;
import java.util.Set;
import org.junit.jupiter.api.Test;
import io.quarkus.test.common.QuarkusTestResource;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.oidc.server.OidcWiremockTestResource;
import io.restassured.RestAssured;
import io.smallrye.jwt.build.Jwt;
@QuarkusTest
@QuarkusTestResource(OidcWiremockTestResource.class)
public class BearerTokenAuthorizationTest {
@Test
public void testBearerToken() {
RestAssured.given().auth().oauth2(getAccessToken("alice", Set.of("user")))
.when().get("/api/users/me")
.then()
.statusCode(200)
// The test endpoint returns the name extracted from the injected `SecurityIdentity` principal.
.body("userName", equalTo("alice"));
}
private String getAccessToken(String userName, Set<String> groups) {
return Jwt.preferredUserName(userName)
.groups(groups)
.issuer("https://server.example.com")
.audience("https://service.example.com")
.sign();
}
}
quarkus-test-oidc-server
エクステンションは、JSON Web Key
(JWK
) フォーマットの署名 RSA 秘密鍵ファイルを含んでおり、smallrye.jwt.sign.key.location
設定プロパティーでそのファイルを参照します。
このエクステンションを使用すると、引数なしの sign()
操作を使用してトークンに署名できます。
OidcWiremockTestResource
を使用して quarkus-oidc
service
アプリケーションをテストすると、通信チャネルも WireMock HTTP スタブに対してテストされるため、最大のカバレッジを得ることができます。
OidcWiremockTestResource
でまだサポートされていない WireMock スタブを使用してテストを実行する必要がある場合は、次の例に示すように、テストクラスに WireMockServer
インスタンスを挿入できます。
|
package io.quarkus.it.keycloak;
import static com.github.tomakehurst.wiremock.client.WireMock.matching;
import static org.hamcrest.Matchers.equalTo;
import org.junit.jupiter.api.Test;
import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.client.WireMock;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.oidc.server.OidcWireMock;
import io.restassured.RestAssured;
@QuarkusTest
public class CustomOidcWireMockStubTest {
@OidcWireMock
WireMockServer wireMockServer;
@Test
public void testInvalidBearerToken() {
wireMockServer.stubFor(WireMock.post("/auth/realms/quarkus/protocol/openid-connect/token/introspect")
.withRequestBody(matching(".*token=invalid_token.*"))
.willReturn(WireMock.aResponse().withStatus(400)));
RestAssured.given().auth().oauth2("invalid_token").when()
.get("/api/users/me/bearer")
.then()
.statusCode(401)
.header("WWW-Authenticate", equalTo("Bearer"));
}
}
OidcTestClient
SaaS OIDC プロバイダー (Auth0
など) を使用していて、テスト (開発) ドメインに対してテストを実行したり、リモート Keycloak テストレルムに対してテストを実行したりする場合、quarkus.oidc.auth-server-url
がすでに設定されていれば、OidcTestClient
を使用できます。
たとえば、次のような設定があるとします。
%test.quarkus.oidc.auth-server-url=https://dev-123456.eu.auth0.com/
%test.quarkus.oidc.client-id=test-auth0-client
%test.quarkus.oidc.credentials.secret=secret
To start, add the same dependency, quarkus-test-oidc-server
, as described in the WireMock section.
次に、以下のようにテストコードを記述します。
package org.acme;
import org.junit.jupiter.api.AfterAll;
import static io.restassured.RestAssured.given;
import static org.hamcrest.CoreMatchers.is;
import java.util.Map;
import org.junit.jupiter.api.Test;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.oidc.client.OidcTestClient;
@QuarkusTest
public class GreetingResourceTest {
static OidcTestClient oidcTestClient = new OidcTestClient();
@AfterAll
public static void close() {
oidcTestClient.close();
}
@Test
public void testHelloEndpoint() {
given()
.auth().oauth2(getAccessToken("alice", "alice"))
.when().get("/hello")
.then()
.statusCode(200)
.body(is("Hello, Alice"));
}
private String getAccessToken(String name, String secret) {
return oidcTestClient.getAccessToken(name, secret,
Map.of("audience", "https://dev-123456.eu.auth0.com/api/v2/",
"scope", "profile"));
}
}
このテストコードは、クライアント ID が test-auth0-client
のアプリケーションを登録し、パスワードが alice
のユーザー alice
を作成したテスト Auth0
ドメインから password
グラントを使用してトークンを取得します。
このようなテストを機能させるには、テスト Auth0
アプリケーションで password
グラントが有効になっている必要があります。
このサンプルコードでは、追加のパラメーターを渡す方法も示しています。Auth0
の場合、これらは audience
および scope
パラメーターです。
Dev Services for Keycloak
Keycloak に対する結合テストには、Dev Services for Keycloak を使用することを推奨します。
Dev Services for Keycloak
は、テストコンテナーを起動して初期化します。次に、quarkus
レルムと quarkus-app
クライアント (secret
シークレット) を作成し、alice
(admin
および user
ロール) および bob
(user
ロール) ユーザーを追加します。これらのプロパティーはすべてカスタマイズできます。
まず、次の依存関係を追加します。この依存関係は、アクセストークンを取得するテストで使用できるユーティリティークラス io.quarkus.test.keycloak.client.KeycloakTestClient
を提供します。
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-test-keycloak-server</artifactId>
<scope>test</scope>
</dependency>
testImplementation("io.quarkus:quarkus-test-keycloak-server")
次に、application.properties
設定ファイルを準備します。
空の application.properties
ファイルから開始できます。Dev Services for Keycloak
が quarkus.oidc.auth-server-url
を登録し、その参照先として実行中のテストコンテナー、quarkus.oidc.client-id=quarkus-app
、および quarkus.oidc.credentials.secret=secret
を設定するためです。
ただし、必要な quarkus-oidc
プロパティーをすでに設定している場合は、次の例に示すように、quarkus.oidc.auth-server-url
を Dev Services for Keycloak
の prod
プロファイルに関連付けるだけで、コンテナーを起動できます。
%prod.quarkus.oidc.auth-server-url=http://localhost:8180/realms/quarkus
テストを実行する前にカスタムレルムファイルを Keycloak にインポートする必要がある場合は、次のように Dev Services for Keycloak
を設定します。
%prod.quarkus.oidc.auth-server-url=http://localhost:8180/realms/quarkus
quarkus.keycloak.devservices.realm-path=quarkus-realm.json
最後に、次の例に示すようにテストを記述します。このテストは JVM モードで実行されます。
package org.acme.security.openid.connect;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.keycloak.client.KeycloakTestClient;
import io.restassured.RestAssured;
import org.junit.jupiter.api.Test;
@QuarkusTest
public class BearerTokenAuthenticationTest {
KeycloakTestClient keycloakClient = new KeycloakTestClient();
@Test
public void testAdminAccess() {
RestAssured.given().auth().oauth2(getAccessToken("alice"))
.when().get("/api/admin")
.then()
.statusCode(200);
RestAssured.given().auth().oauth2(getAccessToken("bob"))
.when().get("/api/admin")
.then()
.statusCode(403);
}
protected String getAccessToken(String userName) {
return keycloakClient.getAccessToken(userName);
}
}
package org.acme.security.openid.connect;
import io.quarkus.test.junit.QuarkusIntegrationTest;
@QuarkusIntegrationTest
public class NativeBearerTokenAuthenticationIT extends BearerTokenAuthenticationTest {
}
Dev Services for Keycloak の初期化と設定の詳細は、Dev Services for Keycloak ガイドを参照してください。
KeycloakTestResourceLifecycleManager
Keycloak との結合テストには KeycloakTestResourceLifecycleManager
を使用することもできます。
Use Dev Services for Keycloak instead of |
まず、以下の依存関係を追加します:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-test-keycloak-server</artifactId>
<scope>test</scope>
</dependency>
testImplementation("io.quarkus:quarkus-test-keycloak-server")
これは、Keycloak コンテナーを起動する io.quarkus.test.common.QuarkusTestResourceLifecycleManager
の実装である io.quarkus.test.keycloak.server.KeycloakTestResourceLifecycleManager
を提供します。
Maven Surefire プラグインを次のように設定します。または、ネイティブイメージテストの maven.failsafe.plugin
と同様に設定します。
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<systemPropertyVariables>
<!-- Or, alternatively, configure 'keycloak.version' -->
<keycloak.docker.image>${keycloak.docker.image}</keycloak.docker.image>
<!--
Disable HTTPS if required:
<keycloak.use.https>false</keycloak.use.https>
-->
</systemPropertyVariables>
</configuration>
</plugin>
REST テストエンドポイントを準備し、次の例に示すように application.properties
を設定します。
# keycloak.url is set by KeycloakTestResourceLifecycleManager
quarkus.oidc.auth-server-url=${keycloak.url:replaced-by-test-resource}/realms/quarkus/
quarkus.oidc.client-id=quarkus-service-app
quarkus.oidc.credentials=secret
quarkus.oidc.application-type=service
最後にテストコードを記述します。 以下に例を示します。
import static io.quarkus.test.keycloak.server.KeycloakTestResourceLifecycleManager.getAccessToken;
import static org.hamcrest.Matchers.equalTo;
import org.junit.jupiter.api.Test;
import io.quarkus.test.common.QuarkusTestResource;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.keycloak.server.KeycloakTestResourceLifecycleManager;
import io.restassured.RestAssured;
@QuarkusTest
@QuarkusTestResource(KeycloakTestResourceLifecycleManager.class)
public class BearerTokenAuthorizationTest {
@Test
public void testBearerToken() {
RestAssured.given().auth().oauth2(getAccessToken("alice"))
.when().get("/api/users/preferredUserName")
.then()
.statusCode(200)
// The test endpoint returns the name extracted from the injected SecurityIdentity Principal
.body("userName", equalTo("alice"));
}
}
上記の例では、KeycloakTestResourceLifecycleManager
が alice
と admin
の 2 人のユーザーを登録します。
デフォルトでは、* ユーザー alice
には user
ロールがあります。このロールは、keycloak.token.user-roles
システムプロパティーを使用してカスタマイズできます。
* ユーザー admin
には user
と admin
の両方のロールがあります。これらのロールは、keycloak.token.admin-roles
システムプロパティーを使用してカスタマイズできます。
デフォルトでは、KeycloakTestResourceLifecycleManager
は HTTPS を使用して Keycloak インスタンスを初期化します。これは keycloak.use.https=false
を使用して無効にできます。
デフォルトのレルム名は quarkus
で、クライアント ID は quarkus-service-app
です。
これらの値をカスタマイズする場合は、keycloak.realm
および keycloak.service.client
システムプロパティーを設定します。
ローカル公開鍵
次の例に示すように、quarkus-oidc
service
アプリケーションをテストするために、ローカルのインライン公開鍵を使用できます。
quarkus.oidc.client-id=test
quarkus.oidc.public-key=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlivFI8qB4D0y2jy0CfEqFyy46R0o7S8TKpsx5xbHKoU1VWg6QkQm+ntyIv1p4kE1sPEQO73+HY8+Bzs75XwRTYL1BmR1w8J5hmjVWjc6R2BTBGAYRPFRhor3kpM6ni2SPmNNhurEAHw7TaqszP5eUF/F9+KEBWkwVta+PZ37bwqSE4sCb1soZFrVz/UT/LF4tYpuVYt3YbqToZ3pZOZ9AX2o1GCG3xwOjkc4x0W7ezbQZdC9iftPxVHR8irOijJRRjcPDtA6vPKpzLl6CyYnsIYPd99ltwxTHjr3npfv/3Lw50bAkbT4HeLFxTx4flEoZLKO/g0bAoV2uqBhkA9xnQIDAQAB
smallrye.jwt.sign.key.location=/privateKey.pem
To generate JWT tokens, copy privateKey.pem
from the integration-tests/oidc-tenancy
in the main
Quarkus repository and use a test code similar to the one in the preceding WireMock section.
You can use your own test keys, if preferred.
この方法では、WireMock 方法と比較してカバレッジが制限されます。 たとえば、リモート通信コードはカバーされません。
TestSecurity アノテーション
@TestSecurity
および @OidcSecurity
アノテーションを使用して、service
アプリケーションエンドポイントのコードをテストできます。このコードは、次の注入のいずれか 1 つまたは 3 つすべてに依存します。
-
JsonWebToken
-
UserInfo
-
OidcConfigurationMetadata
まず、以下の依存関係を追加します:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-test-security-oidc</artifactId>
<scope>test</scope>
</dependency>
testImplementation("io.quarkus:quarkus-test-security-oidc")
次の例に示すようにテストコードを記述します。
import static org.hamcrest.Matchers.is;
import org.junit.jupiter.api.Test;
import io.quarkus.test.common.http.TestHTTPEndpoint;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.security.TestSecurity;
import io.quarkus.test.security.oidc.Claim;
import io.quarkus.test.security.oidc.ConfigMetadata;
import io.quarkus.test.security.oidc.OidcSecurity;
import io.quarkus.test.security.oidc.UserInfo;
import io.restassured.RestAssured;
@QuarkusTest
@TestHTTPEndpoint(ProtectedResource.class)
public class TestSecurityAuthTest {
@Test
@TestSecurity(user = "userOidc", roles = "viewer")
public void testOidc() {
RestAssured.when().get("test-security-oidc").then()
.body(is("userOidc:viewer"));
}
@Test
@TestSecurity(user = "userOidc", roles = "viewer")
@OidcSecurity(claims = {
@Claim(key = "email", value = "user@gmail.com")
}, userinfo = {
@UserInfo(key = "sub", value = "subject")
}, config = {
@ConfigMetadata(key = "issuer", value = "issuer")
})
public void testOidcWithClaimsUserInfoAndMetadata() {
RestAssured.when().get("test-security-oidc-claims-userinfo-metadata").then()
.body(is("userOidc:viewer:user@gmail.com:subject:issuer"));
}
}
このコード例で使用されている ProtectedResource
クラスは次のようになります。
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import io.quarkus.oidc.OidcConfigurationMetadata;
import io.quarkus.oidc.UserInfo;
import io.quarkus.security.Authenticated;
import org.eclipse.microprofile.jwt.JsonWebToken;
@Path("/service")
@Authenticated
public class ProtectedResource {
@Inject
JsonWebToken accessToken;
@Inject
UserInfo userInfo;
@Inject
OidcConfigurationMetadata configMetadata;
@GET
@Path("test-security-oidc")
public String testSecurityOidc() {
return accessToken.getName() + ":" + accessToken.getGroups().iterator().next();
}
@GET
@Path("test-security-oidc-claims-userinfo-metadata")
public String testSecurityOidcWithClaimsUserInfoMetadata() {
return accessToken.getName() + ":" + accessToken.getGroups().iterator().next()
+ ":" + accessToken.getClaim("email")
+ ":" + userInfo.getString("sub")
+ ":" + configMetadata.get("issuer");
}
}
@TestSecurity
アノテーションは常に使用する必要があります。
その user
プロパティーは JsonWebToken.getName()
として返され、その roles
プロパティーは JsonWebToken.getGroups()
として返されます。
@OidcSecurity
アノテーションは任意です。これを使用すると、追加のトークンクレームと UserInfo
および OidcConfigurationMetadata
プロパティーを設定できます。
さらに、quarkus.oidc.token.issuer
プロパティーが設定されている場合、OidcConfigurationMetadata
issuer
プロパティー値として使用されます。
不透明トークンを使用する場合は、次のコード例に示す方法でトークンをテストできます。
import static org.hamcrest.Matchers.is;
import org.junit.jupiter.api.Test;
import io.quarkus.test.common.http.TestHTTPEndpoint;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.security.TestSecurity;
import io.quarkus.test.security.oidc.OidcSecurity;
import io.quarkus.test.security.oidc.TokenIntrospection;
import io.restassured.RestAssured;
@QuarkusTest
@TestHTTPEndpoint(ProtectedResource.class)
public class TestSecurityAuthTest {
@Test
@TestSecurity(user = "userOidc", roles = "viewer")
@OidcSecurity(introspectionRequired = true,
introspection = {
@TokenIntrospection(key = "email", value = "user@gmail.com")
}
)
public void testOidcWithClaimsUserInfoAndMetadata() {
RestAssured.when().get("test-security-oidc-opaque-token").then()
.body(is("userOidc:viewer:userOidc:viewer:user@gmail.com"));
}
}
このコード例で使用されている ProtectedResource
クラスは次のようになります。
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import io.quarkus.oidc.TokenIntrospection;
import io.quarkus.security.Authenticated;
import io.quarkus.security.identity.SecurityIdentity;
@Path("/service")
@Authenticated
public class ProtectedResource {
@Inject
SecurityIdentity securityIdentity;
@Inject
TokenIntrospection introspection;
@GET
@Path("test-security-oidc-opaque-token")
public String testSecurityOidcOpaqueToken() {
return securityIdentity.getPrincipal().getName() + ":" + securityIdentity.getRoles().iterator().next()
+ ":" + introspection.getString("username")
+ ":" + introspection.getString("scope")
+ ":" + introspection.getString("email");
}
}
@TestSecurity
、user
、および roles
属性は、TokenIntrospection
、username
、および scope
プロパティーとして使用できます。
io.quarkus.test.security.oidc.TokenIntrospection
を使用して、email
などの追加のイントロスペクションレスポンスプロパティーを追加します。
これは、複数のテスト方法で同じセキュリティー設定セットを使用する必要がある場合に特に便利です。 |
ログでのエラーの確認
トークン検証エラーの詳細を確認するには、io.quarkus.oidc.runtime.OidcProvider
および TRACE
レベルのロギングを有効にします。
quarkus.log.category."io.quarkus.oidc.runtime.OidcProvider".level=TRACE
quarkus.log.category."io.quarkus.oidc.runtime.OidcProvider".min-level=TRACE
OidcProvider
クライアント初期化エラーの詳細を確認するには、次のように io.quarkus.oidc.runtime.OidcRecorder
および TRACE
レベルのロギングを有効にします。
quarkus.log.category."io.quarkus.oidc.runtime.OidcRecorder".level=TRACE
quarkus.log.category."io.quarkus.oidc.runtime.OidcRecorder".min-level=TRACE
OIDC プロバイダーへの外部および内部アクセス
OIDC プロバイダーおよびその他のエンドポイントの外部からアクセス可能なトークンは、自動検出された URL や quarkus.oidc.auth-server-url
内部 URL を基準に設定された URL とは異なる HTTP(S) URL を持っている可能性があります。
たとえば、SPA が外部トークンエンドポイントアドレスからトークンを取得し、それをベアラートークンとして Quarkus に送信するとします。
その場合、エンドポイントから発行者の検証の失敗が報告される可能性があります。
このような場合に Keycloak を使用する場合は、KEYCLOAK_FRONTEND_URL
システムプロパティーを外部からアクセス可能なベース URL に設定して Keycloak を起動してください。
他の OIDC プロバイダーを使用する場合は、プロバイダーのドキュメントを参照してください。
client-id
プロパティーの使用
quarkus.oidc.client-id
プロパティーは、現在のベアラートークンをリクエストした OIDC クライアントを特定します。
OIDC クライアントは、ブラウザーで実行される SPA アプリケーション、または Quarkus service
アプリケーションにアクセストークンを伝播する Quarkus web-app
コンフィデンシャルクライアントアプリケーションです。
このプロパティーは、service
アプリケーションがトークンをリモートでイントロスペクトすることが予想される場合に必要です。これは、不透明トークンの場合は常に当てはまります。
このプロパティーは、ローカル JSON Web Token (JWT) 検証の場合にのみ省略可能です。
エンドポイントがリモートイントロスペクションエンドポイントへのアクセスを必要としない場合でも、quarkus.oidc.client-id
プロパティーを設定することを推奨します。
これは、client-id
が設定されている場合、それを使用してトークンオーディエンスを検証できるためです。
また、トークンの検証が失敗した場合にも client-id がログに含まれるため、特定のクライアントに発行されたトークンのトレーサビリティーが向上し、より長い期間にわたる分析が可能になります。
たとえば、OIDC プロバイダーでトークンオーディエンスを設定する場合は、次の設定パターンを検討してください。
# Set client-id
quarkus.oidc.client-id=quarkus-app
# Token audience claim must contain 'quarkus-app'
quarkus.oidc.token.audience=${quarkus.oidc.client-id}
quarkus.oidc.client-id
を設定したが、いずれかの OIDC プロバイダーエンドポイントへのリモートアクセスがエンドポイントに必要ない場合 (イントロスペクション、トークンの取得など)、quarkus.oidc.credentials
または同様のプロパティーを使用してクライアントシークレットを設定しないでください。その場合、クライアントシークレットは使用されないためです。
Quarkus |
HTTP リクエストが完了した後の認証
場合によっては、アクティブな HTTP リクエストコンテキストが存在しない場合に、特定のトークンの SecurityIdentity
を作成する必要があります。
quarkus-oidc
エクステンションは、トークンを SecurityIdentity
インスタンスに変換するための io.quarkus.oidc.TenantIdentityProvider
を提供します。
たとえば、HTTP リクエストが完了した後にトークンを検証する必要がある状況の 1 つとして、Vert.x イベントバス を使用してメッセージを処理する場合があります。
以下の例では、さまざまな CDI リクエストコンテキスト内で 'product-order' メッセージを使用しています。
したがって、注入された SecurityIdentity
は、検証されたアイデンティティーを正しく表さず、匿名になります。
package org.acme.quickstart.oidc;
import static jakarta.ws.rs.core.HttpHeaders.AUTHORIZATION;
import jakarta.inject.Inject;
import jakarta.ws.rs.HeaderParam;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import io.vertx.core.eventbus.EventBus;
@Path("order")
public class OrderResource {
@Inject
EventBus eventBus;
@POST
public void order(String product, @HeaderParam(AUTHORIZATION) String bearer) {
String rawToken = bearer.substring("Bearer ".length()); (1)
eventBus.publish("product-order", new Product(product, rawToken));
}
public static class Product {
public String product;
public String customerAccessToken;
public Product() {
}
public Product(String product, String customerAccessToken) {
this.product = product;
this.customerAccessToken = customerAccessToken;
}
}
}
1 | この時点では、プロアクティブ認証が無効な場合、トークンは検証されません。 |
package org.acme.quickstart.oidc;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import io.quarkus.oidc.AccessTokenCredential;
import io.quarkus.oidc.Tenant;
import io.quarkus.oidc.TenantIdentityProvider;
import io.quarkus.security.identity.SecurityIdentity;
import io.quarkus.vertx.ConsumeEvent;
import io.smallrye.common.annotation.Blocking;
@ApplicationScoped
public class OrderService {
@Tenant("tenantId")
@Inject
TenantIdentityProvider identityProvider;
@Inject
TenantIdentityProvider defaultIdentityProvider; (1)
@Blocking
@ConsumeEvent("product-order")
void processOrder(OrderResource.Product product) {
AccessTokenCredential tokenCredential = new AccessTokenCredential(product.customerAccessToken);
SecurityIdentity securityIdentity = identityProvider.authenticate(tokenCredential).await().indefinitely(); (2)
...
}
}
1 | For the default tenant, the Tenant qualifier is optional. |
2 | トークンの検証を実行し、トークンを SecurityIdentity に変換します。 |
When the provider is used during an HTTP request, the tenant configuration can be resolved as described in
the Using OpenID Connect Multi-Tenancy guide.
However, when there is no active HTTP request, you must select the tenant explicitly with the |
動的テナント設定解決 は現在サポートされていません。 動的テナントを必要とする認証は失敗します。 |
OIDC リクエストフィルター
You can filter OIDC requests made by Quarkus to the OIDC provider by registering one or more OidcRequestFilter
implementations, which can update or add new request headers, and log requests.
For more information, see OIDC request filters.