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";

Reply via email to