This is an automated email from the ASF dual-hosted git repository. btellier pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/james-project.git
commit 321c83c1090e6274b685d9f597d7f6579d7c9b9d Author: Benoit TELLIER <[email protected]> AuthorDate: Fri Jan 16 21:30:34 2026 +0100 [ENHANCEMENT] Validate aud without introspect --- .../org/apache/james/jwt/JwtTokenVerifier.java | 26 ++++++++-------------- .../org/apache/james/jwt/OidcJwtTokenVerifier.java | 16 ++++++++----- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/server/protocols/jwt/src/main/java/org/apache/james/jwt/JwtTokenVerifier.java b/server/protocols/jwt/src/main/java/org/apache/james/jwt/JwtTokenVerifier.java index f5b79927ec..7fb8350c15 100644 --- a/server/protocols/jwt/src/main/java/org/apache/james/jwt/JwtTokenVerifier.java +++ b/server/protocols/jwt/src/main/java/org/apache/james/jwt/JwtTokenVerifier.java @@ -29,7 +29,6 @@ import java.util.Optional; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.github.fge.lambdas.Throwing; import com.google.common.collect.ImmutableList; import io.jsonwebtoken.Claims; @@ -38,7 +37,6 @@ import io.jsonwebtoken.JwtException; import io.jsonwebtoken.JwtParser; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.Locator; -import io.jsonwebtoken.MalformedJwtException; import io.jsonwebtoken.io.CompressionAlgorithm; public class JwtTokenVerifier { @@ -122,15 +120,15 @@ public class JwtTokenVerifier { } public <T> Optional<T> verifyAndExtractClaim(String token, String claimName, Class<T> returnType) { - try { - // if the token contains a kid, verify only with the corresponding key (or fail) - return verifyAndExtractClaim(token, claimName, returnType, kidJwtParser); - } catch (NullPointerException npe) { // our own key locator throws NPE when there is no kid - // if token does not specify kid, fallback to trying all keys - return jwtParsers.stream() - .flatMap(parser -> verifyAndExtractClaim(token, claimName, returnType, parser).stream()) - .findFirst(); - } + return verify(token) + .flatMap(claims -> { + try { + return Optional.ofNullable(claims.get(claimName, returnType)); + } catch (JwtException e) { + LOGGER.info("Failed Jwt verification", e); + return Optional.empty(); + } + }); } public Optional<Claims> verify(String token) { @@ -145,12 +143,6 @@ public class JwtTokenVerifier { } } - private <T> Optional<T> verifyAndExtractClaim(String token, String claimName, Class<T> returnType, JwtParser parser) { - return retrieveClaims(token, parser) - .map(Throwing.function(claims -> Optional.ofNullable(claims.get(claimName, returnType)) - .orElseThrow(() -> new MalformedJwtException("'" + claimName + "' field in token is mandatory")))); - } - private Optional<Claims> retrieveClaims(String token, JwtParser parser) { try { Jws<Claims> jws = parser.parseSignedClaims(token); diff --git a/server/protocols/jwt/src/main/java/org/apache/james/jwt/OidcJwtTokenVerifier.java b/server/protocols/jwt/src/main/java/org/apache/james/jwt/OidcJwtTokenVerifier.java index 965013a96b..8e5eed7383 100644 --- a/server/protocols/jwt/src/main/java/org/apache/james/jwt/OidcJwtTokenVerifier.java +++ b/server/protocols/jwt/src/main/java/org/apache/james/jwt/OidcJwtTokenVerifier.java @@ -32,6 +32,7 @@ import org.slf4j.LoggerFactory; import com.google.common.annotations.VisibleForTesting; +import io.jsonwebtoken.JwtException; import reactor.core.publisher.Mono; public class OidcJwtTokenVerifier { @@ -72,11 +73,16 @@ public class OidcJwtTokenVerifier { @VisibleForTesting Optional<String> verifySignatureAndExtractClaim(String jwtToken) { - return new JwtTokenVerifier(JwksPublicKeyProvider.of(oidcSASLConfiguration.getJwksURL())) - .verify(jwtToken) - .filter(claims -> oidcSASLConfiguration.getAud().map(expectedAud -> claims.getAudience().contains(expectedAud)) - .orElse(true)) // true if no aud is configured - .flatMap(claims -> Optional.ofNullable(claims.get(oidcSASLConfiguration.getClaim(), String.class))); + try { + return new JwtTokenVerifier(JwksPublicKeyProvider.of(oidcSASLConfiguration.getJwksURL())) + .verify(jwtToken) + .filter(claims -> oidcSASLConfiguration.getAud().map(expectedAud -> claims.getAudience().contains(expectedAud)) + .orElse(true)) // true if no aud is configured + .flatMap(claims -> Optional.ofNullable(claims.get(oidcSASLConfiguration.getClaim(), String.class))); + } catch (JwtException e) { + LOGGER.info("Failed Jwt verification", e); + return Optional.empty(); + } } @VisibleForTesting --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
