diff --git a/docs/src/main/asciidoc/security-oidc-bearer-token-authentication.adoc b/docs/src/main/asciidoc/security-oidc-bearer-token-authentication.adoc index a636c7dc392d87..16261ebf9deb56 100644 --- a/docs/src/main/asciidoc/security-oidc-bearer-token-authentication.adoc +++ b/docs/src/main/asciidoc/security-oidc-bearer-token-authentication.adoc @@ -1110,6 +1110,12 @@ xref:security-openid-connect-multitenancy.adoc#tenant-config-resolver[Dynamic te Authentication that requires dynamic tenant will fail. ==== +[[oidc-request-filters]] +== OIDC request filters + +You can filter OIDC requests made by Quarkus to the OIDC provider by registering one or more `OidcRequestFiler` implementations, which can update or add new request headers, as well as log requests. +For more information, see xref:security-code-flow-authentication#oidc-request-filters[OIDC request filters]. + == References * xref:security-oidc-configuration-properties-reference.adoc[OIDC configuration properties] diff --git a/docs/src/main/asciidoc/security-oidc-code-flow-authentication.adoc b/docs/src/main/asciidoc/security-oidc-code-flow-authentication.adoc index 96bafe7ccd4712..161420b377b36d 100644 --- a/docs/src/main/asciidoc/security-oidc-code-flow-authentication.adoc +++ b/docs/src/main/asciidoc/security-oidc-code-flow-authentication.adoc @@ -279,11 +279,77 @@ quarkus.oidc.introspection-credentials.name=introspection-user-name quarkus.oidc.introspection-credentials.secret=introspection-user-secret ---- -[[oidc-client-filters]] -==== OIDC request customization +[[oidc-request-filters]] +==== OIDC request filters -You can customize OIDC requests made by Quarkus to the OIDC provider by registering one or more `OidcRequestFiler` implementations, which can update or add new request headers. -For more information, see xref:security-openid-connect-client-reference#oidc-client-filters[Client request customization]. +You can filter OIDC requests made by Quarkus to the OIDC provider by registering one or more `OidcRequestFiler` implementations, which can update or add new request headers, as well as log requests. + +For example: + +[source,java] +---- +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 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 + } +} +---- +<1> Get `OidcConfigurationMetadata` which contains all supported OIDC endpoint addresses. +<2> Use `OidcConfigurationMetadata` to filter requests to the OIDC token endpoint only. + +Alternatively, you can use `OidcRequestFilter.Endpoint` enum to make sure this filter is applied to the token endpoint requests only: + +[source,java] +---- +package io.quarkus.it.keycloak; + +import jakarta.enterprise.context.ApplicationScoped; + +import io.quarkus.arc.Unremovable; +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 request, Buffer buffer, OidcRequestContextProperties contextProps) { + 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 + } + + @Override + public Endpoint endpoint() { + return Endpoint.TOKEN; <1> + } +} +---- +<1> Restrict this filter to requests targeting the OIDC token endpoint only. ==== Redirecting to and from the OIDC provider diff --git a/docs/src/main/asciidoc/security-openid-connect-client-reference.adoc b/docs/src/main/asciidoc/security-openid-connect-client-reference.adoc index 692575db26ef7f..c547625a21a677 100644 --- a/docs/src/main/asciidoc/security-openid-connect-client-reference.adoc +++ b/docs/src/main/asciidoc/security-openid-connect-client-reference.adoc @@ -872,10 +872,10 @@ quarkus.log.category."io.quarkus.oidc.client.runtime.OidcClientRecorder".level=T quarkus.log.category."io.quarkus.oidc.client.runtime.OidcClientRecorder".min-level=TRACE ---- -[[oidc-client-filters]] -== OIDC request customization +[[oidc-request-filters]] +== OIDC request filters -You can customize OIDC requests made by Quarkus to the OIDC provider by registering one or more `OidcRequestFiler` implementations which can update or add new request headers, for example, a filter can analyze the request body and add its digest as a new header value: +You can filter OIDC requests made by Quarkus to the OIDC provider by registering one or more `OidcRequestFiler` implementations which can update or add new request headers, for example, a filter can analyze the request body and add its digest as a new header value: [source,java] ---- diff --git a/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/OidcClientImpl.java b/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/OidcClientImpl.java index 0f0252a3a003e6..f5938bfc0e75b2 100644 --- a/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/OidcClientImpl.java +++ b/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/OidcClientImpl.java @@ -17,7 +17,9 @@ import io.quarkus.oidc.client.OidcClientConfig; import io.quarkus.oidc.client.OidcClientException; import io.quarkus.oidc.client.Tokens; +import io.quarkus.oidc.common.OidcRequestContextProperties; import io.quarkus.oidc.common.OidcRequestFilter; +import io.quarkus.oidc.common.OidcRequestFilter.Endpoint; import io.quarkus.oidc.common.runtime.OidcCommonUtils; import io.quarkus.oidc.common.runtime.OidcConstants; import io.smallrye.mutiny.Uni; @@ -71,7 +73,7 @@ public Uni getTokens(Map additionalGrantParameters) { throw new OidcClientException( "Only 'refresh_token' grant is supported, please call OidcClient#refreshTokens method instead"); } - return getJsonResponse(tokenGrantParams, additionalGrantParameters, false); + return getJsonResponse(Endpoint.TOKEN, tokenGrantParams, additionalGrantParameters, false); } @Override @@ -82,7 +84,7 @@ public Uni refreshTokens(String refreshToken, Map additi } MultiMap refreshGrantParams = copyMultiMap(commonRefreshGrantParams); refreshGrantParams.add(OidcConstants.REFRESH_TOKEN_VALUE, refreshToken); - return getJsonResponse(refreshGrantParams, additionalGrantParameters, true); + return getJsonResponse(Endpoint.TOKEN, refreshGrantParams, additionalGrantParameters, true); } @Override @@ -94,7 +96,8 @@ public Uni revokeAccessToken(String accessToken, Map ad if (tokenRevokeUri != null) { MultiMap tokenRevokeParams = new MultiMap(io.vertx.core.MultiMap.caseInsensitiveMultiMap()); tokenRevokeParams.set(OidcConstants.REVOCATION_TOKEN, accessToken); - return postRequest(client.postAbs(tokenRevokeUri), tokenRevokeParams, additionalParameters, false) + return postRequest(Endpoint.TOKEN_REVOCATION, client.postAbs(tokenRevokeUri), tokenRevokeParams, + additionalParameters, false) .transform(resp -> toRevokeResponse(resp)); } else { LOG.debugf("%s OidcClient can not revoke the access token because the revocation endpoint URL is not set"); @@ -111,20 +114,21 @@ private Boolean toRevokeResponse(HttpResponse resp) { return resp.statusCode() == 503 ? false : true; } - private Uni getJsonResponse(MultiMap formBody, Map additionalGrantParameters, boolean refresh) { + private Uni getJsonResponse(Endpoint endpoint, MultiMap formBody, Map additionalGrantParameters, + boolean refresh) { //Uni needs to be lazy by default, we don't send the request unless //something has subscribed to it. This is important for the CAS state //management in TokensHelper return Uni.createFrom().deferred(new Supplier>() { @Override public Uni get() { - return postRequest(client.postAbs(tokenRequestUri), formBody, additionalGrantParameters, refresh) + return postRequest(endpoint, client.postAbs(tokenRequestUri), formBody, additionalGrantParameters, refresh) .transform(resp -> emitGrantTokens(resp, refresh)); } }); } - private UniOnItem> postRequest(HttpRequest request, MultiMap formBody, + private UniOnItem> postRequest(Endpoint endpoint, HttpRequest request, MultiMap formBody, Map additionalGrantParameters, boolean refresh) { MultiMap body = formBody; @@ -165,7 +169,7 @@ private UniOnItem> postRequest(HttpRequest request, } // Retry up to three times with a one-second delay between the retries if the connection is closed Buffer buffer = OidcCommonUtils.encodeForm(body); - Uni> response = filter(request, buffer).sendBuffer(buffer) + Uni> response = filter(endpoint, request, buffer).sendBuffer(buffer) .onFailure(ConnectException.class) .retry() .atMost(oidcConfig.connectionRetryCount) @@ -259,9 +263,15 @@ private void checkClosed() { } } - private HttpRequest filter(HttpRequest request, Buffer body) { - for (OidcRequestFilter filter : filters) { - filter.filter(request, body, null); + private HttpRequest filter(Endpoint endpoint, HttpRequest request, Buffer body) { + if (!filters.isEmpty()) { + OidcRequestContextProperties props = new OidcRequestContextProperties(); + for (OidcRequestFilter filter : filters) { + Endpoint filterEndpoint = filter.endpoint(); + if (Endpoint.ALL == filterEndpoint || endpoint == filterEndpoint) { + filter.filter(request, body, props); + } + } } return request; } diff --git a/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/OidcRequestContextProperties.java b/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/OidcRequestContextProperties.java index d7a1f620a48af7..e5dee80db7fe3e 100644 --- a/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/OidcRequestContextProperties.java +++ b/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/OidcRequestContextProperties.java @@ -1,20 +1,28 @@ package io.quarkus.oidc.common; +import java.util.Collections; import java.util.Map; public class OidcRequestContextProperties { public static String TOKEN = "token"; public static String TOKEN_CREDENTIAL = "token_credential"; + public static String DISCOVERY_ENDPOINT = "discovery_endpoint"; private final Map properties; + public OidcRequestContextProperties() { + this(Map.of()); + } + public OidcRequestContextProperties(Map properties) { this.properties = properties; } - public Object get(String name) { - return properties.get(name); + public T get(String name) { + @SuppressWarnings("unchecked") + T value = (T) properties.get(name); + return value; } public String getString(String name) { @@ -25,4 +33,8 @@ public T get(String name, Class type) { return type.cast(get(name)); } + public Map getAll() { + return Collections.unmodifiableMap(properties); + } + } diff --git a/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/OidcRequestFilter.java b/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/OidcRequestFilter.java index 7318f34eff3b17..165f927263d802 100644 --- a/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/OidcRequestFilter.java +++ b/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/OidcRequestFilter.java @@ -8,12 +8,49 @@ * which are made from the OIDC adapter to the OIDC provider */ public interface OidcRequestFilter { + + enum Endpoint { + ALL, + + /** + * Applies to OIDC discovery requests + */ + DISCOVERY, + + /** + * Applies to OIDC token endpoint requests + */ + TOKEN, + + /** + * Applies to OIDC token revocation endpoint requests + */ + TOKEN_REVOCATION, + + /** + * Applies to OIDC token introspection requests + */ + INTROSPECTION, + /** + * Applies to OIDC JSON Web Key Set endpoint requests + */ + JWKS, + /** + * Applies to OIDC UserInfo endpoint requests + */ + USERINFO + } + /** * Filter OIDC requests * * @param request HTTP request that can have its headers customized * @param body request body, will be null for HTTP GET methods, may be null for other HTTP methods - * @param contextProperties context properties that can be available in context of some requests, can be null + * @param contextProperties context properties that can be available in context of some requests */ void filter(HttpRequest request, Buffer requestBody, OidcRequestContextProperties contextProperties); + + default Endpoint endpoint() { + return Endpoint.ALL; + } } diff --git a/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/runtime/OidcCommonUtils.java b/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/runtime/OidcCommonUtils.java index 3b0e9dbdc8f768..cc813506e60a2e 100644 --- a/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/runtime/OidcCommonUtils.java +++ b/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/runtime/OidcCommonUtils.java @@ -30,7 +30,9 @@ import io.quarkus.arc.ArcContainer; import io.quarkus.credentials.CredentialsProvider; import io.quarkus.credentials.runtime.CredentialsProviderFinder; +import io.quarkus.oidc.common.OidcRequestContextProperties; import io.quarkus.oidc.common.OidcRequestFilter; +import io.quarkus.oidc.common.OidcRequestFilter.Endpoint; import io.quarkus.oidc.common.runtime.OidcCommonConfig.Credentials; import io.quarkus.oidc.common.runtime.OidcCommonConfig.Credentials.Provider; import io.quarkus.oidc.common.runtime.OidcCommonConfig.Credentials.Secret; @@ -429,10 +431,17 @@ public static Predicate oidcEndpointNotAvailable() { public static Uni discoverMetadata(WebClient client, List filters, String authServerUrl, long connectionDelayInMillisecs) { - final String discoveryUrl = authServerUrl + OidcConstants.WELL_KNOWN_CONFIGURATION; + final String discoveryUrl = getDiscoveryUri(authServerUrl); HttpRequest request = client.getAbs(discoveryUrl); - for (OidcRequestFilter filter : filters) { - filter.filter(request, null, null); + if (!filters.isEmpty()) { + OidcRequestContextProperties requestProps = new OidcRequestContextProperties( + Map.of(OidcRequestContextProperties.DISCOVERY_ENDPOINT, discoveryUrl)); + for (OidcRequestFilter filter : filters) { + Endpoint filterEndpoint = filter.endpoint(); + if (filterEndpoint == Endpoint.ALL || filterEndpoint == Endpoint.DISCOVERY) { + filter.filter(request, null, requestProps); + } + } } return request.send().onItem().transform(resp -> { if (resp.statusCode() == 200) { @@ -452,6 +461,10 @@ public static Uni discoverMetadata(WebClient client, List getJsonWebKeySet(OidcRequestContextProperties contextProperties) { - return filter(client.getAbs(metadata.getJsonWebKeySetUri()), null, contextProperties).send().onItem() + return filter(Endpoint.JWKS, client.getAbs(metadata.getJsonWebKeySetUri()), null, contextProperties).send().onItem() .transform(resp -> getJsonWebKeySet(resp)); } public Uni getUserInfo(String token) { LOG.debugf("Get UserInfo on: %s auth: %s", metadata.getUserInfoUri(), OidcConstants.BEARER_SCHEME + " " + token); - return filter(client.getAbs(metadata.getUserInfoUri()), null, null) + return filter(Endpoint.USERINFO, client.getAbs(metadata.getUserInfoUri()), null, null) .putHeader(AUTHORIZATION_HEADER, OidcConstants.BEARER_SCHEME + " " + token) .send().onItem().transform(resp -> getUserInfo(resp)); } @@ -168,7 +170,9 @@ private UniOnItem> getHttpResponse(String uri, MultiMap for LOG.debugf("Get token on: %s params: %s headers: %s", metadata.getTokenUri(), formBody, request.headers()); // Retry up to three times with a one-second delay between the retries if the connection is closed. Buffer buffer = OidcCommonUtils.encodeForm(formBody); - Uni> response = filter(request, buffer, null).sendBuffer(buffer) + + Endpoint endpoint = introspect ? Endpoint.INTROSPECTION : Endpoint.TOKEN; + Uni> response = filter(endpoint, request, buffer, null).sendBuffer(buffer) .onFailure(ConnectException.class) .retry() .atMost(oidcConfig.connectionRetryCount).onFailure().transform(t -> t.getCause()); @@ -224,10 +228,19 @@ public Key getClientJwtKey() { return clientJwtKey; } - private HttpRequest filter(HttpRequest request, Buffer body, + private HttpRequest filter(Endpoint endpoint, HttpRequest request, Buffer body, OidcRequestContextProperties contextProperties) { - for (OidcRequestFilter filter : filters) { - filter.filter(request, body, contextProperties); + if (!filters.isEmpty()) { + Map newProperties = contextProperties == null ? new HashMap<>() + : new HashMap<>(contextProperties.getAll()); + newProperties.put(OidcConfigurationMetadata.class.getName(), metadata); + OidcRequestContextProperties newContextProperties = new OidcRequestContextProperties(newProperties); + for (OidcRequestFilter filter : filters) { + Endpoint filterEndpoint = filter.endpoint(); + if (Endpoint.ALL == filterEndpoint || endpoint == filterEndpoint) { + filter.filter(request, body, newContextProperties); + } + } } return request; } diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcRecorder.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcRecorder.java index 50264f617dfd54..38bca4a502dd38 100644 --- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcRecorder.java +++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcRecorder.java @@ -446,7 +446,8 @@ protected static Uni createOidcClientUni(OidcTenantConfig oi .transform(new Function() { @Override public OidcConfigurationMetadata apply(JsonObject json) { - return new OidcConfigurationMetadata(json, createLocalMetadata(oidcConfig, authServerUriString)); + return new OidcConfigurationMetadata(json, createLocalMetadata(oidcConfig, authServerUriString), + OidcCommonUtils.getDiscoveryUri(authServerUriString)); } }); } diff --git a/integration-tests/oidc-client-wiremock/src/main/java/io/quarkus/it/keycloak/OidcRequestCustomizer.java b/integration-tests/oidc-client-wiremock/src/main/java/io/quarkus/it/keycloak/OidcRequestCustomizer.java index b0d0f2282c034d..a236b5ecef0503 100644 --- a/integration-tests/oidc-client-wiremock/src/main/java/io/quarkus/it/keycloak/OidcRequestCustomizer.java +++ b/integration-tests/oidc-client-wiremock/src/main/java/io/quarkus/it/keycloak/OidcRequestCustomizer.java @@ -28,4 +28,9 @@ private String getGrantType(String formString) { } return ""; } + + @Override + public Endpoint endpoint() { + return Endpoint.TOKEN; + } } diff --git a/integration-tests/oidc-wiremock/src/main/java/io/quarkus/it/keycloak/OidcDiscoveryRequestCustomizer.java b/integration-tests/oidc-wiremock/src/main/java/io/quarkus/it/keycloak/OidcDiscoveryRequestCustomizer.java new file mode 100644 index 00000000000000..6d470e9ecea05d --- /dev/null +++ b/integration-tests/oidc-wiremock/src/main/java/io/quarkus/it/keycloak/OidcDiscoveryRequestCustomizer.java @@ -0,0 +1,24 @@ +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 OidcDiscoveryRequestCustomizer implements OidcRequestFilter { + + @Override + public void filter(HttpRequest request, Buffer buffer, OidcRequestContextProperties contextProps) { + request.putHeader("Discovery", "OK"); + } + + @Override + public Endpoint endpoint() { + return Endpoint.DISCOVERY; + } +} diff --git a/integration-tests/oidc-wiremock/src/main/java/io/quarkus/it/keycloak/OidcRequestCustomizer.java b/integration-tests/oidc-wiremock/src/main/java/io/quarkus/it/keycloak/OidcJwksRequestCustomizer.java similarity index 72% rename from integration-tests/oidc-wiremock/src/main/java/io/quarkus/it/keycloak/OidcRequestCustomizer.java rename to integration-tests/oidc-wiremock/src/main/java/io/quarkus/it/keycloak/OidcJwksRequestCustomizer.java index 0f76995ecd0ed4..a9994849aea9bc 100644 --- a/integration-tests/oidc-wiremock/src/main/java/io/quarkus/it/keycloak/OidcRequestCustomizer.java +++ b/integration-tests/oidc-wiremock/src/main/java/io/quarkus/it/keycloak/OidcJwksRequestCustomizer.java @@ -4,22 +4,24 @@ import io.quarkus.arc.Unremovable; import io.quarkus.oidc.AccessTokenCredential; +import io.quarkus.oidc.OidcConfigurationMetadata; import io.quarkus.oidc.common.OidcRequestContextProperties; import io.quarkus.oidc.common.OidcRequestFilter; -import io.vertx.core.http.HttpMethod; import io.vertx.mutiny.core.buffer.Buffer; import io.vertx.mutiny.ext.web.client.HttpRequest; @ApplicationScoped @Unremovable -public class OidcRequestCustomizer implements OidcRequestFilter { +public class OidcJwksRequestCustomizer implements OidcRequestFilter { @Override public void filter(HttpRequest request, Buffer buffer, OidcRequestContextProperties contextProps) { - HttpMethod method = request.method(); + OidcConfigurationMetadata metadata = contextProps.get(OidcConfigurationMetadata.class.getName()); + // There are many tenants in the test so the URI check is still required String uri = request.uri(); - if (method == HttpMethod.GET && uri.endsWith("/auth/azure/jwk")) { - String token = contextProps.getString(OidcRequestContextProperties.TOKEN); + if (uri.equals("/auth/azure/jwk") && + metadata.getJsonWebKeySetUri().endsWith(uri)) { + String token = contextProps.get(OidcRequestContextProperties.TOKEN); AccessTokenCredential tokenCred = contextProps.get(OidcRequestContextProperties.TOKEN_CREDENTIAL, AccessTokenCredential.class); // or @@ -38,4 +40,9 @@ public void filter(HttpRequest request, Buffer buffer, OidcRequestContex } } + // Or: + //@Override + //public Endpoint endpoint() { + // return Endpoint.JWKS; + //} } diff --git a/integration-tests/oidc-wiremock/src/test/java/io/quarkus/it/keycloak/WiremockTestResource.java b/integration-tests/oidc-wiremock/src/test/java/io/quarkus/it/keycloak/WiremockTestResource.java index 31f834d31d05be..0c7e47985a5a43 100644 --- a/integration-tests/oidc-wiremock/src/test/java/io/quarkus/it/keycloak/WiremockTestResource.java +++ b/integration-tests/oidc-wiremock/src/test/java/io/quarkus/it/keycloak/WiremockTestResource.java @@ -1,6 +1,7 @@ package io.quarkus.it.keycloak; import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; import static com.github.tomakehurst.wiremock.client.WireMock.get; import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; @@ -24,6 +25,7 @@ public void start() { server.stubFor( get(urlEqualTo("/auth/realms/quarkus2/.well-known/openid-configuration")) + .withHeader("Discovery", equalTo("OK")) .willReturn(aResponse() .withHeader("Content-Type", "application/json") .withBody("{\n" +