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