This is an automated email from the ASF dual-hosted git repository. dblevins pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/tomee.git
commit 183e88fd1ee4bb751c5eaea7b358ef7cd7895781 Author: David Blevins <dblev...@tomitribe.com> AuthorDate: Wed Aug 31 15:43:34 2022 -0700 TOMEE-3949 Support for JWT audience aud claim --- .../apache/tomee/microprofile/jwt/MPJWTFilter.java | 38 ++++++++++++---------- .../jwt/config/JWTAuthConfiguration.java | 19 ++++++++--- .../jwt/config/JWTAuthConfigurationProperties.java | 17 +++++++--- 3 files changed, 49 insertions(+), 25 deletions(-) diff --git a/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/MPJWTFilter.java b/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/MPJWTFilter.java index 3ded1ff31b..c9f7c5049c 100644 --- a/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/MPJWTFilter.java +++ b/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/MPJWTFilter.java @@ -16,7 +16,20 @@ */ package org.apache.tomee.microprofile.jwt; -import org.apache.commons.lang3.Validate; +import jakarta.enterprise.inject.Instance; +import jakarta.inject.Inject; +import jakarta.servlet.Filter; +import jakarta.servlet.FilterChain; +import jakarta.servlet.FilterConfig; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequestWrapper; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.ext.ExceptionMapper; +import jakarta.ws.rs.ext.Provider; import org.apache.openejb.loader.SystemInstance; import org.apache.openejb.spi.SecurityService; import org.apache.openejb.util.Logger; @@ -39,21 +52,7 @@ import org.jose4j.jwt.consumer.JwtConsumerBuilder; import org.jose4j.jwt.consumer.JwtContext; import org.jose4j.keys.resolvers.JwksVerificationKeyResolver; -import jakarta.enterprise.inject.Instance; -import jakarta.inject.Inject; import javax.security.auth.Subject; -import jakarta.servlet.Filter; -import jakarta.servlet.FilterChain; -import jakarta.servlet.FilterConfig; -import jakarta.servlet.ServletException; -import jakarta.servlet.ServletRequest; -import jakarta.servlet.ServletResponse; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletRequestWrapper; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.ws.rs.core.Response; -import jakarta.ws.rs.ext.ExceptionMapper; -import jakarta.ws.rs.ext.Provider; import java.io.IOException; import java.security.Principal; import java.util.Collections; @@ -294,7 +293,7 @@ public class MPJWTFilter implements Filter { } final String headerScheme = (jwtAuthConfiguration.getHeaderScheme() + " ").toLowerCase(Locale.ENGLISH); - if (headerScheme.trim().length() > 0 && !authorizationHeader.toLowerCase(Locale.ENGLISH).startsWith(headerScheme)) { + if (headerScheme.trim().length() > 0 && !authorizationHeader.toLowerCase(Locale.ENGLISH).startsWith(headerScheme)) { throw new BadAuthorizationPrefixException(authorizationHeader); } @@ -330,7 +329,6 @@ public class MPJWTFilter implements Filter { final JwtConsumerBuilder builder = new JwtConsumerBuilder() .setRelaxVerificationKeyValidation() .setRequireSubject() - .setSkipDefaultAudienceValidation() .setJwsAlgorithmConstraints( new AlgorithmConstraints(AlgorithmConstraints.ConstraintType.WHITELIST, AlgorithmIdentifiers.RSA_USING_SHA256, @@ -341,6 +339,12 @@ public class MPJWTFilter implements Filter { AlgorithmIdentifiers.ECDSA_USING_P521_CURVE_AND_SHA512 )); + if (authContextInfo.getAudiences().length > 0) { + builder.setExpectedAudience(true, authContextInfo.getAudiences()); + } else { + builder.setSkipDefaultAudienceValidation(); + } + if (!authContextInfo.isAllowNoExpiryClaim()) { builder.setRequireExpirationTime(); } diff --git a/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/config/JWTAuthConfiguration.java b/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/config/JWTAuthConfiguration.java index 7a9fae68d9..eec7fc4180 100644 --- a/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/config/JWTAuthConfiguration.java +++ b/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/config/JWTAuthConfiguration.java @@ -34,19 +34,21 @@ public class JWTAuthConfiguration { public static final String DEFAULT_KEY = "DEFAULT"; private Map<String, Key> publicKeys; + private String[] audiences; private String issuer; private int expGracePeriodSecs = 60; private String headerName = "Authorization"; private String headerScheme = "Bearer"; private boolean allowNoExpiryClaim = false; - private JWTAuthConfiguration(final Key publicKey, final String issuer, final boolean allowNoExpiryClaim) { + private JWTAuthConfiguration(final Key publicKey, final String issuer, final boolean allowNoExpiryClaim, final String[] audiences) { this.publicKeys = Collections.singletonMap(DEFAULT_KEY, publicKey); this.issuer = issuer; this.allowNoExpiryClaim = allowNoExpiryClaim; + this.audiences = audiences; } - private JWTAuthConfiguration(final Map<String, Key> publicKeys, final String issuer, final boolean allowNoExpiryClaim) { + private JWTAuthConfiguration(final Map<String, Key> publicKeys, final String issuer, final boolean allowNoExpiryClaim, final String[] audiences) { if (publicKeys.size() == 1) { final Key singleKey = publicKeys.values().iterator().next(); this.publicKeys = Collections.singletonMap(DEFAULT_KEY, singleKey); @@ -55,14 +57,23 @@ public class JWTAuthConfiguration { } this.issuer = issuer; this.allowNoExpiryClaim = allowNoExpiryClaim; + this.audiences = audiences; } public static JWTAuthConfiguration authConfiguration(final Key publicKey, final String issuer, final boolean allowNoExpiryClaim) { - return new JWTAuthConfiguration(publicKey, issuer, allowNoExpiryClaim); + return new JWTAuthConfiguration(publicKey, issuer, allowNoExpiryClaim, new String[0]); } public static JWTAuthConfiguration authConfiguration(final Map<String, Key> publicKeys, final String issuer, final boolean allowNoExpiryClaim) { - return new JWTAuthConfiguration(publicKeys, issuer, allowNoExpiryClaim); + return authConfiguration(publicKeys, issuer, allowNoExpiryClaim, new String[0]); + } + + public static JWTAuthConfiguration authConfiguration(final Map<String, Key> publicKeys, final String issuer, final boolean allowNoExpiryClaim, final String[] audiences) { + return new JWTAuthConfiguration(publicKeys, issuer, allowNoExpiryClaim, audiences); + } + + public String[] getAudiences() { + return audiences; } public boolean isSingleKey() { diff --git a/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/config/JWTAuthConfigurationProperties.java b/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/config/JWTAuthConfigurationProperties.java index 87a8350d6b..f5255bb428 100644 --- a/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/config/JWTAuthConfigurationProperties.java +++ b/mp-jwt/src/main/java/org/apache/tomee/microprofile/jwt/config/JWTAuthConfigurationProperties.java @@ -25,9 +25,13 @@ import org.eclipse.microprofile.config.Config; import org.eclipse.microprofile.config.ConfigProvider; import java.security.Key; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; import java.util.Map; import java.util.Optional; +import static org.eclipse.microprofile.jwt.config.Names.AUDIENCES; import static org.eclipse.microprofile.jwt.config.Names.ISSUER; import static org.eclipse.microprofile.jwt.config.Names.VERIFIER_PUBLIC_KEY; import static org.eclipse.microprofile.jwt.config.Names.VERIFIER_PUBLIC_KEY_LOCATION; @@ -69,6 +73,12 @@ public class JWTAuthConfigurationProperties { return config.getOptionalValue(ISSUER, String.class); } + private List<String> getAudiences() { + final String audiences = config.getOptionalValue(AUDIENCES, String.class).orElse(null); + if (audiences == null) return Collections.EMPTY_LIST; + return Arrays.asList(audiences.split(" *, *")); + } + private JWTAuthConfiguration createJWTAuthConfiguration() { if (getVerifierPublicKey().isPresent() && getPublicKeyLocation().isPresent()) { throw new DeploymentException("Both " + @@ -80,13 +90,12 @@ public class JWTAuthConfigurationProperties { final Optional<String> publicKeyContents = getVerifierPublicKey(); final Optional<String> publicKeyLocation = getPublicKeyLocation(); + final List<String> audiences = getAudiences(); - final Optional<Map<String, Key>> first = new PublicKeyResolver().resolve(publicKeyContents, publicKeyLocation); + final Map<String, Key> keys = new PublicKeyResolver().resolve(publicKeyContents, publicKeyLocation).orElse(null); final Boolean allowNoExp = config.getOptionalValue("mp.jwt.tomee.allow.no-exp", Boolean.class).orElse(false); - return first - .map(keys -> JWTAuthConfiguration.authConfiguration(keys, getIssuer().orElse(null), allowNoExp)) - .orElse(null); + return JWTAuthConfiguration.authConfiguration(keys, getIssuer().orElse(null), allowNoExp, audiences.toArray(new String[0])); } }