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

technoboy pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar.git


The following commit(s) were added to refs/heads/master by this push:
     new 270120ce6e3 [improve][misc] Upgrade jjwt library version to 0.13.0 
(#25043)
270120ce6e3 is described below

commit 270120ce6e33e5a084397ca31186f1bb87835e48
Author: Lari Hotari <[email protected]>
AuthorDate: Tue Dec 9 03:49:06 2025 +0200

    [improve][misc] Upgrade jjwt library version to 0.13.0 (#25043)
---
 distribution/server/src/assemble/LICENSE.bin.txt   |  6 +--
 pom.xml                                            | 16 ++-----
 pulsar-broker-auth-oidc/pom.xml                    |  4 --
 .../AuthenticationProviderToken.java               | 27 +++++------
 .../MultiRolesTokenAuthorizationProvider.java      | 55 ++++++++++++----------
 .../AuthenticationProviderTokenTest.java           | 12 ++---
 .../MultiRolesTokenAuthorizationProviderTest.java  | 10 ++--
 .../pulsar/utils/auth/tokens/TokensCliUtils.java   |  4 +-
 .../utils/auth/tokens/TokensCliUtilsTest.java      | 12 ++---
 9 files changed, 68 insertions(+), 78 deletions(-)

diff --git a/distribution/server/src/assemble/LICENSE.bin.txt 
b/distribution/server/src/assemble/LICENSE.bin.txt
index 5af59139404..3950efbf886 100644
--- a/distribution/server/src/assemble/LICENSE.bin.txt
+++ b/distribution/server/src/assemble/LICENSE.bin.txt
@@ -488,9 +488,9 @@ The Apache Software License, Version 2.0
   * OpenHFT
     - net.openhft-zero-allocation-hashing-0.16.jar
   * Java JSON WebTokens
-    - io.jsonwebtoken-jjwt-api-0.11.1.jar
-    - io.jsonwebtoken-jjwt-impl-0.11.1.jar
-    - io.jsonwebtoken-jjwt-jackson-0.11.1.jar
+    - io.jsonwebtoken-jjwt-api-0.13.0.jar
+    - io.jsonwebtoken-jjwt-impl-0.13.0.jar
+    - io.jsonwebtoken-jjwt-jackson-0.13.0.jar
   * JCTools - Java Concurrency Tools for the JVM
     - org.jctools-jctools-core-4.0.5.jar
   * Vertx
diff --git a/pom.xml b/pom.xml
index 162c3be16a3..9394d73db50 100644
--- a/pom.xml
+++ b/pom.xml
@@ -250,7 +250,7 @@ flexible messaging model and an intuitive client 
API.</description>
     <debezium.version>3.2.5.Final</debezium.version>
     
<debezium.postgresql.version>${postgresql-jdbc.version}</debezium.postgresql.version>
     <debezium.mysql.version>9.4.0</debezium.mysql.version>
-    <jsonwebtoken.version>0.11.1</jsonwebtoken.version>
+    <jsonwebtoken.version>0.13.0</jsonwebtoken.version>
     <opencensus.version>0.28.0</opencensus.version>
     <hadoop3.version>3.4.2</hadoop3.version>
     <dnsjava3.version>3.6.2</dnsjava3.version>
@@ -1208,18 +1208,10 @@ flexible messaging model and an intuitive client 
API.</description>
 
       <dependency>
         <groupId>io.jsonwebtoken</groupId>
-        <artifactId>jjwt-api</artifactId>
-        <version>${jsonwebtoken.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>io.jsonwebtoken</groupId>
-        <artifactId>jjwt-impl</artifactId>
-        <version>${jsonwebtoken.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>io.jsonwebtoken</groupId>
-        <artifactId>jjwt-jackson</artifactId>
+        <artifactId>jjwt-bom</artifactId>
         <version>${jsonwebtoken.version}</version>
+        <type>pom</type>
+        <scope>import</scope>
       </dependency>
 
       <dependency>
diff --git a/pulsar-broker-auth-oidc/pom.xml b/pulsar-broker-auth-oidc/pom.xml
index f50423e144e..14ec21bb58e 100644
--- a/pulsar-broker-auth-oidc/pom.xml
+++ b/pulsar-broker-auth-oidc/pom.xml
@@ -34,10 +34,6 @@
   <description>Open ID Connect authentication plugin for broker</description>
   <name>Pulsar Broker Auth OIDC</name>
 
-  <properties>
-    <jsonwebtoken.version>0.11.5</jsonwebtoken.version>
-  </properties>
-
   <dependencies>
 
     <dependency>
diff --git 
a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/AuthenticationProviderToken.java
 
b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/AuthenticationProviderToken.java
index 74bc85ad3ff..7a1b518831f 100644
--- 
a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/AuthenticationProviderToken.java
+++ 
b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/AuthenticationProviderToken.java
@@ -23,7 +23,7 @@ import static 
org.apache.pulsar.broker.web.AuthenticationFilter.AuthenticatedDat
 import static 
org.apache.pulsar.broker.web.AuthenticationFilter.AuthenticatedRoleAttributeName;
 import io.jsonwebtoken.Claims;
 import io.jsonwebtoken.ExpiredJwtException;
-import io.jsonwebtoken.Jwt;
+import io.jsonwebtoken.Jws;
 import io.jsonwebtoken.JwtException;
 import io.jsonwebtoken.JwtParser;
 import io.jsonwebtoken.Jwts;
@@ -33,8 +33,9 @@ import io.jsonwebtoken.security.SignatureException;
 import java.io.IOException;
 import java.net.SocketAddress;
 import java.security.Key;
+import java.util.Collection;
 import java.util.Date;
-import java.util.List;
+import java.util.Optional;
 import javax.naming.AuthenticationException;
 import javax.net.ssl.SSLSession;
 import javax.servlet.http.HttpServletRequest;
@@ -137,7 +138,7 @@ public class AuthenticationProviderToken implements 
AuthenticationProvider {
 
         long allowedSkew = getConfTokenAllowedClockSkewSeconds(config);
 
-        this.parser = Jwts.parserBuilder()
+        this.parser = Jwts.parser()
                 .setAllowedClockSkewSeconds(allowedSkew)
                 .setSigningKey(this.validationKey)
                 .build();
@@ -228,9 +229,9 @@ public class AuthenticationProviderToken implements 
AuthenticationProvider {
     }
 
     @SuppressWarnings("unchecked")
-    private Jwt<?, Claims> authenticateToken(final String token) throws 
AuthenticationException {
+    private Jws<Claims> authenticateToken(final String token) throws 
AuthenticationException {
         try {
-            Jwt<?, Claims> jwt = parser.parseClaimsJws(token);
+            Jws<Claims> jwt = parser.parseClaimsJws(token);
 
             if (audienceClaim != null) {
                 Object object = jwt.getBody().get(audienceClaim);
@@ -238,8 +239,8 @@ public class AuthenticationProviderToken implements 
AuthenticationProvider {
                     throw new JwtException("Found null Audience in token, for 
claimed field: " + audienceClaim);
                 }
 
-                if (object instanceof List) {
-                    List<String> audiences = (List<String>) object;
+                if (object instanceof Collection) {
+                    Collection<String> audiences = (Collection<String>) object;
                     // audience not contains this broker, throw exception.
                     if (audiences.stream().noneMatch(audienceInToken -> 
audienceInToken.equals(audience))) {
                         incrementFailureMetric(ErrorCode.INVALID_AUDIENCES);
@@ -272,15 +273,13 @@ public class AuthenticationProviderToken implements 
AuthenticationProvider {
         }
     }
 
-    private String getPrincipal(Jwt<?, Claims> jwt) {
+    private String getPrincipal(Jws<Claims> jwt) {
         try {
             return jwt.getBody().get(roleClaim, String.class);
         } catch (RequiredTypeException requiredTypeException) {
-            List list = jwt.getBody().get(roleClaim, List.class);
-            if (list != null && !list.isEmpty() && list.get(0) instanceof 
String) {
-                return (String) list.get(0);
-            }
-            return null;
+            Collection list = jwt.getBody().get(roleClaim, Collection.class);
+            Optional<String> firstEntry = 
list.stream().findFirst().map(Object::toString);
+            return firstEntry.orElse(null);
         }
     }
 
@@ -358,7 +357,7 @@ public class AuthenticationProviderToken implements 
AuthenticationProvider {
         private final SocketAddress remoteAddress;
         private final SSLSession sslSession;
         private AuthenticationDataSource authenticationDataSource;
-        private Jwt<?, Claims> jwt;
+        private Jws<Claims> jwt;
         private long expiration;
 
         TokenAuthenticationState(
diff --git 
a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/MultiRolesTokenAuthorizationProvider.java
 
b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/MultiRolesTokenAuthorizationProvider.java
index fdab233a510..6009da001bf 100644
--- 
a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/MultiRolesTokenAuthorizationProvider.java
+++ 
b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/MultiRolesTokenAuthorizationProvider.java
@@ -18,13 +18,16 @@
  */
 package org.apache.pulsar.broker.authorization;
 
-import io.jsonwebtoken.Claims;
-import io.jsonwebtoken.Jwt;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
 import io.jsonwebtoken.JwtParser;
 import io.jsonwebtoken.Jwts;
-import io.jsonwebtoken.RequiredTypeException;
 import java.io.IOException;
+import java.io.UncheckedIOException;
 import java.util.ArrayList;
+import java.util.Base64;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
@@ -64,12 +67,16 @@ public class MultiRolesTokenAuthorizationProvider extends 
PulsarAuthorizationPro
     // The token's claim that corresponds to the "role" string
     static final String CONF_TOKEN_AUTH_CLAIM = "tokenAuthClaim";
 
+    static final String DEFAULT_ROLE_CLAIM = "roles";
+
+    private static final ObjectMapper mapper = new ObjectMapper();
+
     private final JwtParser parser;
     private String roleClaim;
 
     public MultiRolesTokenAuthorizationProvider() {
-        this.roleClaim = Claims.SUBJECT;
-        this.parser = Jwts.parserBuilder().build();
+        this.roleClaim = DEFAULT_ROLE_CLAIM;
+        this.parser = Jwts.parser().unsecured().build();
     }
 
     @Override
@@ -179,30 +186,26 @@ public class MultiRolesTokenAuthorizationProvider extends 
PulsarAuthorizationPro
             log.warn("Unable to extract additional roles from JWT token");
             return Collections.emptySet();
         }
-        String unsignedToken = splitToken[0] + "." + splitToken[1] + ".";
+        String unsignedToken = replaceAlgWithNoneInHeader(splitToken[0]) + "." 
+ splitToken[1] + ".";
+        Object jwtRole = 
parser.parseUnsecuredClaims(unsignedToken).getBody().get(roleClaim);
+        if (jwtRole instanceof String) {
+            return new HashSet<String>(Collections.singletonList((String) 
jwtRole));
+        } else if (jwtRole instanceof Collection) {
+            return new HashSet<>((Collection<String>) jwtRole);
+        } else {
+            return Collections.emptySet();
+        }
+    }
 
-        Jwt<?, Claims> jwt = parser.parseClaimsJwt(unsignedToken);
+    // replace alg with none in header so that it can be parsed in unsecured 
mode
+    private static String replaceAlgWithNoneInHeader(String header) {
         try {
-            final String jwtRole = jwt.getBody().get(roleClaim, String.class);
-            if (jwtRole == null) {
-                if (log.isDebugEnabled()) {
-                    log.debug("Do not have corresponding claim in jwt token. 
claim={}", roleClaim);
-                }
-                return Collections.emptySet();
-            }
-            return new HashSet<>(Collections.singletonList(jwtRole));
-        } catch (RequiredTypeException requiredTypeException) {
-            try {
-                List list = jwt.getBody().get(roleClaim, List.class);
-                if (list != null) {
-                    return new HashSet<String>(list);
-                }
-            } catch (RequiredTypeException requiredTypeException1) {
-                return Collections.emptySet();
-            }
+            JsonNode jsonNode = 
mapper.readTree(Base64.getDecoder().decode(header));
+            ((ObjectNode) jsonNode).put("alg", "none");
+            return 
Base64.getEncoder().withoutPadding().encodeToString(mapper.writeValueAsBytes(jsonNode));
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
         }
-
-        return Collections.emptySet();
     }
 
     public CompletableFuture<Boolean> authorize(String role, 
AuthenticationDataSource authenticationData,
diff --git 
a/pulsar-broker-common/src/test/java/org/apache/pulsar/broker/authentication/AuthenticationProviderTokenTest.java
 
b/pulsar-broker-common/src/test/java/org/apache/pulsar/broker/authentication/AuthenticationProviderTokenTest.java
index 8e1aa54522d..a2daace307a 100644
--- 
a/pulsar-broker-common/src/test/java/org/apache/pulsar/broker/authentication/AuthenticationProviderTokenTest.java
+++ 
b/pulsar-broker-common/src/test/java/org/apache/pulsar/broker/authentication/AuthenticationProviderTokenTest.java
@@ -31,7 +31,7 @@ import static org.testng.Assert.assertTrue;
 import static org.testng.Assert.fail;
 import com.google.common.collect.Lists;
 import io.jsonwebtoken.Claims;
-import io.jsonwebtoken.Jwt;
+import io.jsonwebtoken.Jws;
 import io.jsonwebtoken.JwtBuilder;
 import io.jsonwebtoken.Jwts;
 import io.jsonwebtoken.SignatureAlgorithm;
@@ -92,10 +92,10 @@ public class AuthenticationProviderTokenTest {
                 .compact();
 
         @SuppressWarnings("unchecked")
-        Jwt<?, Claims> jwt = Jwts.parserBuilder()
+        Jws<Claims> jwt = Jwts.parser()
                 
.setSigningKey(AuthTokenUtils.decodeSecretKey(secretKey.getEncoded()))
                 .build()
-                .parse(token);
+                .parseSignedClaims(token);
 
         assertNotNull(jwt);
         assertNotNull(jwt.getBody());
@@ -115,10 +115,10 @@ public class AuthenticationProviderTokenTest {
                 Optional.empty());
 
         @SuppressWarnings("unchecked")
-        Jwt<?, Claims> jwt = Jwts.parserBuilder().setSigningKey(
-                
AuthTokenUtils.decodePublicKey(Decoders.BASE64.decode(publicKey), 
SignatureAlgorithm.RS256))
+        Jws<Claims> jwt = Jwts.parser().setSigningKey(
+                        
AuthTokenUtils.decodePublicKey(Decoders.BASE64.decode(publicKey), 
SignatureAlgorithm.RS256))
                 .build()
-                .parse(token);
+                .parseSignedClaims(token);
 
         assertNotNull(jwt);
         assertNotNull(jwt.getBody());
diff --git 
a/pulsar-broker-common/src/test/java/org/apache/pulsar/broker/authorization/MultiRolesTokenAuthorizationProviderTest.java
 
b/pulsar-broker-common/src/test/java/org/apache/pulsar/broker/authorization/MultiRolesTokenAuthorizationProviderTest.java
index e6818dca4c0..fd3e24c676b 100644
--- 
a/pulsar-broker-common/src/test/java/org/apache/pulsar/broker/authorization/MultiRolesTokenAuthorizationProviderTest.java
+++ 
b/pulsar-broker-common/src/test/java/org/apache/pulsar/broker/authorization/MultiRolesTokenAuthorizationProviderTest.java
@@ -43,7 +43,7 @@ public class MultiRolesTokenAuthorizationProviderTest {
         SecretKey secretKey = 
AuthTokenUtils.createSecretKey(SignatureAlgorithm.HS256);
         String userA = "user-a";
         String userB = "user-b";
-        String token = Jwts.builder().claim("sub", new String[]{userA, 
userB}).signWith(secretKey).compact();
+        String token = Jwts.builder().claim("roles", new String[]{userA, 
userB}).signWith(secretKey).compact();
 
         MultiRolesTokenAuthorizationProvider provider = new 
MultiRolesTokenAuthorizationProvider();
         ServiceConfiguration conf = new ServiceConfiguration();
@@ -84,7 +84,7 @@ public class MultiRolesTokenAuthorizationProviderTest {
     @Test
     public void testMultiRolesAuthzWithEmptyRoles() throws Exception {
         SecretKey secretKey = 
AuthTokenUtils.createSecretKey(SignatureAlgorithm.HS256);
-        String token = Jwts.builder().claim("sub", new 
String[]{}).signWith(secretKey).compact();
+        String token = Jwts.builder().claim("roles", new 
String[]{}).signWith(secretKey).compact();
 
         MultiRolesTokenAuthorizationProvider provider = new 
MultiRolesTokenAuthorizationProvider();
         ServiceConfiguration conf = new ServiceConfiguration();
@@ -113,7 +113,7 @@ public class MultiRolesTokenAuthorizationProviderTest {
     public void testMultiRolesAuthzWithSingleRole() throws Exception {
         SecretKey secretKey = 
AuthTokenUtils.createSecretKey(SignatureAlgorithm.HS256);
         String testRole = "test-role";
-        String token = Jwts.builder().claim("sub", 
testRole).signWith(secretKey).compact();
+        String token = Jwts.builder().claim("roles", 
testRole).signWith(secretKey).compact();
 
         MultiRolesTokenAuthorizationProvider provider = new 
MultiRolesTokenAuthorizationProvider();
         ServiceConfiguration conf = new ServiceConfiguration();
@@ -147,7 +147,7 @@ public class MultiRolesTokenAuthorizationProviderTest {
     public void testMultiRolesAuthzWithoutClaim() throws Exception {
         final SecretKey secretKey = 
AuthTokenUtils.createSecretKey(SignatureAlgorithm.HS256);
         final String testRole = "test-role";
-        // broker will use "sub" as the claim by default.
+        // broker will use "roles" as the claim by default.
         final String token = Jwts.builder()
                 .claim("whatever", testRole).signWith(secretKey).compact();
         ServiceConfiguration conf = new ServiceConfiguration();
@@ -268,7 +268,7 @@ public class MultiRolesTokenAuthorizationProviderTest {
     public void testMultiRolesAuthzWithSuperUser() throws Exception {
         SecretKey secretKey = 
AuthTokenUtils.createSecretKey(SignatureAlgorithm.HS256);
         String testAdminRole = "admin";
-        String token = Jwts.builder().claim("sub", 
testAdminRole).signWith(secretKey).compact();
+        String token = Jwts.builder().claim("roles", 
testAdminRole).signWith(secretKey).compact();
 
         ServiceConfiguration conf = new ServiceConfiguration();
         conf.setSuperUserRoles(Set.of(testAdminRole));
diff --git 
a/pulsar-broker/src/main/java/org/apache/pulsar/utils/auth/tokens/TokensCliUtils.java
 
b/pulsar-broker/src/main/java/org/apache/pulsar/utils/auth/tokens/TokensCliUtils.java
index 6f718601646..b2cf1f2006f 100644
--- 
a/pulsar-broker/src/main/java/org/apache/pulsar/utils/auth/tokens/TokensCliUtils.java
+++ 
b/pulsar-broker/src/main/java/org/apache/pulsar/utils/auth/tokens/TokensCliUtils.java
@@ -20,7 +20,7 @@ package org.apache.pulsar.utils.auth.tokens;
 
 import com.google.common.annotations.VisibleForTesting;
 import io.jsonwebtoken.Claims;
-import io.jsonwebtoken.Jwt;
+import io.jsonwebtoken.Jws;
 import io.jsonwebtoken.Jwts;
 import io.jsonwebtoken.SignatureAlgorithm;
 import io.jsonwebtoken.io.Decoders;
@@ -291,7 +291,7 @@ public class TokensCliUtils {
             }
 
             // Validate the token
-            Jwt<?, Claims> jwt = Jwts.parserBuilder()
+            Jws<Claims> jwt = Jwts.parser()
                     .setSigningKey(validationKey)
                     .build()
                     .parseClaimsJws(token);
diff --git 
a/pulsar-broker/src/test/java/org/apache/pulsar/utils/auth/tokens/TokensCliUtilsTest.java
 
b/pulsar-broker/src/test/java/org/apache/pulsar/utils/auth/tokens/TokensCliUtilsTest.java
index 005b160887e..aae379dae14 100644
--- 
a/pulsar-broker/src/test/java/org/apache/pulsar/utils/auth/tokens/TokensCliUtilsTest.java
+++ 
b/pulsar-broker/src/test/java/org/apache/pulsar/utils/auth/tokens/TokensCliUtilsTest.java
@@ -21,8 +21,8 @@ package org.apache.pulsar.utils.auth.tokens;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertTrue;
 import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jws;
 import io.jsonwebtoken.JwsHeader;
-import io.jsonwebtoken.Jwt;
 import io.jsonwebtoken.Jwts;
 import io.jsonwebtoken.io.Decoders;
 import java.io.ByteArrayOutputStream;
@@ -73,12 +73,12 @@ public class TokensCliUtilsTest {
             };
 
             new TokensCliUtils().execute(command);
-            String token = baoStream.toString();
+            String token = baoStream.toString().trim();
 
-            Jwt<?, ?> jwt = Jwts.parserBuilder()
+            Jws<Claims> jwt = Jwts.parser()
                     .setSigningKey(Decoders.BASE64.decode(secretKey))
                     .build()
-                    .parseClaimsJws(token);
+                    .parseSignedClaims(token);
 
             JwsHeader header = (JwsHeader) jwt.getHeader();
             String keyId = header.getKeyId();
@@ -108,13 +108,13 @@ public class TokensCliUtilsTest {
             };
 
             new TokensCliUtils().execute(command);
-            String token = baoStream.toString();
+            String token = baoStream.toString().trim();
 
             Instant start = (new Date().toInstant().plus(expireAsSec - 5, 
ChronoUnit.SECONDS));
             Instant stop = (new Date().toInstant().plus(expireAsSec + 5, 
ChronoUnit.SECONDS));
 
             //Act
-            Claims jwt = Jwts.parserBuilder()
+            Claims jwt = Jwts.parser()
                     
.setSigningKey(Decoders.BASE64.decode("u+FxaxYWpsTfxeEmMh8fQeS3g2jfXw4+sGIv+PTY+BY="))
                     .build()
                     .parseClaimsJws(token)

Reply via email to