This is an automated email from the ASF dual-hosted git repository. diqiu50 pushed a commit to branch cherry-pick/10746-to-branch-1.2 in repository https://gitbox.apache.org/repos/asf/gravitino.git
commit a2fb26abade63a0a37a7e070aff7d172ddf9bc37 Author: Bharath Krishna <[email protected]> AuthorDate: Sat Apr 11 00:29:29 2026 -0700 [Cherry-pick to branch-1.2] [#10746] fix(auth): Cache JWKSource and downgrade token validation log to WARN (#10723) --- .../server/authentication/JwksTokenValidator.java | 39 +++++++++++++++------- 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/server-common/src/main/java/org/apache/gravitino/server/authentication/JwksTokenValidator.java b/server-common/src/main/java/org/apache/gravitino/server/authentication/JwksTokenValidator.java index e358a8998d..1db58ddc0a 100644 --- a/server-common/src/main/java/org/apache/gravitino/server/authentication/JwksTokenValidator.java +++ b/server-common/src/main/java/org/apache/gravitino/server/authentication/JwksTokenValidator.java @@ -57,6 +57,8 @@ public class JwksTokenValidator implements OAuthTokenValidator { private List<String> principalFields; private long allowSkewSeconds; private PrincipalMapper principalMapper; + private GroupMapper groupMapper; + private JWKSource<SecurityContext> jwkSource; @Override public void initialize(Config config) { @@ -77,12 +79,17 @@ public class JwksTokenValidator implements OAuthTokenValidator { "JWKS URI must be configured when using JWKS-based OAuth providers"); } - // Validate JWKS URI format + // Create the JWK source once at initialization. JWKSourceBuilder.create(url).build() enables + // rate-limiting (min 30 s between URL fetches) and caching with refresh-ahead by default: + // - Cache TTL: 5 minutes (DEFAULT_CACHE_TIME_TO_LIVE) + // - Refresh-ahead: 30 seconds before expiration on a background thread + // The Nimbus library handles key rotation transparently within these defaults. try { - new URL(jwksUri); + this.jwkSource = JWKSourceBuilder.create(new URL(jwksUri)).build(); } catch (Exception e) { - LOG.error("Invalid JWKS URI format: {}", jwksUri); - throw new IllegalArgumentException("Invalid JWKS URI format: " + jwksUri, e); + LOG.error("Failed to create JWKS source from URI: {}", jwksUri, e); + throw new IllegalArgumentException( + "Invalid JWKS URI or failed to create JWKS source: " + jwksUri, e); } } @@ -102,7 +109,6 @@ public class JwksTokenValidator implements OAuthTokenValidator { SignedJWT signedJWT = SignedJWT.parse(token); // Set up JWKS source and processor - JWKSource<SecurityContext> jwkSource = createJwkSource(); JWSAlgorithm algorithm = JWSAlgorithm.parse(signedJWT.getHeader().getAlgorithm().getName()); JWSKeySelector<SecurityContext> keySelector = new JWSVerificationKeySelector<>(algorithm, jwkSource); @@ -143,18 +149,27 @@ public class JwksTokenValidator implements OAuthTokenValidator { return principalMapper.map(principal); } catch (Exception e) { - LOG.error("JWKS JWT validation error: {}", e.getMessage()); + LOG.warn( + "JWKS JWT validation failed for principal [{}]: {}", + extractPrincipalForLogging(signedJWT), + e.getMessage()); throw new UnauthorizedException(e, "JWKS JWT validation error"); } } - /** Creates a JWK source from the configured JWKS URI. */ - private JWKSource<SecurityContext> createJwkSource() throws Exception { + /** + * Extracts the principal from a parsed (but not yet verified) JWT for diagnostic logging. Safe to + * call with a null or invalid JWT. + */ + String extractPrincipalForLogging(SignedJWT signedJWT) { + if (signedJWT == null) { + return "unknown"; + } try { - return JWKSourceBuilder.create(new URL(jwksUri)).build(); - } catch (Exception e) { - LOG.error("Failed to create JWKS source from URI: {}", jwksUri, e); - throw new Exception("Failed to create JWKS source: " + e.getMessage(), e); + String principal = extractPrincipal(signedJWT.getJWTClaimsSet()); + return principal != null ? principal : "unknown"; + } catch (Exception ex) { + return "unknown"; } }
