From 5f60a6db7317c37cda66049215a5b180963d9129 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Tue, 26 Mar 2024 19:40:18 +0000 Subject: [PATCH] Enforce OIDC code flow access token verification only if JWT is in the application code --- .../oidc/deployment/OidcBuildStep.java | 11 ++++ .../CodeFlowVerifyAccessTokenDisabled.java | 54 +++++++++++++++++++ ...rotectedResourceWithoutJwtAccessToken.java | 28 ++++++++++ ...on-verify-access-token-disabled.properties | 4 ++ 4 files changed, 97 insertions(+) create mode 100644 extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/CodeFlowVerifyAccessTokenDisabled.java create mode 100644 extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/ProtectedResourceWithoutJwtAccessToken.java create mode 100644 extensions/oidc/deployment/src/test/resources/application-verify-access-token-disabled.properties diff --git a/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/OidcBuildStep.java b/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/OidcBuildStep.java index 9679f0d0b3c7a..c3e7934b780bc 100644 --- a/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/OidcBuildStep.java +++ b/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/OidcBuildStep.java @@ -86,6 +86,9 @@ public class OidcBuildStep { private static final DotName JSON_WEB_TOKEN_NAME = DotName.createSimple(JsonWebToken.class); private static final DotName ID_TOKEN_NAME = DotName.createSimple(IdToken.class); + private static final String QUARKUS_TOKEN_PROPAGATION_PACKAGE = "io.quarkus.oidc.token.propagation"; + private static final String SMALLRYE_JWT_PACKAGE = "io.smallrye.jwt"; + @BuildStep public void provideSecurityInformation(BuildProducer securityInformationProducer) { // TODO: By default quarkus.oidc.application-type = service @@ -323,13 +326,21 @@ private static boolean isInjected(BeanRegistrationPhaseBuildItem beanRegistratio DotName withoutQualifier) { for (InjectionPointInfo injectionPoint : beanRegistrationPhaseBuildItem.getInjectionPoints()) { if (requiredType.equals(injectionPoint.getRequiredType().name()) + && isApplicationPackage(injectionPoint.getTargetInfo()) && (withoutQualifier == null || injectionPoint.getRequiredQualifier(withoutQualifier) == null)) { + LOG.debugf("%s injection point: %s", requiredType.toString(), injectionPoint.getTargetInfo()); return true; } } return false; } + private static boolean isApplicationPackage(String injectionPointTargetInfo) { + return injectionPointTargetInfo != null + && !injectionPointTargetInfo.startsWith(QUARKUS_TOKEN_PROPAGATION_PACKAGE) + && !injectionPointTargetInfo.startsWith(SMALLRYE_JWT_PACKAGE); + } + private static String toTargetName(AnnotationTarget target) { if (target.kind() == CLASS) { return target.asClass().name().toString(); diff --git a/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/CodeFlowVerifyAccessTokenDisabled.java b/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/CodeFlowVerifyAccessTokenDisabled.java new file mode 100644 index 0000000000000..f4a1e67a28b44 --- /dev/null +++ b/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/CodeFlowVerifyAccessTokenDisabled.java @@ -0,0 +1,54 @@ +package io.quarkus.oidc.test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.IOException; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import com.gargoylesoftware.htmlunit.SilentCssErrorHandler; +import com.gargoylesoftware.htmlunit.WebClient; +import com.gargoylesoftware.htmlunit.html.HtmlForm; +import com.gargoylesoftware.htmlunit.html.HtmlPage; + +import io.quarkus.test.QuarkusUnitTest; +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.keycloak.server.KeycloakTestResourceLifecycleManager; + +@QuarkusTestResource(KeycloakTestResourceLifecycleManager.class) +public class CodeFlowVerifyAccessTokenDisabled { + + @RegisterExtension + static final QuarkusUnitTest test = new QuarkusUnitTest() + .withApplicationRoot((jar) -> jar + .addClasses(ProtectedResourceWithoutJwtAccessToken.class) + .addAsResource("application-verify-access-token-disabled.properties", "application.properties")); + + @Test + public void testVerifyAccessTokenDisabled() throws IOException, InterruptedException { + try (final WebClient webClient = createWebClient()) { + + HtmlPage page = webClient.getPage("http://localhost:8081/protected"); + + assertEquals("Sign in to quarkus", page.getTitleText()); + + HtmlForm loginForm = page.getForms().get(0); + + loginForm.getInputByName("username").setValueAttribute("alice"); + loginForm.getInputByName("password").setValueAttribute("alice"); + + page = loginForm.getInputByName("login").click(); + + assertEquals("alice:false", page.getBody().asNormalizedText()); + + webClient.getCookieManager().clearCookies(); + } + } + + private WebClient createWebClient() { + WebClient webClient = new WebClient(); + webClient.setCssErrorHandler(new SilentCssErrorHandler()); + return webClient; + } +} diff --git a/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/ProtectedResourceWithoutJwtAccessToken.java b/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/ProtectedResourceWithoutJwtAccessToken.java new file mode 100644 index 0000000000000..d11ae1b9fdb02 --- /dev/null +++ b/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/ProtectedResourceWithoutJwtAccessToken.java @@ -0,0 +1,28 @@ +package io.quarkus.oidc.test; + +import jakarta.inject.Inject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; + +import org.eclipse.microprofile.jwt.JsonWebToken; + +import io.quarkus.oidc.IdToken; +import io.quarkus.oidc.runtime.OidcConfig; +import io.quarkus.security.Authenticated; + +@Path("/protected") +@Authenticated +public class ProtectedResourceWithoutJwtAccessToken { + + @Inject + @IdToken + JsonWebToken idToken; + + @Inject + OidcConfig config; + + @GET + public String getName() { + return idToken.getName() + ":" + config.defaultTenant.authentication.verifyAccessToken; + } +} diff --git a/extensions/oidc/deployment/src/test/resources/application-verify-access-token-disabled.properties b/extensions/oidc/deployment/src/test/resources/application-verify-access-token-disabled.properties new file mode 100644 index 0000000000000..f01af5aad3860 --- /dev/null +++ b/extensions/oidc/deployment/src/test/resources/application-verify-access-token-disabled.properties @@ -0,0 +1,4 @@ +quarkus.oidc.auth-server-url=${keycloak.url}/realms/quarkus +quarkus.oidc.client-id=quarkus-web-app +quarkus.oidc.credentials.secret=secret +quarkus.oidc.application-type=web-app