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

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


The following commit(s) were added to refs/heads/master by this push:
     new 2629b08c5 KNOX-3150 - Add support for caching JWKS keys (#1044)
2629b08c5 is described below

commit 2629b08c5ac45211717763b1ecfa26eac7118ec7
Author: Sandeep MorĂ© <[email protected]>
AuthorDate: Wed May 14 00:24:55 2025 -0400

    KNOX-3150 - Add support for caching JWKS keys (#1044)
---
 .../gateway/config/impl/GatewayConfigImpl.java     | 19 +++++
 .../token/impl/DefaultTokenAuthorityService.java   |  5 ++
 .../services/token/impl/JWKSourceBuilderTest.java  | 92 ++++++++++++++++++++++
 .../org/apache/knox/gateway/GatewayTestConfig.java | 10 +++
 .../apache/knox/gateway/config/GatewayConfig.java  | 12 +++
 5 files changed, 138 insertions(+)

diff --git 
a/gateway-server/src/main/java/org/apache/knox/gateway/config/impl/GatewayConfigImpl.java
 
b/gateway-server/src/main/java/org/apache/knox/gateway/config/impl/GatewayConfigImpl.java
index 2caef51d4..8b9f8d5ce 100644
--- 
a/gateway-server/src/main/java/org/apache/knox/gateway/config/impl/GatewayConfigImpl.java
+++ 
b/gateway-server/src/main/java/org/apache/knox/gateway/config/impl/GatewayConfigImpl.java
@@ -357,6 +357,15 @@ public class GatewayConfigImpl extends Configuration 
implements GatewayConfig {
 
   private static final String JWKS_OUTAGE_CACHE_TTL = 
GATEWAY_CONFIG_FILE_PREFIX + ".jwks.outage.cache.ttl";
   private static final long JWKS_OUTAGE_CACHE_TTL_DEFAULT = 
TimeUnit.HOURS.toMillis(2);
+
+  private static final String JWKS_CACHE_TTL = GATEWAY_CONFIG_FILE_PREFIX + 
".jwks.cache.ttl";
+  private static final String JWKS_CACHE_REFRESH_TIMEOUT = 
GATEWAY_CONFIG_FILE_PREFIX + ".jwks.cache.refresh.interval";
+  /* The default time to live of cached JWK sets, in milliseconds. Set to 20 
minutes. */
+  private static final long JWKS_CACHE_TTL_DEFAULT = 20 * 60 * 1000;
+  /* The default refresh timeout of cached JWK sets, in milliseconds. Set to 
15 seconds. */
+  private static final long JWKS_CACHE_REFRESH_TIMEOUT_DEFAULT = 15 * 1000;
+
+
   private static final String ISSUER_IGNORE_TYPE_VALIDATION = 
GATEWAY_CONFIG_FILE_PREFIX + ".token.issuers.ignore.type.validation";
 
   //Strict-Transport Option
@@ -1627,6 +1636,16 @@ public class GatewayConfigImpl extends Configuration 
implements GatewayConfig {
     return getLong(JWKS_OUTAGE_CACHE_TTL, JWKS_OUTAGE_CACHE_TTL_DEFAULT);
   }
 
+  @Override
+  public long getJwksCacheTimeToLive(){
+    return getLong(JWKS_CACHE_TTL, JWKS_CACHE_TTL_DEFAULT);
+  }
+
+  @Override
+  public long getJwksCacheRefreshTimeout(){
+    return getLong(JWKS_CACHE_REFRESH_TIMEOUT, 
JWKS_CACHE_REFRESH_TIMEOUT_DEFAULT);
+  }
+
   @Override
   public boolean isStrictTransportEnabled() {
     return getBoolean(STRICT_TRANSPORT_ENABLED, 
DEFAULT_STRICT_TRANSPORT_ENABLED);
diff --git 
a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/DefaultTokenAuthorityService.java
 
b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/DefaultTokenAuthorityService.java
index d299870e8..65e9f0ce1 100644
--- 
a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/DefaultTokenAuthorityService.java
+++ 
b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/DefaultTokenAuthorityService.java
@@ -229,7 +229,12 @@ public class DefaultTokenAuthorityService implements 
JWTokenAuthority, Service {
         JWSAlgorithm expectedJWSAlg = JWSAlgorithm.parse(algorithm);
         /* Retry one time in case of failure and cache JWKS in case there is 
outage, TTL is OUTAGE_TTL */
         long outageTTL = config.getJwksOutageCacheTTL();
+        long cacheTTL = config.getJwksCacheTimeToLive();
+        long cacheTimeOut = config.getJwksCacheRefreshTimeout();
+
         JWKSource<SecurityContext> keySource = JWKSourceBuilder.create(new 
URL(jwksurl))
+                .cache(cacheTTL, cacheTimeOut)
+                .refreshAheadCache(true)
                 .retrying(true)
                 .outageTolerant(outageTTL)
                 .build();
diff --git 
a/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/JWKSourceBuilderTest.java
 
b/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/JWKSourceBuilderTest.java
new file mode 100644
index 000000000..01dc6ca41
--- /dev/null
+++ 
b/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/JWKSourceBuilderTest.java
@@ -0,0 +1,92 @@
+/*
+ * 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.knox.gateway.services.token.impl;
+
+import com.nimbusds.jose.JOSEObjectType;
+import org.apache.knox.gateway.config.GatewayConfig;
+import org.apache.knox.gateway.services.security.AliasService;
+import org.apache.knox.gateway.services.security.KeystoreService;
+import org.apache.knox.gateway.services.security.token.TokenServiceException;
+import org.apache.knox.gateway.services.security.token.impl.JWT;
+import org.easymock.EasyMock;
+import org.junit.Test;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Set;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * Test class for verifying the JWKSourceBuilder parameters in 
DefaultTokenAuthorityService.
+ */
+public class JWKSourceBuilderTest {
+
+    /**
+     * Test that the cacheTTL and cacheTimeOut parameters are correctly passed 
to JWKSourceBuilder.
+     * <p>
+     * This test verifies that the DefaultTokenAuthorityService correctly uses 
the values from
+     * GatewayConfig when building the JWKSource.
+     */
+    @Test
+    public void testJWKSourceBuilderParameters() throws Exception {
+        // Define custom cache TTL and timeout values
+        final long customCacheTTL = 60000; // 1 minute
+        final long customCacheTimeout = 5000; // 5 seconds
+        final long customOutageTTL = 7200000; // 2 hours
+        // Create a mock GatewayConfig that returns our custom values
+        GatewayConfig config = EasyMock.createNiceMock(GatewayConfig.class);
+        
EasyMock.expect(config.getJwksCacheTimeToLive()).andReturn(customCacheTTL).anyTimes();
+        
EasyMock.expect(config.getJwksCacheRefreshTimeout()).andReturn(customCacheTimeout).anyTimes();
+        
EasyMock.expect(config.getJwksOutageCacheTTL()).andReturn(customOutageTTL).anyTimes();
+        
EasyMock.expect(config.getIssuersWithIgnoredTypeHeader()).andReturn(Collections.emptySet()).anyTimes();
+        // Mock the necessary services
+        AliasService aliasService = 
EasyMock.createNiceMock(AliasService.class);
+        KeystoreService keystoreService = 
EasyMock.createNiceMock(KeystoreService.class);
+        // Replay all mocks
+        EasyMock.replay(config, aliasService, keystoreService);
+        // Create the DefaultTokenAuthorityService
+        DefaultTokenAuthorityService service = new 
DefaultTokenAuthorityService();
+        service.setAliasService(aliasService);
+        service.setKeystoreService(keystoreService);
+        service.init(config, new HashMap<>());
+        // Create a test JWT token
+        JWT token = EasyMock.createNiceMock(JWT.class);
+        EasyMock.expect(token.getIssuer()).andReturn("test-issuer").anyTimes();
+        EasyMock.replay(token);
+        // Define the JWK URL and algorithm
+        String jwksUrl = "https://example.com/.well-known/jwks.json";;
+        String algorithm = "RS256";
+        Set<JOSEObjectType> allowedJwsTypes = new HashSet<>();
+        allowedJwsTypes.add(JOSEObjectType.JWT);
+        try {
+            // This will throw an exception because the URL doesn't exist, but 
we're only
+            // interested in verifying that the correct parameters are passed 
to JWKSourceBuilder
+            service.verifyToken(token, jwksUrl, algorithm, allowedJwsTypes);
+            fail("Expected TokenServiceException");
+        } catch (TokenServiceException e) {
+            // Expected exception because the URL doesn't exist
+            // Verify that the exception message indicates an attempt to 
connect to the URL
+            assertTrue(e.getMessage().contains("Cannot verify token"));
+        }
+        // Verify all mocks
+        EasyMock.verify(config, aliasService, keystoreService, token);
+    }
+}
diff --git 
a/gateway-spi-common/src/main/java/org/apache/knox/gateway/GatewayTestConfig.java
 
b/gateway-spi-common/src/main/java/org/apache/knox/gateway/GatewayTestConfig.java
index ae8c65620..c1d2f80cc 100644
--- 
a/gateway-spi-common/src/main/java/org/apache/knox/gateway/GatewayTestConfig.java
+++ 
b/gateway-spi-common/src/main/java/org/apache/knox/gateway/GatewayTestConfig.java
@@ -1142,6 +1142,16 @@ public class GatewayTestConfig extends Configuration 
implements GatewayConfig {
     return TimeUnit.HOURS.toMillis(2);
   }
 
+  @Override
+  public long getJwksCacheTimeToLive() {
+    return 0;
+  }
+
+  @Override
+  public long getJwksCacheRefreshTimeout() {
+    return 0;
+  }
+
 
   @Override
   public Set<String> getIssuersWithIgnoredTypeHeader() {
diff --git 
a/gateway-spi/src/main/java/org/apache/knox/gateway/config/GatewayConfig.java 
b/gateway-spi/src/main/java/org/apache/knox/gateway/config/GatewayConfig.java
index dd7b2441d..f92b8daec 100644
--- 
a/gateway-spi/src/main/java/org/apache/knox/gateway/config/GatewayConfig.java
+++ 
b/gateway-spi/src/main/java/org/apache/knox/gateway/config/GatewayConfig.java
@@ -971,6 +971,18 @@ public interface GatewayConfig {
    */
   long getJwksOutageCacheTTL();
 
+  /**
+   * The time to live of the cached JWK set, in milliseconds.
+   * @return jwks cache TTL
+   */
+  long getJwksCacheTimeToLive();
+
+  /**
+   * The cache refresh timeout, in milliseconds.
+   * @return
+   */
+  long getJwksCacheRefreshTimeout();
+
   /**
    * Some JWT tokens could be missing typ header.
    * This config skips typ validation for tokens issued by

Reply via email to