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

oscerd pushed a commit to branch backport/24032-to-camel-4.18.x
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 9d91376d98c28152a74368c44b2ad515537009ba
Author: Andrea Cosentino <[email protected]>
AuthorDate: Wed Jun 17 11:26:26 2026 +0200

    CAMEL-23760: camel-oauth - require a JWK set to verify token signatures in 
UserProfile
    
    Backport of #24032 to camel-4.18.x. The upgrade-guide entry for this
    change already lives on the 4_18 guide on main (#24044).
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
---
 .../java/org/apache/camel/oauth/UserProfile.java   | 21 ++---
 .../org/apache/camel/oauth/UserProfileTest.java    | 94 ++++++++++++++++++++++
 2 files changed, 105 insertions(+), 10 deletions(-)

diff --git 
a/components/camel-oauth/src/main/java/org/apache/camel/oauth/UserProfile.java 
b/components/camel-oauth/src/main/java/org/apache/camel/oauth/UserProfile.java
index 06c756ad2eda..082e85f02ba8 100644
--- 
a/components/camel-oauth/src/main/java/org/apache/camel/oauth/UserProfile.java
+++ 
b/components/camel-oauth/src/main/java/org/apache/camel/oauth/UserProfile.java
@@ -162,17 +162,18 @@ public class UserProfile {
             var signedJWT = SignedJWT.parse(token);
             var keyID = signedJWT.getHeader().getKeyID();
 
-            // Fetch Keycloak public key
+            // Resolve the signing key from the configured JWK set and verify 
the token signature
             var jwkSet = config.getJWKSet();
-            if (!jwkSet.isEmpty()) {
-                var rsaKey = (RSAKey) jwkSet.getKeyByKeyId(keyID);
-                if (rsaKey == null) {
-                    throw new OAuthException("No matching key found for: " + 
keyID);
-                }
-                RSAPublicKey publicKey = rsaKey.toRSAPublicKey();
-                if (!signedJWT.verify(new RSASSAVerifier(publicKey))) {
-                    throw new OAuthException("Invalid token signature");
-                }
+            if (jwkSet == null || jwkSet.isEmpty()) {
+                throw new OAuthException("Cannot verify token signature: no 
JWK set available");
+            }
+            var rsaKey = (RSAKey) jwkSet.getKeyByKeyId(keyID);
+            if (rsaKey == null) {
+                throw new OAuthException("No matching key found for: " + 
keyID);
+            }
+            RSAPublicKey publicKey = rsaKey.toRSAPublicKey();
+            if (!signedJWT.verify(new RSASSAVerifier(publicKey))) {
+                throw new OAuthException("Invalid token signature");
             }
 
             // Decode the payload into a JsonObject
diff --git 
a/components/camel-oauth/src/test/java/org/apache/camel/oauth/UserProfileTest.java
 
b/components/camel-oauth/src/test/java/org/apache/camel/oauth/UserProfileTest.java
new file mode 100644
index 000000000000..a246c259880b
--- /dev/null
+++ 
b/components/camel-oauth/src/test/java/org/apache/camel/oauth/UserProfileTest.java
@@ -0,0 +1,94 @@
+/*
+ * 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.camel.oauth;
+
+import java.util.Date;
+
+import com.google.gson.JsonObject;
+import com.nimbusds.jose.JWSAlgorithm;
+import com.nimbusds.jose.JWSHeader;
+import com.nimbusds.jose.crypto.RSASSASigner;
+import com.nimbusds.jose.jwk.JWKSet;
+import com.nimbusds.jose.jwk.RSAKey;
+import com.nimbusds.jose.jwk.gen.RSAKeyGenerator;
+import com.nimbusds.jwt.JWTClaimsSet;
+import com.nimbusds.jwt.SignedJWT;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+class UserProfileTest {
+
+    private static final String KID = "test-key-1";
+
+    private RSAKey rsaKey;
+
+    @BeforeEach
+    void setUp() throws Exception {
+        rsaKey = new RSAKeyGenerator(2048).keyID(KID).generate();
+    }
+
+    @Test
+    void accessTokenRejectedWhenJwkSetEmpty() throws Exception {
+        JsonObject json = new JsonObject();
+        json.addProperty("access_token", signedToken());
+
+        // A present-but-empty JWK set means the signature cannot be verified; 
the token must not be trusted.
+        OAuthConfig config = new OAuthConfig().setClientId("my-client");
+        config.setJWKSet(new JWKSet());
+
+        assertThrows(OAuthException.class, () -> UserProfile.fromJson(config, 
json));
+    }
+
+    @Test
+    void accessTokenRejectedWhenJwkSetMissing() throws Exception {
+        JsonObject json = new JsonObject();
+        json.addProperty("access_token", signedToken());
+
+        // No JWK set configured at all: still must not trust an unverified 
token.
+        OAuthConfig config = new OAuthConfig().setClientId("my-client");
+
+        assertThrows(OAuthException.class, () -> UserProfile.fromJson(config, 
json));
+    }
+
+    @Test
+    void accessTokenAcceptedWhenSignatureVerifies() throws Exception {
+        JsonObject json = new JsonObject();
+        json.addProperty("access_token", signedToken());
+
+        OAuthConfig config = new OAuthConfig().setClientId("my-client");
+        config.setJWKSet(new JWKSet(rsaKey.toPublicJWK()));
+
+        UserProfile profile = UserProfile.fromJson(config, json);
+        assertNotNull(profile);
+    }
+
+    private String signedToken() throws Exception {
+        JWTClaimsSet claims = new JWTClaimsSet.Builder()
+                .subject("user1")
+                .issuer("https://idp.example.com";)
+                .audience("my-client")
+                .expirationTime(new Date(System.currentTimeMillis() + 
300_000L))
+                .issueTime(new Date())
+                .build();
+        SignedJWT jwt = new SignedJWT(new 
JWSHeader.Builder(JWSAlgorithm.RS256).keyID(KID).build(), claims);
+        jwt.sign(new RSASSASigner(rsaKey));
+        return jwt.serialize();
+    }
+}

Reply via email to