This is an automated email from the ASF dual-hosted git repository.
lmccay pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/knox.git
The following commit(s) were added to refs/heads/master by this push:
new 84a0f3e1e KNOX-3260 - Extend Client Credentials Support to include the
client_id and client_secret as HTTP Basic (#1154)
84a0f3e1e is described below
commit 84a0f3e1e35adc8163f553c821bcd34ea73f2d73
Author: lmccay <[email protected]>
AuthorDate: Mon Feb 23 14:43:06 2026 -0500
KNOX-3260 - Extend Client Credentials Support to include the client_id and
client_secret as HTTP Basic (#1154)
---
.../federation/jwt/filter/JWTFederationFilter.java | 14 +++++++++++--
...lientIdAndClientSecretFederationFilterTest.java | 24 ++++++++++++++++++++++
2 files changed, 36 insertions(+), 2 deletions(-)
diff --git
a/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/JWTFederationFilter.java
b/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/JWTFederationFilter.java
index 978e49931..e3b0e9f2f 100644
---
a/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/JWTFederationFilter.java
+++
b/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/JWTFederationFilter.java
@@ -262,6 +262,10 @@ public class JWTFederationFilter extends AbstractJWTFilter
{
private void validateClientID(HttpServletRequest request, String tokenValue)
{
final String clientID = request.getParameter(CLIENT_ID);
+ validateClientID(clientID, tokenValue);
+ }
+
+ private void validateClientID(String clientID, String tokenValue) {
String tokenId;
try {
final String[] base64DecodedTokenIdAndPasscode =
decodeBase64(tokenValue).split("::");
@@ -299,7 +303,7 @@ public class JWTFederationFilter extends AbstractJWTFilter {
} else if
(header.toLowerCase(Locale.ROOT).startsWith(BASIC.toLowerCase(Locale.ROOT))) {
// what follows the Basic designator should be the JWT token or
the unique token ID being used
// to request or as an access token
- parsed = parseFromHTTPBasicCredentials(header);
+ parsed = parseFromHTTPBasicCredentials(header, request);
}
}
@@ -350,7 +354,7 @@ public class JWTFederationFilter extends AbstractJWTFilter {
return null;
}
- private Pair<TokenType, String> parseFromHTTPBasicCredentials(final String
header) {
+ private Pair<TokenType, String> parseFromHTTPBasicCredentials(final String
header, final ServletRequest request) {
Pair<TokenType, String> parsed = null;
final String base64Credentials = header.substring(BASIC.length()).trim();
final byte[] credDecoded = Base64.getDecoder().decode(base64Credentials);
@@ -360,6 +364,12 @@ public class JWTFederationFilter extends AbstractJWTFilter
{
String passcode = values[1].isEmpty() ? null : values[1];
if (TOKEN.equalsIgnoreCase(username) ||
PASSCODE.equalsIgnoreCase(username)) {
parsed = Pair.of(TOKEN.equalsIgnoreCase(username) ? TokenType.JWT :
TokenType.Passcode, passcode);
+ } else if (request != null &&
CLIENT_CREDENTIALS.equals(request.getParameter(GRANT_TYPE))) {
+ // Allow client_credentials flow where client_id/client_secret are
provided via HTTP Basic
+ if (passcode != null) {
+ validateClientID(username, passcode);
+ parsed = Pair.of(TokenType.Passcode, passcode);
+ }
}
return parsed;
diff --git
a/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/ClientIdAndClientSecretFederationFilterTest.java
b/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/ClientIdAndClientSecretFederationFilterTest.java
index 191f2c9a4..710604a7e 100644
---
a/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/ClientIdAndClientSecretFederationFilterTest.java
+++
b/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/ClientIdAndClientSecretFederationFilterTest.java
@@ -30,6 +30,8 @@ import org.junit.Test;
import javax.servlet.FilterConfig;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
import java.util.Properties;
import static org.junit.Assert.assertEquals;
@@ -76,6 +78,28 @@ public class ClientIdAndClientSecretFederationFilterTest
extends TokenIDAsHTTPBa
assertEquals(passcode, wireToken.getRight());
}
+ @Test
+ public void testGetWireTokenUsingClientCredentialsBasicAuth() throws
Exception {
+ final String clientId = "client-id-12345";
+ final String passcode =
"WTJ4cFpXNTBMV2xrTFRFeU16UTE6OlkyeHBaVzUwTFhObFkzSmxkQzB4TWpNME5RPT0=";
+ final String basicCredentials = Base64.getEncoder()
+ .encodeToString((clientId + ":" +
passcode).getBytes(StandardCharsets.UTF_8));
+
+ final HttpServletRequest request =
EasyMock.createNiceMock(HttpServletRequest.class);
+ EasyMock.expect(request.getHeader("Authorization")).andReturn("Basic "
+ basicCredentials).anyTimes();
+
EasyMock.expect(request.getParameter(JWTFederationFilter.GRANT_TYPE)).andReturn(JWTFederationFilter.CLIENT_CREDENTIALS).anyTimes();
+ EasyMock.replay(request);
+
+ handler.init(new TestFilterConfig(getProperties()));
+ final Pair<TokenType, String> wireToken = ((TestJWTFederationFilter)
handler).getWireToken(request);
+
+ EasyMock.verify(request);
+
+ assertNotNull(wireToken);
+ assertEquals(TokenType.Passcode, wireToken.getLeft());
+ assertEquals(passcode, wireToken.getRight());
+ }
+
@Test
public void testVerifyClientCredentialsFlow() throws Exception {
final String topologyName = "jwt-topology";