This is an automated email from the ASF dual-hosted git repository.

lianetm pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/kafka.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 78c1da1acac KAFKA-17089: Incorrect JWT parsing in 
OAuthBearerUnsecuredJws (#19946)
78c1da1acac is described below

commit 78c1da1acac2ae6b0fc2e4b2c90e30d12e66b0df
Author: Kirk True <[email protected]>
AuthorDate: Thu Jun 12 06:53:50 2025 -0700

    KAFKA-17089: Incorrect JWT parsing in OAuthBearerUnsecuredJws (#19946)
    
    Fixed a long-standing issue where the client JWT validation was decoding
    the JWT sections using base 64 instead of URL-safe base 64.
    
    Note: server-side validation leverages the jose4j library for parsing
    JWTs, hence no fix is needed there.
    
    Reviewers: Lianet Magrans <[email protected]>, Manikumar Reddy
    <[email protected]>
    
    ---------
    
    Co-authored-by: Lianet Magrans <[email protected]>
---
 .../unsecured/OAuthBearerUnsecuredJws.java         |  2 +-
 .../oauthbearer/ClientJwtValidatorTest.java        | 29 ++++++++++++++++++++++
 .../internals/secured/OAuthBearerTest.java         |  2 +-
 3 files changed, 31 insertions(+), 2 deletions(-)

diff --git 
a/clients/src/main/java/org/apache/kafka/common/security/oauthbearer/internals/unsecured/OAuthBearerUnsecuredJws.java
 
b/clients/src/main/java/org/apache/kafka/common/security/oauthbearer/internals/unsecured/OAuthBearerUnsecuredJws.java
index 52872a00e45..bea463a8d14 100644
--- 
a/clients/src/main/java/org/apache/kafka/common/security/oauthbearer/internals/unsecured/OAuthBearerUnsecuredJws.java
+++ 
b/clients/src/main/java/org/apache/kafka/common/security/oauthbearer/internals/unsecured/OAuthBearerUnsecuredJws.java
@@ -294,7 +294,7 @@ public class OAuthBearerUnsecuredJws implements 
OAuthBearerToken {
     public static Map<String, Object> toMap(String split) throws 
OAuthBearerIllegalTokenException {
         Map<String, Object> retval = new HashMap<>();
         try {
-            byte[] decode = Base64.getDecoder().decode(split);
+            byte[] decode = Base64.getUrlDecoder().decode(split);
             JsonNode jsonNode = new ObjectMapper().readTree(decode);
             if (jsonNode == null)
                 throw new 
OAuthBearerIllegalTokenException(OAuthBearerValidationResult.newFailure("malformed
 JSON"));
diff --git 
a/clients/src/test/java/org/apache/kafka/common/security/oauthbearer/ClientJwtValidatorTest.java
 
b/clients/src/test/java/org/apache/kafka/common/security/oauthbearer/ClientJwtValidatorTest.java
index 3156d3ca810..e30fd2a7103 100644
--- 
a/clients/src/test/java/org/apache/kafka/common/security/oauthbearer/ClientJwtValidatorTest.java
+++ 
b/clients/src/test/java/org/apache/kafka/common/security/oauthbearer/ClientJwtValidatorTest.java
@@ -18,6 +18,15 @@
 package org.apache.kafka.common.security.oauthbearer;
 
 import 
org.apache.kafka.common.security.oauthbearer.internals.secured.AccessTokenBuilder;
+import org.apache.kafka.common.utils.Utils;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.Base64;
+
+import static 
org.apache.kafka.common.security.oauthbearer.OAuthBearerLoginModule.OAUTHBEARER_MECHANISM;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 
 public class ClientJwtValidatorTest extends JwtValidatorTest {
 
@@ -26,4 +35,24 @@ public class ClientJwtValidatorTest extends JwtValidatorTest 
{
         return new ClientJwtValidator();
     }
 
+    @Test
+    void testJwtRequiresBase64UrlDecoding() throws Exception {
+        String header = "{\"alg\":\"HS256\",\"typ\":\"JWT\"}";
+        String payload = "{\"sub\": \"jdoe\", \"exp\": 0, \"iat\": 0, 
\"data\":\">>>___<<<---\"}";
+        String signature = "dummysignature";
+        String jwt = createJwt(header, payload, signature);
+
+        // Verify that decoding the payload fails for "plain" base 64, but 
works with URL-safe base 64.
+        String urlEncodedPayload = 
Base64.getUrlEncoder().encodeToString(Utils.utf8(payload));
+        assertThrows(IllegalArgumentException.class, () -> 
Base64.getDecoder().decode(urlEncodedPayload));
+        assertDoesNotThrow(() -> 
Base64.getUrlDecoder().decode(urlEncodedPayload));
+
+        try (JwtValidator validator = createJwtValidator()) {
+            validator.configure(getSaslConfigs(), OAUTHBEARER_MECHANISM, 
getJaasConfigEntries());
+            assertDoesNotThrow(
+                () -> validator.validate(jwt),
+                "Valid, URL-safe base 64-encoded JWT should be decodable"
+            );
+        }
+    }
 }
diff --git 
a/clients/src/test/java/org/apache/kafka/common/security/oauthbearer/internals/secured/OAuthBearerTest.java
 
b/clients/src/test/java/org/apache/kafka/common/security/oauthbearer/internals/secured/OAuthBearerTest.java
index 7a0aeea3d3d..6cfee841780 100644
--- 
a/clients/src/test/java/org/apache/kafka/common/security/oauthbearer/internals/secured/OAuthBearerTest.java
+++ 
b/clients/src/test/java/org/apache/kafka/common/security/oauthbearer/internals/secured/OAuthBearerTest.java
@@ -193,7 +193,7 @@ public abstract class OAuthBearerTest {
     }
 
     protected String createJwt(String header, String payload, String 
signature) {
-        Base64.Encoder enc = Base64.getEncoder();
+        Base64.Encoder enc = Base64.getUrlEncoder();
         header = enc.encodeToString(Utils.utf8(header));
         payload = enc.encodeToString(Utils.utf8(payload));
         signature = enc.encodeToString(Utils.utf8(signature));

Reply via email to