This is an automated email from the ASF dual-hosted git repository.
gnodet pushed a commit to branch camel-4.22.x
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/camel-4.22.x by this push:
new 01cf7cc708d0 [backport camel-4.22.x] CAMEL-25486: camel-http - the
OAuth2 token request with a resource indicator sends only the resource
parameter (#27614)
01cf7cc708d0 is described below
commit 01cf7cc708d03dce66842eb9db6e690b52d08fce
Author: Guillaume Nodet <[email protected]>
AuthorDate: Fri Oct 9 13:20:58 2026 +0200
[backport camel-4.22.x] CAMEL-25486: camel-http - the OAuth2 token request
with a resource indicator sends only the resource parameter (#27614)
---
.../component/http/OAuth2ClientConfigurer.java | 2 +-
.../http/HttpOAuth2ResourceIndicatorTest.java | 129 +++++++++++++++++++++
2 files changed, 130 insertions(+), 1 deletion(-)
diff --git
a/components/camel-http/src/main/java/org/apache/camel/component/http/OAuth2ClientConfigurer.java
b/components/camel-http/src/main/java/org/apache/camel/component/http/OAuth2ClientConfigurer.java
index c98d980a3ac6..cf321c3e61e2 100644
---
a/components/camel-http/src/main/java/org/apache/camel/component/http/OAuth2ClientConfigurer.java
+++
b/components/camel-http/src/main/java/org/apache/camel/component/http/OAuth2ClientConfigurer.java
@@ -171,7 +171,7 @@ public class OAuth2ClientConfigurer extends ServiceSupport
implements HttpClient
HttpCredentialsHelper.generateBasicAuthHeader(clientId,
clientSecret));
}
if (null != resourceIndicator) {
- bodyStr = String.join(bodyStr, "&resource=" + resourceIndicator);
+ bodyStr += "&resource=" + resourceIndicator;
}
httpPost.setEntity(new StringEntity(bodyStr,
ContentType.APPLICATION_FORM_URLENCODED));
diff --git
a/components/camel-http/src/test/java/org/apache/camel/component/http/HttpOAuth2ResourceIndicatorTest.java
b/components/camel-http/src/test/java/org/apache/camel/component/http/HttpOAuth2ResourceIndicatorTest.java
new file mode 100644
index 000000000000..cfdc48edaff4
--- /dev/null
+++
b/components/camel-http/src/test/java/org/apache/camel/component/http/HttpOAuth2ResourceIndicatorTest.java
@@ -0,0 +1,129 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.http;
+
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.component.http.handler.HeaderValidationHandler;
+import org.apache.camel.util.json.Jsoner;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.HttpStatus;
+import org.apache.hc.core5.http.impl.bootstrap.HttpServer;
+import org.apache.hc.core5.http.impl.bootstrap.ServerBootstrap;
+import org.apache.hc.core5.http.io.entity.EntityUtils;
+import org.apache.hc.core5.http.io.entity.StringEntity;
+import org.apache.hc.core5.net.WWWFormCodec;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * The token request of the OAuth2 client credentials flow with a resource
indicator (RFC 8707) has the resource
+ * parameter besides the other parameters.
+ */
+public class HttpOAuth2ResourceIndicatorTest extends BaseHttpTest {
+
+ private static final String FAKE_TOKEN = "xxx.yyy.zzz";
+ private static final String CLIENT_ID = "test-client";
+ private static final String CLIENT_SECRET = "test-secret";
+ private static final String SCOPE = "test-scope";
+ private static final String RESOURCE = "https://api.example.com/";
+
+ // the parameters of the token requests the token endpoint received
+ private final List<Map<String, String>> tokenRequests = new
CopyOnWriteArrayList<>();
+
+ @Override
+ public void setupResources() throws Exception {
+ }
+
+ @Test
+ public void resourceIsAddedToTheTokenRequest() throws Exception {
+ Map<String, String> tokenRequest = requestWithToken(false);
+
+ assertEquals("client_credentials", tokenRequest.get("grant_type"));
+ assertEquals(SCOPE, tokenRequest.get("scope"));
+ assertEquals(RESOURCE, tokenRequest.get("resource"));
+ }
+
+ @Test
+ public void resourceIsAddedToTheTokenRequestWithBodyAuthentication()
throws Exception {
+ Map<String, String> tokenRequest = requestWithToken(true);
+
+ assertEquals("client_credentials", tokenRequest.get("grant_type"));
+ assertEquals(SCOPE, tokenRequest.get("scope"));
+ assertEquals(CLIENT_ID, tokenRequest.get("client_id"));
+ assertEquals(CLIENT_SECRET, tokenRequest.get("client_secret"));
+ assertEquals(RESOURCE, tokenRequest.get("resource"));
+ }
+
+ // Sends a request that needs a token, and returns the parameters of the
token request
+ private Map<String, String> requestWithToken(boolean bodyAuthentication)
throws Exception {
+ tokenRequests.clear();
+ try (HttpServer localServer = createLocalServer()) {
+ String tokenEndpoint = "http://localhost:" +
localServer.getLocalPort() + "/token";
+ String requestUrl = "http://localhost:" +
localServer.getLocalPort() + "/post?httpMethod=POST&oauth2ClientId="
+ + CLIENT_ID + "&oauth2ClientSecret=" +
CLIENT_SECRET + "&oauth2TokenEndpoint=" + tokenEndpoint
+ + "&oauth2Scope=" + SCOPE +
"&oauth2ResourceIndicator=" + RESOURCE
+ + "&oauth2BodyAuthentication=" +
bodyAuthentication;
+
+ Exchange exchange = template.request(requestUrl, e -> {
+ });
+
+ assertExchange(exchange);
+ }
+ assertEquals(1, tokenRequests.size());
+ return tokenRequests.get(0);
+ }
+
+ @Override
+ protected void assertHeaders(Map<String, Object> headers) {
+ assertEquals(HttpStatus.SC_OK,
headers.get(Exchange.HTTP_RESPONSE_CODE));
+ }
+
+ @Override
+ protected String getExpectedContent() {
+ return "";
+ }
+
+ private HttpServer createLocalServer() throws Exception {
+ Map<String, String> expectedHeaders = new HashMap<>();
+ expectedHeaders.put("Authorization", "Bearer " + FAKE_TOKEN);
+
+ HttpServer localServer = ServerBootstrap.bootstrap()
+
.setCanonicalHostName("localhost").setHttpProcessor(getBasicHttpProcessor())
+
.setConnectionReuseStrategy(getConnectionReuseStrategy()).setResponseFactory(getHttpResponseFactory())
+ .setSslContext(getSSLContext())
+ .register("/token", (request, response, context) -> {
+ Map<String, String> parameters = new HashMap<>();
+
WWWFormCodec.parse(EntityUtils.toString(request.getEntity()),
StandardCharsets.UTF_8)
+ .forEach(pair -> parameters.put(pair.getName(),
pair.getValue()));
+ tokenRequests.add(parameters);
+ response.setEntity(new StringEntity(
+ Jsoner.serialize(Map.of("access_token",
FAKE_TOKEN)), ContentType.APPLICATION_JSON));
+ })
+ .register("/post", new HeaderValidationHandler("POST", null,
null, null, expectedHeaders))
+ .create();
+
+ localServer.start();
+ return localServer;
+ }
+}