This is an automated email from the ASF dual-hosted git repository.
Croway pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new 04a1f0d29f1d CAMEL-26162: camel-workday - the token request sends the
Base64 Basic header, and the access token is read as JSON (#28691)
04a1f0d29f1d is described below
commit 04a1f0d29f1d797ae00cb626f4d0dd92c81ca243
Author: Vignesh Nayak Manel <[email protected]>
AuthorDate: Thu Oct 29 19:45:53 2026 +0530
CAMEL-26162: camel-workday - the token request sends the Base64 Basic
header, and the access token is read as JSON (#28691)
The Authorization header of the token request was "Basic " +
Arrays.toString of
the Base64 bytes ("Basic [89, 50, ...]"), so Workday rejected it and no
access
token could be obtained (since the HttpComponents 5 upgrade, CAMEL-18995).
The
access token was cut from the response at fixed offsets, which only worked
for
one exact JSON layout. It is now read as the access_token field of the JSON
response with camel-util-json.
Co-authored-by: Claude Opus 5.5 <[email protected]>
---
components/camel-workday/pom.xml | 4 +
.../workday/auth/AuthClientForIntegration.java | 25 ++---
.../workday/auth/AuthClientForIntegrationTest.java | 124 +++++++++++++++++++++
3 files changed, 139 insertions(+), 14 deletions(-)
diff --git a/components/camel-workday/pom.xml b/components/camel-workday/pom.xml
index 4687d5adaf04..aeaba270d65b 100644
--- a/components/camel-workday/pom.xml
+++ b/components/camel-workday/pom.xml
@@ -43,6 +43,10 @@
<groupId>org.apache.camel</groupId>
<artifactId>camel-support</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-util-json</artifactId>
+ </dependency>
<!-- testing -->
<dependency>
diff --git
a/components/camel-workday/src/main/java/org/apache/camel/component/workday/auth/AuthClientForIntegration.java
b/components/camel-workday/src/main/java/org/apache/camel/component/workday/auth/AuthClientForIntegration.java
index d24bca4c3bac..712ac2ecdc16 100644
---
a/components/camel-workday/src/main/java/org/apache/camel/component/workday/auth/AuthClientForIntegration.java
+++
b/components/camel-workday/src/main/java/org/apache/camel/component/workday/auth/AuthClientForIntegration.java
@@ -20,11 +20,12 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Base64;
import java.util.List;
import org.apache.camel.component.workday.WorkdayConfiguration;
+import org.apache.camel.util.json.JsonObject;
+import org.apache.camel.util.json.Jsoner;
import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.classic.methods.HttpUriRequest;
import org.apache.hc.client5.http.entity.UrlEncodedFormEntity;
@@ -80,7 +81,7 @@ public class AuthClientForIntegration implements
AuthenticationClient {
try (ByteArrayOutputStream baos = new
ByteArrayOutputStream()) {
httpResponse.getEntity().writeTo(baos);
- return parseResponse(baos.toString());
+ return
parseResponse(baos.toString(StandardCharsets.UTF_8));
}
});
@@ -95,25 +96,21 @@ public class AuthClientForIntegration implements
AuthenticationClient {
HttpPost postMethod = new HttpPost(tokenUrl);
postMethod.addHeader(CONTENT_TYPE_HEADER, CONTENT_TYPE);
postMethod.addHeader(AUTHORIZATION_HEADER,
- "Basic " + Arrays.toString(Base64.getEncoder()
- .encode((workdayConfiguration.getClientId() + ":" +
workdayConfiguration.getClientSecret())
- .getBytes())));
+ "Basic " + Base64.getEncoder()
+ .encodeToString((workdayConfiguration.getClientId() +
":" + workdayConfiguration.getClientSecret())
+ .getBytes(StandardCharsets.UTF_8)));
postMethod.setEntity(new UrlEncodedFormEntity(nvps,
StandardCharsets.UTF_8));
return postMethod;
}
private String parseResponse(String response) {
-
- int tokenIdx = response.indexOf(ACCESS_TOKEN);
-
- if (tokenIdx < 1) {
- throw new IllegalStateException("No valid access token response.");
+ JsonObject json = Jsoner.deserialize(response, (JsonObject) null);
+ Object accessToken = json != null ? json.get(ACCESS_TOKEN) : null;
+ if (accessToken instanceof String token && !token.isEmpty()) {
+ return token;
}
-
- response = response.substring(response.indexOf(ACCESS_TOKEN) + 16,
response.length() - 3);
-
- return response;
+ throw new IllegalStateException("No valid access token response.");
}
}
diff --git
a/components/camel-workday/src/test/java/org/apache/camel/component/workday/auth/AuthClientForIntegrationTest.java
b/components/camel-workday/src/test/java/org/apache/camel/component/workday/auth/AuthClientForIntegrationTest.java
new file mode 100644
index 000000000000..9a94fafab070
--- /dev/null
+++
b/components/camel-workday/src/test/java/org/apache/camel/component/workday/auth/AuthClientForIntegrationTest.java
@@ -0,0 +1,124 @@
+/*
+ * 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.workday.auth;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+import org.apache.camel.component.workday.WorkdayConfiguration;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
+import org.apache.hc.core5.http.ClassicHttpRequest;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.HttpException;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.HttpStatus;
+import org.apache.hc.core5.http.io.HttpClientResponseHandler;
+import org.apache.hc.core5.http.io.entity.EntityUtils;
+import org.apache.hc.core5.http.io.entity.StringEntity;
+import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
+import org.apache.hc.core5.http.protocol.HttpContext;
+import org.apache.hc.core5.io.CloseMode;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+class AuthClientForIntegrationTest {
+
+ @Test
+ void tokenRequestHasTheBasicAuthorizationHeader() throws Exception {
+ TokenEndpoint tokenEndpoint = new
TokenEndpoint("{\"access_token\":\"the-token\"}");
+
+ assertEquals("the-token", new
AuthClientForIntegration(configuration()).getBearerToken(tokenEndpoint));
+
+ ClassicHttpRequest request = tokenEndpoint.request;
+ assertEquals("https://impl.workday.com/ccx/oauth2/camel/token",
request.getUri().toString());
+ // Base64 of client-id:client-secret
+ assertEquals("Basic Y2xpZW50LWlkOmNsaWVudC1zZWNyZXQ=",
request.getFirstHeader("Authorization").getValue());
+
assertEquals("grant_type=refresh_token&refresh_token=the-refresh-token",
+ EntityUtils.toString(request.getEntity(),
StandardCharsets.UTF_8));
+ }
+
+ @Test
+ void accessTokenIsReadFromAnyJsonLayout() throws Exception {
+ TokenEndpoint tokenEndpoint = new TokenEndpoint(
+ "{\n \"access_token\" : \"the-token\",\n \"token_type\" :
\"Bearer\",\n \"expires_in\" : 3600\n}");
+
+ assertEquals("the-token", new
AuthClientForIntegration(configuration()).getBearerToken(tokenEndpoint));
+ }
+
+ @Test
+ void responseWithoutAccessTokenIsRejected() {
+ AuthClientForIntegration client = new
AuthClientForIntegration(configuration());
+
+ for (String response : new String[] {
+ "{\"error\":\"invalid_client\",\"error_description\":\"no
access_token for you\"}", "access_token", "" }) {
+ IllegalStateException e = assertThrows(IllegalStateException.class,
+ () -> client.getBearerToken(new TokenEndpoint(response)),
response);
+ assertEquals("No valid access token response.", e.getMessage());
+ }
+ }
+
+ private static WorkdayConfiguration configuration() {
+ WorkdayConfiguration configuration = new WorkdayConfiguration();
+ configuration.setHost("impl.workday.com");
+ configuration.setTenant("camel");
+ configuration.setClientId("client-id");
+ configuration.setClientSecret("client-secret");
+ configuration.setTokenRefresh("the-refresh-token");
+ return configuration;
+ }
+
+ /**
+ * Records the token request and answers it with the given JSON, in place
of the Workday token endpoint.
+ */
+ private static final class TokenEndpoint extends CloseableHttpClient {
+
+ private final String response;
+ private ClassicHttpRequest request;
+
+ private TokenEndpoint(String response) {
+ this.response = response;
+ }
+
+ @Override
+ public <T> T execute(ClassicHttpRequest request,
HttpClientResponseHandler<? extends T> responseHandler)
+ throws IOException {
+ this.request = request;
+ try (BasicClassicHttpResponse httpResponse = new
BasicClassicHttpResponse(HttpStatus.SC_OK)) {
+ httpResponse.setEntity(new StringEntity(response,
ContentType.APPLICATION_JSON));
+ return responseHandler.handleResponse(httpResponse);
+ } catch (HttpException e) {
+ throw new IOException(e);
+ }
+ }
+
+ @Override
+ protected CloseableHttpResponse doExecute(HttpHost target,
ClassicHttpRequest request, HttpContext context) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void close() {
+ }
+
+ @Override
+ public void close(CloseMode closeMode) {
+ }
+ }
+}