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

rzo1 pushed a commit to branch tomee-10.x
in repository https://gitbox.apache.org/repos/asf/tomee.git

commit 5fd2b10bdbb3ff68aa705d19f6b44a2a1d754b40
Author: Richard Zowalla <[email protected]>
AuthorDate: Fri Sep 25 20:59:16 2026 +0200

    TOMEE-4029 verify tokens without kid against all configured JWKS keys
    
    (cherry picked from commit f951316a2eeb9f41ad5573ed682ea6f650747999)
---
 .../apache/tomee/microprofile/jwt/MPJWTFilter.java |  5 +-
 .../jwt/MultipleKeysWithoutKidTest.java            | 84 ++++++++++++++++++++++
 2 files changed, 88 insertions(+), 1 deletion(-)

diff --git 
a/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/MPJWTFilter.java 
b/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/MPJWTFilter.java
index 7d03074bb6..5108c49d48 100644
--- a/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/MPJWTFilter.java
+++ b/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/MPJWTFilter.java
@@ -442,7 +442,10 @@ public class MPJWTFilter implements Filter {
                     final Key key = publicKeys.values().iterator().next();
                     builder.setVerificationKey(key);
                 } else if (publicKeys.size() > 1) {
-                    builder.setVerificationKeyResolver(new 
JwksVerificationKeyResolver(asJwks(publicKeys)));
+                    final JwksVerificationKeyResolver resolver = new 
JwksVerificationKeyResolver(asJwks(publicKeys));
+                    // the kid header is optional, without it try the keys 
against the signature
+                    resolver.setDisambiguateWithVerifySignature(true);
+                    builder.setVerificationKeyResolver(resolver);
                 }
 
                 final Map<String, Key> decryptKeys;
diff --git 
a/mp-jwt/src/test/java/org/apache/tomee/microprofile/jwt/MultipleKeysWithoutKidTest.java
 
b/mp-jwt/src/test/java/org/apache/tomee/microprofile/jwt/MultipleKeysWithoutKidTest.java
new file mode 100644
index 0000000000..57704fa7e3
--- /dev/null
+++ 
b/mp-jwt/src/test/java/org/apache/tomee/microprofile/jwt/MultipleKeysWithoutKidTest.java
@@ -0,0 +1,84 @@
+/*
+ *     Licensed to the Apache Software Foundation (ASF) under one or more
+ *     contributor license agreements.  See the NOTICE file distributed with
+ *     this work for additional information regarding copyright ownership.
+ *     The ASF licenses this file to You under the Apache License, Version 2.0
+ *     (the "License"); you may not use this file except in compliance with
+ *     the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *     Unless required by applicable law or agreed to in writing, software
+ *     distributed under the License is distributed on an "AS IS" BASIS,
+ *     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *     See the License for the specific language governing permissions and
+ *     limitations under the License.
+ */
+package org.apache.tomee.microprofile.jwt;
+
+import org.apache.tomee.microprofile.jwt.config.JWTAuthConfiguration;
+import org.eclipse.microprofile.jwt.JsonWebToken;
+import org.jose4j.jwk.RsaJsonWebKey;
+import org.jose4j.jwk.RsaJwkGenerator;
+import org.jose4j.jws.AlgorithmIdentifiers;
+import org.jose4j.jws.JsonWebSignature;
+import org.jose4j.jwt.JwtClaims;
+import org.junit.Test;
+
+import java.security.Key;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+public class MultipleKeysWithoutKidTest {
+
+    @Test
+    public void tokenWithoutKidIsVerifiedAgainstAllKeys() throws Exception {
+        final RsaJsonWebKey first = RsaJwkGenerator.generateJwk(2048);
+        final RsaJsonWebKey second = RsaJwkGenerator.generateJwk(2048);
+
+        final JsonWebToken token = 
MPJWTFilter.ValidateJSonWebToken.parse(sign(second), config(first, second));
+
+        assertEquals("alice", token.getName());
+    }
+
+    @Test
+    public void tokenSignedWithUnknownKeyIsRejected() throws Exception {
+        final RsaJsonWebKey first = RsaJwkGenerator.generateJwk(2048);
+        final RsaJsonWebKey second = RsaJwkGenerator.generateJwk(2048);
+        final RsaJsonWebKey unknown = RsaJwkGenerator.generateJwk(2048);
+
+        try {
+            MPJWTFilter.ValidateJSonWebToken.parse(sign(unknown), 
config(first, second));
+            fail("token signed with an unknown key must not be accepted");
+        } catch (final ParseException expected) {
+            // ok
+        }
+    }
+
+    private static String sign(final RsaJsonWebKey key) throws Exception {
+        final JwtClaims claims = new JwtClaims();
+        claims.setSubject("alice");
+        claims.setIssuer("https://server.example.com";);
+        claims.setExpirationTimeMinutesInTheFuture(5);
+        claims.setIssuedAtToNow();
+
+        final JsonWebSignature jws = new JsonWebSignature();
+        jws.setPayload(claims.toJson());
+        jws.setKey(key.getPrivateKey());
+        jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256);
+        // no kid header on purpose
+        return jws.getCompactSerialization();
+    }
+
+    private static JWTAuthConfiguration config(final RsaJsonWebKey... keys) {
+        final Map<String, Key> publicKeys = new LinkedHashMap<>();
+        for (int i = 0; i < keys.length; i++) {
+            publicKeys.put("key" + i, keys[i].getPublicKey());
+        }
+        return new JWTAuthConfiguration(() -> publicKeys, 
"https://server.example.com";, false, new String[0],
+                LinkedHashMap::new, "Authorization", null, null, null, null, 
0);
+    }
+}

Reply via email to