diff --git a/extensions/smallrye-jwt/deployment/src/main/java/io/quarkus/smallrye/jwt/deployment/SmallRyeJwtProcessor.java b/extensions/smallrye-jwt/deployment/src/main/java/io/quarkus/smallrye/jwt/deployment/SmallRyeJwtProcessor.java index 9f78eeadbc1ee..2cf6f90973cd8 100644 --- a/extensions/smallrye-jwt/deployment/src/main/java/io/quarkus/smallrye/jwt/deployment/SmallRyeJwtProcessor.java +++ b/extensions/smallrye-jwt/deployment/src/main/java/io/quarkus/smallrye/jwt/deployment/SmallRyeJwtProcessor.java @@ -30,8 +30,11 @@ import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; import io.quarkus.security.deployment.JCAProviderBuildItem; +import io.quarkus.smallrye.jwt.runtime.auth.DefaultJwtParser; +import io.quarkus.smallrye.jwt.runtime.auth.DefaultJwtRolesMapper; import io.quarkus.smallrye.jwt.runtime.auth.JWTAuthMechanism; import io.quarkus.smallrye.jwt.runtime.auth.JwtPrincipalProducer; +import io.quarkus.smallrye.jwt.runtime.auth.JwtTokenUtils; import io.quarkus.smallrye.jwt.runtime.auth.MpJwtValidator; import io.quarkus.smallrye.jwt.runtime.auth.RawOptionalClaimCreator; import io.smallrye.jwt.algorithm.SignatureAlgorithm; @@ -79,6 +82,9 @@ void registerAdditionalBeans(BuildProducer additionalBe removable.addBeanClass(RawClaimTypeProducer.class); removable.addBeanClass(JsonValueProducer.class); removable.addBeanClass(JwtPrincipalProducer.class); + removable.addBeanClass(JwtTokenUtils.class); + removable.addBeanClass(DefaultJwtParser.class); + removable.addBeanClass(DefaultJwtRolesMapper.class); removable.addBeanClass(Claim.class); additionalBeans.produce(removable.build()); diff --git a/extensions/smallrye-jwt/deployment/src/test/java/io/quarkus/jwt/test/TokenRealmUnitTest.java b/extensions/smallrye-jwt/deployment/src/test/java/io/quarkus/jwt/test/TokenRealmUnitTest.java index ad644752526d7..4a17ff247a519 100644 --- a/extensions/smallrye-jwt/deployment/src/test/java/io/quarkus/jwt/test/TokenRealmUnitTest.java +++ b/extensions/smallrye-jwt/deployment/src/test/java/io/quarkus/jwt/test/TokenRealmUnitTest.java @@ -16,6 +16,10 @@ import io.quarkus.security.identity.request.TokenAuthenticationRequest; import io.quarkus.security.runtime.AnonymousIdentityProvider; import io.quarkus.security.runtime.QuarkusIdentityProviderManagerImpl; +import io.quarkus.smallrye.jwt.runtime.auth.DefaultJwtParser; +import io.quarkus.smallrye.jwt.runtime.auth.DefaultJwtRolesMapper; +import io.quarkus.smallrye.jwt.runtime.auth.JwtParser; +import io.quarkus.smallrye.jwt.runtime.auth.JwtRolesMapper; import io.quarkus.smallrye.jwt.runtime.auth.MpJwtValidator; import io.smallrye.jwt.auth.principal.JWTAuthContextInfo; @@ -30,7 +34,9 @@ public void testAuthenticator() throws Exception { PublicKey pk1 = keyPair.getPublic(); PrivateKey pk1Priv = keyPair.getPrivate(); JWTAuthContextInfo contextInfo = new JWTAuthContextInfo((RSAPublicKey) pk1, "https://server.example.com"); - MpJwtValidator jwtValidator = new MpJwtValidator(contextInfo); + JwtParser jwtParser = new DefaultJwtParser(); + JwtRolesMapper jwtRolesMapper = new DefaultJwtRolesMapper(); + MpJwtValidator jwtValidator = new MpJwtValidator(contextInfo, jwtParser, jwtRolesMapper); QuarkusIdentityProviderManagerImpl authenticator = QuarkusIdentityProviderManagerImpl.builder() .addProvider(new AnonymousIdentityProvider()) .setBlockingExecutor(new Executor() { diff --git a/extensions/smallrye-jwt/runtime/src/main/java/io/quarkus/smallrye/jwt/runtime/auth/DefaultJwtParser.java b/extensions/smallrye-jwt/runtime/src/main/java/io/quarkus/smallrye/jwt/runtime/auth/DefaultJwtParser.java new file mode 100644 index 0000000000000..f6d640e6cf6be --- /dev/null +++ b/extensions/smallrye-jwt/runtime/src/main/java/io/quarkus/smallrye/jwt/runtime/auth/DefaultJwtParser.java @@ -0,0 +1,21 @@ +package io.quarkus.smallrye.jwt.runtime.auth; + +import javax.enterprise.context.ApplicationScoped; + +import org.jose4j.jwt.consumer.JwtContext; + +import io.quarkus.arc.DefaultBean; +import io.smallrye.jwt.auth.principal.DefaultJWTTokenParser; +import io.smallrye.jwt.auth.principal.JWTAuthContextInfo; +import io.smallrye.jwt.auth.principal.ParseException; + +@DefaultBean +@ApplicationScoped +public class DefaultJwtParser implements JwtParser { + private final DefaultJWTTokenParser parser = new DefaultJWTTokenParser(); + + @Override + public JwtContext parse(String token, JWTAuthContextInfo authContextInfo) throws ParseException { + return parser.parse(token, authContextInfo); + } +} diff --git a/extensions/smallrye-jwt/runtime/src/main/java/io/quarkus/smallrye/jwt/runtime/auth/DefaultJwtRolesMapper.java b/extensions/smallrye-jwt/runtime/src/main/java/io/quarkus/smallrye/jwt/runtime/auth/DefaultJwtRolesMapper.java new file mode 100644 index 0000000000000..6d9362a4fa4d8 --- /dev/null +++ b/extensions/smallrye-jwt/runtime/src/main/java/io/quarkus/smallrye/jwt/runtime/auth/DefaultJwtRolesMapper.java @@ -0,0 +1,20 @@ +package io.quarkus.smallrye.jwt.runtime.auth; + +import java.util.HashSet; + +import javax.enterprise.context.ApplicationScoped; + +import org.jose4j.jwt.JwtClaims; +import org.jose4j.jwt.MalformedClaimException; + +import io.quarkus.arc.DefaultBean; + +@DefaultBean +@ApplicationScoped +public class DefaultJwtRolesMapper implements JwtRolesMapper { + + @Override + public HashSet mapGroupsAndRoles(JwtClaims claims) throws MalformedClaimException { + return new HashSet<>(claims.getStringListClaimValue("groups")); + } +} diff --git a/extensions/smallrye-jwt/runtime/src/main/java/io/quarkus/smallrye/jwt/runtime/auth/JWTAuthMechanism.java b/extensions/smallrye-jwt/runtime/src/main/java/io/quarkus/smallrye/jwt/runtime/auth/JWTAuthMechanism.java index ab8793afbdede..81d8f4d802dbd 100644 --- a/extensions/smallrye-jwt/runtime/src/main/java/io/quarkus/smallrye/jwt/runtime/auth/JWTAuthMechanism.java +++ b/extensions/smallrye-jwt/runtime/src/main/java/io/quarkus/smallrye/jwt/runtime/auth/JWTAuthMechanism.java @@ -24,7 +24,7 @@ import io.smallrye.jwt.auth.AbstractBearerTokenExtractor; import io.smallrye.jwt.auth.cdi.PrincipalProducer; import io.smallrye.jwt.auth.principal.JWTAuthContextInfo; -import io.vertx.ext.web.Cookie; +import io.vertx.core.http.Cookie; import io.vertx.ext.web.RoutingContext; /** diff --git a/extensions/smallrye-jwt/runtime/src/main/java/io/quarkus/smallrye/jwt/runtime/auth/JwtParser.java b/extensions/smallrye-jwt/runtime/src/main/java/io/quarkus/smallrye/jwt/runtime/auth/JwtParser.java new file mode 100644 index 0000000000000..6c89e6e1978d2 --- /dev/null +++ b/extensions/smallrye-jwt/runtime/src/main/java/io/quarkus/smallrye/jwt/runtime/auth/JwtParser.java @@ -0,0 +1,10 @@ +package io.quarkus.smallrye.jwt.runtime.auth; + +import org.jose4j.jwt.consumer.JwtContext; + +import io.smallrye.jwt.auth.principal.JWTAuthContextInfo; +import io.smallrye.jwt.auth.principal.ParseException; + +public interface JwtParser { + JwtContext parse(String token, JWTAuthContextInfo authContextInfo) throws ParseException; +} diff --git a/extensions/smallrye-jwt/runtime/src/main/java/io/quarkus/smallrye/jwt/runtime/auth/JwtRolesMapper.java b/extensions/smallrye-jwt/runtime/src/main/java/io/quarkus/smallrye/jwt/runtime/auth/JwtRolesMapper.java new file mode 100644 index 0000000000000..9d20932dbea35 --- /dev/null +++ b/extensions/smallrye-jwt/runtime/src/main/java/io/quarkus/smallrye/jwt/runtime/auth/JwtRolesMapper.java @@ -0,0 +1,10 @@ +package io.quarkus.smallrye.jwt.runtime.auth; + +import java.util.HashSet; + +import org.jose4j.jwt.JwtClaims; +import org.jose4j.jwt.MalformedClaimException; + +public interface JwtRolesMapper { + HashSet mapGroupsAndRoles(JwtClaims claims) throws MalformedClaimException; +} diff --git a/extensions/smallrye-jwt/runtime/src/main/java/io/quarkus/smallrye/jwt/runtime/auth/JwtTokenUtils.java b/extensions/smallrye-jwt/runtime/src/main/java/io/quarkus/smallrye/jwt/runtime/auth/JwtTokenUtils.java new file mode 100644 index 0000000000000..147f9166ff9da --- /dev/null +++ b/extensions/smallrye-jwt/runtime/src/main/java/io/quarkus/smallrye/jwt/runtime/auth/JwtTokenUtils.java @@ -0,0 +1,20 @@ +package io.quarkus.smallrye.jwt.runtime.auth; + +import javax.enterprise.context.ApplicationScoped; + +import org.jose4j.jwt.JwtClaims; +import org.jose4j.jwt.consumer.InvalidJwtException; +import org.jose4j.jwt.consumer.JwtConsumer; +import org.jose4j.jwt.consumer.JwtConsumerBuilder; + +@ApplicationScoped +public class JwtTokenUtils { + public JwtClaims decodeTokenUnverified(String tokenString) throws InvalidJwtException { + JwtConsumer consumer = new JwtConsumerBuilder() + .setSkipAllValidators() + .setDisableRequireSignature() + .setSkipSignatureVerification() + .build(); + return consumer.processToClaims(tokenString); + } +} diff --git a/extensions/smallrye-jwt/runtime/src/main/java/io/quarkus/smallrye/jwt/runtime/auth/MpJwtValidator.java b/extensions/smallrye-jwt/runtime/src/main/java/io/quarkus/smallrye/jwt/runtime/auth/MpJwtValidator.java index c6cf4570538df..5b7aea54724d0 100644 --- a/extensions/smallrye-jwt/runtime/src/main/java/io/quarkus/smallrye/jwt/runtime/auth/MpJwtValidator.java +++ b/extensions/smallrye-jwt/runtime/src/main/java/io/quarkus/smallrye/jwt/runtime/auth/MpJwtValidator.java @@ -1,6 +1,5 @@ package io.quarkus.smallrye.jwt.runtime.auth; -import java.util.HashSet; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; @@ -18,7 +17,6 @@ import io.quarkus.security.identity.SecurityIdentity; import io.quarkus.security.identity.request.TokenAuthenticationRequest; import io.quarkus.security.runtime.QuarkusSecurityIdentity; -import io.smallrye.jwt.auth.principal.DefaultJWTTokenParser; import io.smallrye.jwt.auth.principal.JWTAuthContextInfo; import io.smallrye.jwt.auth.principal.ParseException; @@ -31,16 +29,14 @@ public class MpJwtValidator implements IdentityProvider authenticate(TokenAuthenticationRequest QuarkusJwtCallerPrincipal principal = new QuarkusJwtCallerPrincipal(name, claims); return CompletableFuture .completedFuture(QuarkusSecurityIdentity.builder().setPrincipal(principal) - .addRoles(new HashSet<>(claims.getStringListClaimValue("groups"))) + .addRoles(jwtRolesMapper.mapGroupsAndRoles(claims)) .addAttribute(SecurityIdentity.USER_ATTRIBUTE, principal).build()); } catch (ParseException | MalformedClaimException e) { log.debug("Authentication failed", e); - CompletableFuture cf = new CompletableFuture(); + CompletableFuture cf = new CompletableFuture<>(); cf.completeExceptionally(new AuthenticationFailedException(e)); return cf; }