diff --git a/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/OidcClientConfig.java b/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/OidcClientConfig.java index 832a012974abb..975168eaf5abb 100644 --- a/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/OidcClientConfig.java +++ b/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/OidcClientConfig.java @@ -159,6 +159,12 @@ public void setExpiresInProperty(String expiresInProperty) { @ConfigItem(defaultValue = "true") public boolean earlyTokensAcquisition = true; + /** + * Custom HTTP headers which have to be sent to the token endpoint + */ + @ConfigItem + public Map headers; + public Optional getId() { return id; } @@ -198,4 +204,12 @@ public Optional getRefreshTokenTimeSkew() { public void setRefreshTokenTimeSkew(Duration refreshTokenTimeSkew) { this.refreshTokenTimeSkew = Optional.of(refreshTokenTimeSkew); } + + public Map getHeaders() { + return headers; + } + + public void setHeaders(Map headers) { + this.headers = headers; + } } 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 63bf8b9805211..27978c01c4908 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 @@ -88,6 +88,11 @@ public Uni get() { HttpRequest request = client.postAbs(tokenRequestUri); request.putHeader(HttpHeaders.CONTENT_TYPE.toString(), HttpHeaders.APPLICATION_X_WWW_FORM_URLENCODED.toString()); + if (oidcConfig.headers != null) { + for (Map.Entry headerEntry : oidcConfig.headers.entrySet()) { + request.putHeader(headerEntry.getKey(), headerEntry.getValue()); + } + } if (clientSecretBasicAuthScheme != null) { request.putHeader(AUTHORIZATION_HEADER, clientSecretBasicAuthScheme); } else if (clientJwtKey != null) { diff --git a/integration-tests/oidc-client-wiremock/src/main/java/io/quarkus/it/keycloak/FrontendResource.java b/integration-tests/oidc-client-wiremock/src/main/java/io/quarkus/it/keycloak/FrontendResource.java index e6f7da8c2d6b2..2c6bacdc96380 100644 --- a/integration-tests/oidc-client-wiremock/src/main/java/io/quarkus/it/keycloak/FrontendResource.java +++ b/integration-tests/oidc-client-wiremock/src/main/java/io/quarkus/it/keycloak/FrontendResource.java @@ -5,10 +5,13 @@ import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; +import javax.ws.rs.WebApplicationException; import org.eclipse.microprofile.rest.client.inject.RestClient; import io.quarkus.oidc.client.NamedOidcClient; +import io.quarkus.oidc.client.OidcClient; +import io.quarkus.oidc.client.OidcClientException; import io.quarkus.oidc.client.OidcClients; import io.quarkus.oidc.client.Tokens; import io.smallrye.mutiny.Uni; @@ -23,6 +26,10 @@ public class FrontendResource { @NamedOidcClient("non-standard-response") Tokens tokens; + @Inject + @NamedOidcClient("non-standard-response-without-header") + OidcClient tokensWithoutHeader; + @Inject OidcClients clients; @@ -35,7 +42,18 @@ public String echoToken() { @GET @Path("echoTokenNonStandardResponse") public String echoTokenNonStandardResponse() { - return tokens.getAccessToken() + " " + tokens.getRefreshToken(); + try { + return tokens.getAccessToken() + " " + tokens.getRefreshToken(); + } catch (OidcClientException ex) { + throw new WebApplicationException(401); + } + } + + @GET + @Path("echoTokenNonStandardResponseWithoutHeader") + public Uni echoTokenNonStandardResponseWithoutHeader() { + return tokensWithoutHeader.getTokens().onFailure(OidcClientException.class) + .transform(t -> new WebApplicationException(401)); } @GET diff --git a/integration-tests/oidc-client-wiremock/src/main/resources/application.properties b/integration-tests/oidc-client-wiremock/src/main/resources/application.properties index 33370bbd44517..d7f48a4442734 100644 --- a/integration-tests/oidc-client-wiremock/src/main/resources/application.properties +++ b/integration-tests/oidc-client-wiremock/src/main/resources/application.properties @@ -18,6 +18,19 @@ quarkus.oidc-client.non-standard-response.grant.refresh-token-property=refreshTo quarkus.oidc-client.non-standard-response.grant.expires-in-property=expiresIn quarkus.oidc-client.non-standard-response.grant-options.password.username=alice quarkus.oidc-client.non-standard-response.grant-options.password.password=alice +quarkus.oidc-client.non-standard-response.headers.X-Custom=XCustomHeaderValue + +quarkus.oidc-client.non-standard-response-without-header.auth-server-url=${keycloak.url} +quarkus.oidc-client.non-standard-response-without-header.discovery-enabled=false +quarkus.oidc-client.non-standard-response-without-header.token-path=/non-standard-tokens +quarkus.oidc-client.non-standard-response-without-header.client-id=quarkus-app +quarkus.oidc-client.non-standard-response-without-header.credentials.secret=secret +quarkus.oidc-client.non-standard-response-without-header.grant.type=password +quarkus.oidc-client.non-standard-response-without-header.grant.access-token-property=accessToken +quarkus.oidc-client.non-standard-response-without-header.grant.refresh-token-property=refreshToken +quarkus.oidc-client.non-standard-response-without-header.grant.expires-in-property=expiresIn +quarkus.oidc-client.non-standard-response-without-header.grant-options.password.username=alice +quarkus.oidc-client.non-standard-response-without-header.grant-options.password.password=alice quarkus.oidc-client.refresh.auth-server-url=${keycloak.url} quarkus.oidc-client.refresh.discovery-enabled=false diff --git a/integration-tests/oidc-client-wiremock/src/test/java/io/quarkus/it/keycloak/KeycloakRealmResourceManager.java b/integration-tests/oidc-client-wiremock/src/test/java/io/quarkus/it/keycloak/KeycloakRealmResourceManager.java index a8aef8f8c2f1f..93a509926d7e5 100644 --- a/integration-tests/oidc-client-wiremock/src/test/java/io/quarkus/it/keycloak/KeycloakRealmResourceManager.java +++ b/integration-tests/oidc-client-wiremock/src/test/java/io/quarkus/it/keycloak/KeycloakRealmResourceManager.java @@ -36,6 +36,7 @@ public Map start() { .withBody( "{\"access_token\":\"access_token_1\", \"expires_in\":4, \"refresh_token\":\"refresh_token_1\"}"))); server.stubFor(WireMock.post("/non-standard-tokens") + .withHeader("X-Custom", matching("XCustomHeaderValue")) .withRequestBody(matching("grant_type=password&username=alice&password=alice")) .willReturn(WireMock .aResponse() diff --git a/integration-tests/oidc-client-wiremock/src/test/java/io/quarkus/it/keycloak/OidcClientTest.java b/integration-tests/oidc-client-wiremock/src/test/java/io/quarkus/it/keycloak/OidcClientTest.java index 7d4c45df61bd5..f5a955b8549a4 100644 --- a/integration-tests/oidc-client-wiremock/src/test/java/io/quarkus/it/keycloak/OidcClientTest.java +++ b/integration-tests/oidc-client-wiremock/src/test/java/io/quarkus/it/keycloak/OidcClientTest.java @@ -61,6 +61,13 @@ public void testEchoTokensNonStandardResponse() { .body(equalTo("access_token_n refresh_token_n")); } + @Test + public void testEchoTokensNonStandardResponseWithoutHeader() { + RestAssured.when().get("/frontend/echoTokenNonStandardResponseWithoutHeader") + .then() + .statusCode(401); + } + @Test public void testEchoTokensRefreshTokenOnly() { RestAssured.given().queryParam("refreshToken", "shared_refresh_token")