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 06f05b499 KNOX-3040 - Followup patch. Support for multiple JWKS 
endpoints + code cleanup (#924)
06f05b499 is described below

commit 06f05b499d5ca71411f9dd79f1782eed58f61bd9
Author: Sandeep MorĂ© <[email protected]>
AuthorDate: Tue Jul 30 10:30:39 2024 -0400

    KNOX-3040 - Followup patch. Support for multiple JWKS endpoints + code 
cleanup (#924)
    
    * KNOX-3040 - Followup patch. Support for multiple JWKS endpoints + code 
cleanup
    
    * Review changes
    
    * Update main.yml
    
    Bumping github actions for checkout and setup-java to v4
    
    * Update main.yml
    
    added distribution and cache attributes
    
    * Update main.yml
    
    simplify mvn cmd
    
    * KNOX-3053 - Fix DefaultTopologyService - Mock setup moved before method 
call (#927)
    
    * KNOX-3040-1 - Fix unit test
    
    ---------
    
    Co-authored-by: lmccay <[email protected]>
    Co-authored-by: hanicz <[email protected]>
---
 .../hadoopauth/filter/HadoopAuthFilterTest.java    |  1 +
 .../provider/federation/jwt/JWTMessages.java       |  3 ++
 .../federation/jwt/filter/AbstractJWTFilter.java   |  7 ++--
 .../federation/jwt/filter/JWTFederationFilter.java | 37 +++++++++++++++++++++-
 .../provider/federation/AbstractJWTFilterTest.java |  7 ++++
 .../gateway/config/impl/GatewayConfigImpl.java     | 10 +++++-
 .../token/impl/DefaultTokenAuthorityService.java   | 29 +++++++++++++++--
 .../token/impl/TokenAuthorityServiceMessages.java  |  3 ++
 .../service/knoxsso/WebSSOResourceTest.java        |  6 ++++
 .../knoxtoken/TokenServiceResourceTest.java        |  7 ++++
 .../org/apache/knox/gateway/GatewayTestConfig.java |  7 ++++
 .../apache/knox/gateway/config/GatewayConfig.java  |  5 +++
 .../services/security/token/JWTokenAuthority.java  |  4 +++
 .../services/security/token/impl/JWTToken.java     | 34 ++++++++++----------
 .../services/security/token/impl/JWTTokenTest.java |  8 ++---
 pom.xml                                            |  2 +-
 16 files changed, 140 insertions(+), 30 deletions(-)

diff --git 
a/gateway-provider-security-hadoopauth/src/test/java/org/apache/knox/gateway/hadoopauth/filter/HadoopAuthFilterTest.java
 
b/gateway-provider-security-hadoopauth/src/test/java/org/apache/knox/gateway/hadoopauth/filter/HadoopAuthFilterTest.java
index 8b1755fd7..c06876524 100644
--- 
a/gateway-provider-security-hadoopauth/src/test/java/org/apache/knox/gateway/hadoopauth/filter/HadoopAuthFilterTest.java
+++ 
b/gateway-provider-security-hadoopauth/src/test/java/org/apache/knox/gateway/hadoopauth/filter/HadoopAuthFilterTest.java
@@ -582,6 +582,7 @@ public class HadoopAuthFilterTest {
       
expect(filterConfig.getInitParameter(JWTFederationFilter.KNOX_TOKEN_AUDIENCES)).andReturn(null).anyTimes();
       
expect(filterConfig.getInitParameter(JWTFederationFilter.KNOX_TOKEN_QUERY_PARAM_NAME)).andReturn(null).anyTimes();
       
expect(filterConfig.getInitParameter(JWTFederationFilter.JWKS_URL)).andReturn(null).anyTimes();
+      
expect(filterConfig.getInitParameter(JWTFederationFilter.JWKS_URLS)).andReturn(null).anyTimes();
       
expect(filterConfig.getInitParameter(JWTFederationFilter.TOKEN_PRINCIPAL_CLAIM)).andReturn(null).anyTimes();
       
expect(filterConfig.getInitParameter(JWTFederationFilter.TOKEN_VERIFICATION_PEM)).andReturn(null).anyTimes();
       
expect(filterConfig.getInitParameter(JWTFederationFilter.JWT_UNAUTHENTICATED_PATHS_PARAM)).andReturn(null).anyTimes();
diff --git 
a/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/JWTMessages.java
 
b/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/JWTMessages.java
index d41ca2d0a..b5ba653f3 100644
--- 
a/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/JWTMessages.java
+++ 
b/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/JWTMessages.java
@@ -123,4 +123,7 @@ public interface JWTMessages {
 
   @Message( level = MessageLevel.INFO, text = "Token verification result using 
knox signing cert, verified: {0}" )
   void signingKeyVerificationResultMessage(boolean verified);
+
+  @Message(level = MessageLevel.ERROR, text = "Invalid URL ignored. Not a 
valid JWKS url {0}")
+  void invalidJwksUrl(String jwksUrl);
 }
diff --git 
a/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/AbstractJWTFilter.java
 
b/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/AbstractJWTFilter.java
index e9daff9b1..fb1517a73 100644
--- 
a/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/AbstractJWTFilter.java
+++ 
b/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/AbstractJWTFilter.java
@@ -20,6 +20,7 @@ package 
org.apache.knox.gateway.provider.federation.jwt.filter;
 import static java.nio.charset.StandardCharsets.UTF_8;
 
 import java.io.IOException;
+import java.net.URI;
 import java.security.Principal;
 import java.security.PrivilegedActionException;
 import java.security.PrivilegedExceptionAction;
@@ -115,7 +116,7 @@ public abstract class AbstractJWTFilter implements Filter {
   private String expectedIssuer;
   private String expectedSigAlg;
   protected String expectedPrincipalClaim;
-  protected String expectedJWKSUrl;
+  protected Set<URI> expectedJWKSUrls = new HashSet();
   protected Set<JOSEObjectType> allowedJwsTypes;
 
   private TokenStateService tokenStateService;
@@ -516,8 +517,8 @@ public abstract class AbstractJWTFilter implements Filter {
           log.pemVerificationResultMessage(verified);
         }
 
-        if (!verified && expectedJWKSUrl != null) {
-          verified = authority.verifyToken(token, expectedJWKSUrl, 
expectedSigAlg, allowedJwsTypes);
+        if (!verified && expectedJWKSUrls != null && 
!expectedJWKSUrls.isEmpty()) {
+          verified = authority.verifyToken(token, expectedJWKSUrls, 
expectedSigAlg, allowedJwsTypes);
           log.jwksVerificationResultMessage(verified);
         }
 
diff --git 
a/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/JWTFederationFilter.java
 
b/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/JWTFederationFilter.java
index a58622a0e..e3ac3967e 100644
--- 
a/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/JWTFederationFilter.java
+++ 
b/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/JWTFederationFilter.java
@@ -23,14 +23,18 @@ import static 
org.apache.knox.gateway.util.AuthFilterUtils.DEFAULT_AUTH_UNAUTHEN
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStreamReader;
+import java.net.URI;
+import java.net.URISyntaxException;
 import java.net.URLDecoder;
 import java.nio.charset.StandardCharsets;
 import java.text.ParseException;
+import java.util.Arrays;
 import java.util.Base64;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
 import java.util.Set;
+import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
 import javax.security.auth.Subject;
@@ -76,6 +80,7 @@ public class JWTFederationFilter extends AbstractJWTFilter {
   public static final String KNOX_TOKEN_QUERY_PARAM_NAME = 
"knox.token.query.param.name";
   public static final String TOKEN_PRINCIPAL_CLAIM = 
"knox.token.principal.claim";
   public static final String JWKS_URL = "knox.token.jwks.url";
+  public static final String JWKS_URLS = "knox.token.jwks.urls";
   public static final String ALLOWED_JWS_TYPES = 
"knox.token.allowed.jws.types";
   public static final String BEARER   = "Bearer ";
   public static final String BASIC    = "Basic";
@@ -110,7 +115,13 @@ public class JWTFederationFilter extends AbstractJWTFilter 
{
     //  JWKSUrl
     String oidcjwksurl = filterConfig.getInitParameter(JWKS_URL);
     if (oidcjwksurl != null) {
-      expectedJWKSUrl = oidcjwksurl;
+      expectedJWKSUrls = parseJwksUrlsFromConfig(oidcjwksurl);
+    }
+
+    /* in case knox.token.jwks.urls property is defined use it */
+    final String oidcjwksurls = filterConfig.getInitParameter(JWKS_URLS);
+    if (oidcjwksurls != null) {
+      expectedJWKSUrls.addAll(parseJwksUrlsFromConfig(oidcjwksurls));
     }
 
     allowedJwsTypes = new HashSet<>();
@@ -149,6 +160,30 @@ public class JWTFederationFilter extends AbstractJWTFilter 
{
     configureExpectedParameters(filterConfig);
   }
 
+  /**
+   * Helper function to extract URLs from given string
+   * in the form of https://url:port/contxt/.wellknown, 
https://url2:port/contxt/.wellknown
+   * into expectedJWKSUrl URL set.
+   * @param oidcjwksurls
+   */
+  private Set<URI> parseJwksUrlsFromConfig(final String oidcjwksurls) {
+    final Set<URI> jwksUrlSet = new HashSet<>();
+    final Set<String> jwksurls = Arrays.stream(
+            oidcjwksurls.split(","))
+            .map(String::trim)
+            .collect(Collectors.toSet());
+
+    for (final String jwksurl : jwksurls) {
+        try {
+          jwksUrlSet.add(new URI(jwksurl));
+        } catch (URISyntaxException e) {
+          /* Not valid JWKS url, log and move on */
+          log.invalidJwksUrl(jwksurl);
+        }
+    }
+    return jwksUrlSet;
+  }
+
   @Override
   public void destroy() {
   }
diff --git 
a/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/AbstractJWTFilterTest.java
 
b/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/AbstractJWTFilterTest.java
index 384468a3a..bb255a19c 100644
--- 
a/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/AbstractJWTFilterTest.java
+++ 
b/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/AbstractJWTFilterTest.java
@@ -33,6 +33,7 @@ import 
org.apache.knox.gateway.provider.federation.jwt.filter.SignatureVerificat
 import org.apache.knox.gateway.security.PrimaryPrincipal;
 import org.apache.knox.gateway.services.security.token.JWTokenAttributes;
 import org.apache.knox.gateway.services.security.token.JWTokenAuthority;
+import org.apache.knox.gateway.services.security.token.TokenServiceException;
 import org.apache.knox.gateway.services.security.token.impl.JWT;
 import org.apache.knox.gateway.services.security.token.impl.JWTToken;
 import org.apache.knox.gateway.util.X509CertificateUtil;
@@ -55,6 +56,7 @@ import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.lang.reflect.Field;
 import java.net.InetAddress;
+import java.net.URI;
 import java.nio.charset.StandardCharsets;
 import java.security.AccessController;
 import java.security.KeyPair;
@@ -1180,6 +1182,11 @@ public abstract class AbstractJWTFilterTest  {
     public boolean verifyToken(JWT token, String jwksurl, String algorithm, 
Set<JOSEObjectType> allowedJwsTypes) {
      return false;
     }
+
+    @Override
+    public boolean verifyToken(JWT token, Set<URI> jwksurls, String algorithm, 
Set<JOSEObjectType> allowedJwsTypes) throws TokenServiceException {
+      return false;
+    }
   }
 
   protected static class TestFilterChain implements FilterChain {
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 1b6e967a7..5f35ff8e7 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
@@ -166,7 +166,7 @@ public class GatewayConfigImpl extends Configuration 
implements GatewayConfig {
   public static final String WEBSHELL_READ_BUFFER_SIZE = 
GATEWAY_CONFIG_FILE_PREFIX + ".webshell.read.buffer.size";
 
   /**
-   * Properties for for gateway port mapping feature
+   * Properties for gateway port mapping feature
    */
   public static final String GATEWAY_PORT_MAPPING_PREFIX = 
GATEWAY_CONFIG_FILE_PREFIX + ".port.mapping.";
   public static final String GATEWAY_PORT_MAPPING_REGEX = 
GATEWAY_CONFIG_FILE_PREFIX + "\\.port\\.mapping\\..*";
@@ -354,6 +354,9 @@ public class GatewayConfigImpl extends Configuration 
implements GatewayConfig {
 
   private static final String GATEWAY_HEALTH_CHECK_TOPOLOGIES = 
GATEWAY_CONFIG_FILE_PREFIX + ".health.check.topologies";
 
+  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);
+
   public GatewayConfigImpl() {
     init();
   }
@@ -1590,4 +1593,9 @@ public class GatewayConfigImpl extends Configuration 
implements GatewayConfig {
     return get(UI_BANNER_TEXT, "");
   }
 
+  @Override
+  public long getJwksOutageCacheTTL() {
+    return getLong(JWKS_OUTAGE_CACHE_TTL, JWKS_OUTAGE_CACHE_TTL_DEFAULT);
+  }
+
 }
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 275e2ec97..08cc297cf 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
@@ -18,6 +18,7 @@
 package org.apache.knox.gateway.services.token.impl;
 
 import java.net.MalformedURLException;
+import java.net.URI;
 import java.net.URL;
 import java.nio.charset.StandardCharsets;
 import java.security.Key;
@@ -47,7 +48,7 @@ import com.nimbusds.jose.crypto.MACVerifier;
 import com.nimbusds.jose.crypto.RSASSASigner;
 import com.nimbusds.jose.crypto.RSASSAVerifier;
 import com.nimbusds.jose.jwk.source.JWKSource;
-import com.nimbusds.jose.jwk.source.RemoteJWKSet;
+import com.nimbusds.jose.jwk.source.JWKSourceBuilder;
 import com.nimbusds.jose.proc.BadJOSEException;
 import com.nimbusds.jose.proc.DefaultJOSEObjectTypeVerifier;
 import com.nimbusds.jose.proc.JOSEObjectTypeVerifier;
@@ -226,7 +227,12 @@ public class DefaultTokenAuthorityService implements 
JWTokenAuthority, Service {
     try {
       if (algorithm != null && jwksurl != null) {
         JWSAlgorithm expectedJWSAlg = JWSAlgorithm.parse(algorithm);
-        JWKSource<SecurityContext> keySource = new RemoteJWKSet<>(new 
URL(jwksurl));
+        /* Retry one time in case of failure and cache JWKS in case there is 
outage, TTL is OUTAGE_TTL */
+        long outageTTL = config.getJwksOutageCacheTTL();
+        JWKSource<SecurityContext> keySource = JWKSourceBuilder.create(new 
URL(jwksurl))
+                .retrying(true)
+                .outageTolerant(outageTTL)
+                .build();
         JWSKeySelector<SecurityContext> keySelector = new 
JWSVerificationKeySelector<>(expectedJWSAlg, keySource);
 
         // Create a JWT processor for the access tokens
@@ -248,6 +254,25 @@ public class DefaultTokenAuthorityService implements 
JWTokenAuthority, Service {
     return verified;
   }
 
+  @Override
+  public boolean verifyToken(JWT token, Set<URI> jwksurls, String algorithm, 
Set<JOSEObjectType> allowedJwsTypes) throws TokenServiceException {
+    boolean verified = false;
+    for(final URI url : jwksurls) {
+      try {
+        verified = this.verifyToken(token, url.toString(), algorithm, 
allowedJwsTypes);
+        /* if token is verified no need to check further return result */
+        if(verified) {
+          return verified;
+        }
+      } catch (TokenServiceException e) {
+        /* failed to verify token, log and move on */
+        LOG.jwksVerificationFailed(url.toString(), e.toString());
+      }
+    }
+    return verified;
+  }
+
+
   @Override
   public void init(GatewayConfig config, Map<String, String> options)
       throws ServiceLifecycleException {
diff --git 
a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/TokenAuthorityServiceMessages.java
 
b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/TokenAuthorityServiceMessages.java
index f93c95287..4ade199e0 100644
--- 
a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/TokenAuthorityServiceMessages.java
+++ 
b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/TokenAuthorityServiceMessages.java
@@ -25,4 +25,7 @@ import org.apache.knox.gateway.i18n.messages.Messages;
 public interface TokenAuthorityServiceMessages {
   @Message(level = MessageLevel.ERROR, text = "There was an error getting kid, 
cause: {0}")
   void errorGettingKid(String message);
+
+  @Message(level = MessageLevel.ERROR, text = "Failed to verify token using 
JWKS endpoint {0}, reason: {1}")
+  void jwksVerificationFailed(String jwksUrl, String reason);
 }
diff --git 
a/gateway-service-knoxsso/src/test/java/org/apache/knox/gateway/service/knoxsso/WebSSOResourceTest.java
 
b/gateway-service-knoxsso/src/test/java/org/apache/knox/gateway/service/knoxsso/WebSSOResourceTest.java
index ecf7463ba..991af4117 100644
--- 
a/gateway-service-knoxsso/src/test/java/org/apache/knox/gateway/service/knoxsso/WebSSOResourceTest.java
+++ 
b/gateway-service-knoxsso/src/test/java/org/apache/knox/gateway/service/knoxsso/WebSSOResourceTest.java
@@ -29,6 +29,7 @@ import static org.junit.Assert.assertTrue;
 import java.lang.reflect.Field;
 import java.net.HttpCookie;
 import java.net.MalformedURLException;
+import java.net.URI;
 import java.net.URLEncoder;
 import java.nio.charset.StandardCharsets;
 import java.security.KeyPair;
@@ -1005,5 +1006,10 @@ public class WebSSOResourceTest {
     public boolean verifyToken(JWT token, String jwksurl, String algorithm, 
Set<JOSEObjectType> allowedJwsTypes) {
      return false;
     }
+
+    @Override
+    public boolean verifyToken(JWT token, Set<URI> jwksurls, String algorithm, 
Set<JOSEObjectType> allowedJwsTypes) throws TokenServiceException {
+      return false;
+    }
   }
 }
diff --git 
a/gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java
 
b/gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java
index 9ef8523cf..b3798bdd7 100644
--- 
a/gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java
+++ 
b/gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java
@@ -33,6 +33,7 @@ import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 import java.io.IOException;
+import java.net.URI;
 import java.security.KeyPair;
 import java.security.KeyPairGenerator;
 import java.security.Principal;
@@ -95,6 +96,7 @@ import 
org.apache.knox.gateway.services.security.token.JWTokenAuthority;
 import org.apache.knox.gateway.services.security.token.KnoxToken;
 import 
org.apache.knox.gateway.services.security.token.PersistentTokenStateService;
 import org.apache.knox.gateway.services.security.token.TokenMetadata;
+import org.apache.knox.gateway.services.security.token.TokenServiceException;
 import org.apache.knox.gateway.services.security.token.TokenStateService;
 import org.apache.knox.gateway.services.security.token.TokenUtils;
 import org.apache.knox.gateway.services.security.token.UnknownTokenException;
@@ -1899,5 +1901,10 @@ public class TokenServiceResourceTest {
     public boolean verifyToken(JWT token, String jwksurl, String algorithm, 
Set<JOSEObjectType> allowedJwsTypes) {
      return false;
     }
+
+    @Override
+    public boolean verifyToken(JWT token, Set<URI> jwksurls, String algorithm, 
Set<JOSEObjectType> allowedJwsTypes) throws TokenServiceException {
+      return false;
+    }
   }
 }
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 e3f2ddab5..fd26c0f2a 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
@@ -32,6 +32,7 @@ import java.util.Properties;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.TimeUnit;
 
 import org.apache.commons.lang3.StringUtils;
 import org.apache.hadoop.conf.Configuration;
@@ -1126,4 +1127,10 @@ public class GatewayTestConfig extends Configuration 
implements GatewayConfig {
     return null;
   }
 
+  @Override
+  public long getJwksOutageCacheTTL() {
+    return TimeUnit.HOURS.toMillis(2);
+  }
+
+
 }
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 352aabbe5..bdb10adf9 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
@@ -956,4 +956,9 @@ public interface GatewayConfig {
    */
   String getBannerText();
 
+  /**
+   * The time to live of the cached JWK set to cover outages, in milliseconds.
+   * @return jwks outage cache TTL
+   */
+  long getJwksOutageCacheTTL();
 }
diff --git 
a/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/JWTokenAuthority.java
 
b/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/JWTokenAuthority.java
index ccd74c714..7c1e503c6 100644
--- 
a/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/JWTokenAuthority.java
+++ 
b/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/JWTokenAuthority.java
@@ -17,6 +17,7 @@
  */
 package org.apache.knox.gateway.services.security.token;
 
+import java.net.URI;
 import java.security.interfaces.RSAPublicKey;
 import java.util.Set;
 
@@ -33,4 +34,7 @@ public interface JWTokenAuthority {
   boolean verifyToken(JWT token, RSAPublicKey publicKey) throws 
TokenServiceException;
 
   boolean verifyToken(JWT token, String jwksurl, String algorithm, 
Set<JOSEObjectType> allowedJwsTypes) throws TokenServiceException;
+
+  boolean verifyToken(JWT token, Set<URI> jwksurls, String algorithm, 
Set<JOSEObjectType> allowedJwsTypes) throws TokenServiceException;
+
 }
diff --git 
a/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/impl/JWTToken.java
 
b/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/impl/JWTToken.java
index 7920bfcf5..cd9ccc5b2 100644
--- 
a/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/impl/JWTToken.java
+++ 
b/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/impl/JWTToken.java
@@ -63,26 +63,26 @@ public class JWTToken implements JWT {
     try {
       header = new JWSHeader(new JWSAlgorithm(jwtAttributes.getAlgorithm()),
               jwtAttributes.getType() == null ? null : new 
JOSEObjectType(jwtAttributes.getType()),
-      null,
-      null,
-      jwtAttributes.getJkuUri(),
-      null,
-      null,
-      null,
-      null,
-      null,
-      jwtAttributes.getKid(),
-      null,
-      null);
+              null,
+              null,
+              jwtAttributes.getJkuUri(),
+              null,
+              null,
+              null,
+              null,
+              null,
+              jwtAttributes.getKid(),
+              null,
+              null);
     } catch (URISyntaxException e) {
       /* in event of bad URI exception fall back to using just algo in header 
*/
       header = new JWSHeader(new JWSAlgorithm(jwtAttributes.getAlgorithm()));
     }
     JWTClaimsSet claims;
     JWTClaimsSet.Builder builder = new JWTClaimsSet.Builder()
-      .issuer(jwtAttributes.getIssuer())
-      .subject(jwtAttributes.getUserName())
-      .audience(jwtAttributes.getAudiences());
+            .issuer(jwtAttributes.getIssuer())
+            .subject(jwtAttributes.getUserName())
+            .audience(jwtAttributes.getAudiences());
     if(jwtAttributes.getExpiresDate() != null) {
       builder = builder.expirationTime(jwtAttributes.getExpiresDate());
     }
@@ -127,7 +127,7 @@ public class JWTToken implements JWT {
     JWTClaimsSet claims;
     try {
       claims = jwt.getJWTClaimsSet();
-      c = claims.toJSONObject().toJSONString();
+      c = claims.toJSONObject().toString();
     } catch (ParseException e) {
       log.unableToParseToken(e);
     }
@@ -195,7 +195,7 @@ public class JWTToken implements JWT {
     String c = null;
 
     claim = getAudienceClaims();
-    if (claim != null) {
+    if (claim != null && claim.length > 0) {
       c = claim[0];
     }
 
@@ -217,7 +217,7 @@ public class JWTToken implements JWT {
 
   @Override
   public String getExpires() {
-      Date expires = getExpiresDate();
+    Date expires = getExpiresDate();
     if (expires != null) {
       return String.valueOf(expires.getTime());
     }
diff --git 
a/gateway-spi/src/test/java/org/apache/knox/gateway/services/security/token/impl/JWTTokenTest.java
 
b/gateway-spi/src/test/java/org/apache/knox/gateway/services/security/token/impl/JWTTokenTest.java
index ff0df7af4..c3099a898 100644
--- 
a/gateway-spi/src/test/java/org/apache/knox/gateway/services/security/token/impl/JWTTokenTest.java
+++ 
b/gateway-spi/src/test/java/org/apache/knox/gateway/services/security/token/impl/JWTTokenTest.java
@@ -19,7 +19,6 @@ package org.apache.knox.gateway.services.security.token.impl;
 
 import static java.util.Arrays.asList;
 import static java.util.Collections.singletonList;
-import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
@@ -143,7 +142,7 @@ public class JWTTokenTest {
     assertEquals("KNOXSSO", token.getIssuer());
     assertEquals("[email protected]", token.getSubject());
     assertNull(token.getAudience());
-    assertArrayEquals(null, token.getAudienceClaims());
+    assertEquals(0, token.getAudienceClaims().length);
   }
 
   @Test
@@ -205,15 +204,14 @@ public class JWTTokenTest {
   @Test
   public void testUnsignedToken() throws Exception {
     String unsignedToken = 
"eyJhbGciOiJub25lIn0.eyJzdWIiOiJhbGljZSIsImp0aSI6ImY2YmNj"
-                               + 
"MDVjLWI4MTktNGM0Mi1iMGMyLWJlYmY1MDE4YWFiZiJ9.";
+            + "MDVjLWI4MTktNGM0Mi1iMGMyLWJlYmY1MDE4YWFiZiJ9.";
 
     try {
       new JWTToken(unsignedToken);
       fail("Failure expected on an unsigned token");
     } catch (ParseException ex) {
       // expected
-      assertEquals("Invalid JWS header: The algorithm \"alg\" header parameter 
must be for signatures",
-          ex.getMessage());
+      assertEquals("Invalid JWS header: Not a JWS header", ex.getMessage());
     }
   }
 
diff --git a/pom.xml b/pom.xml
index 673384b39..03dfbb0d9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -252,7 +252,7 @@
         <mina.version>2.0.22</mina.version>
         <mockito.version>4.8.1</mockito.version>
         <netty.version>4.1.77.Final</netty.version>
-        <nimbus-jose-jwt.version>8.14.1</nimbus-jose-jwt.version>
+        <nimbus-jose-jwt.version>9.37.3</nimbus-jose-jwt.version>
         <nodejs.version>v14.15.0</nodejs.version>
         <okhttp.version>2.7.5</okhttp.version>
         <opensaml.version>3.4.5</opensaml.version>

Reply via email to