From ece55727d33d3c879ce7089a724674b80875c7f2 Mon Sep 17 00:00:00 2001 From: Athou Date: Mon, 19 Aug 2024 14:31:12 +0200 Subject: [PATCH] better workaround for cookie max-age --- commafeed-server/pom.xml | 4 + .../FormAuthenticationCookieInterceptor.java | 34 +++++++ ...okieMaxAgeFormAuthenticationMechanism.java | 90 ------------------- 3 files changed, 38 insertions(+), 90 deletions(-) create mode 100644 commafeed-server/src/main/java/com/commafeed/security/FormAuthenticationCookieInterceptor.java delete mode 100644 commafeed-server/src/main/java/com/commafeed/security/mechanism/CookieMaxAgeFormAuthenticationMechanism.java diff --git a/commafeed-server/pom.xml b/commafeed-server/pom.xml index 2b65e2c7..adac30b4 100644 --- a/commafeed-server/pom.xml +++ b/commafeed-server/pom.xml @@ -248,6 +248,10 @@ io.quarkus quarkus-arc + + io.quarkus + quarkus-reactive-routes + io.quarkus quarkus-security diff --git a/commafeed-server/src/main/java/com/commafeed/security/FormAuthenticationCookieInterceptor.java b/commafeed-server/src/main/java/com/commafeed/security/FormAuthenticationCookieInterceptor.java new file mode 100644 index 00000000..3692ed2d --- /dev/null +++ b/commafeed-server/src/main/java/com/commafeed/security/FormAuthenticationCookieInterceptor.java @@ -0,0 +1,34 @@ +package com.commafeed.security; + +import io.quarkus.vertx.http.runtime.HttpConfiguration; +import io.quarkus.vertx.http.runtime.security.FormAuthenticationMechanism; +import io.quarkus.vertx.web.RouteFilter; +import io.vertx.core.http.Cookie; +import io.vertx.core.http.impl.ServerCookie; +import io.vertx.ext.web.RoutingContext; +import jakarta.inject.Singleton; +import lombok.RequiredArgsConstructor; + +/** + * Intercepts responses and sets a Max-Age on the cookie created by {@link FormAuthenticationMechanism} because it has no value by default. + * + * This is a workaround for https://github.com/quarkusio/quarkus/issues/42463 + */ +@RequiredArgsConstructor +@Singleton +public class FormAuthenticationCookieInterceptor { + + private final HttpConfiguration config; + + @RouteFilter(Integer.MAX_VALUE) + public void cookieInterceptor(RoutingContext context) { + context.addHeadersEndHandler(v -> { + Cookie cookie = context.request().getCookie(config.auth.form.cookieName); + if (cookie instanceof ServerCookie sc && sc.isChanged()) { + cookie.setMaxAge(config.auth.form.timeout.toSeconds()); + } + }); + + context.next(); + } +} diff --git a/commafeed-server/src/main/java/com/commafeed/security/mechanism/CookieMaxAgeFormAuthenticationMechanism.java b/commafeed-server/src/main/java/com/commafeed/security/mechanism/CookieMaxAgeFormAuthenticationMechanism.java deleted file mode 100644 index c5ffa581..00000000 --- a/commafeed-server/src/main/java/com/commafeed/security/mechanism/CookieMaxAgeFormAuthenticationMechanism.java +++ /dev/null @@ -1,90 +0,0 @@ -package com.commafeed.security.mechanism; - -import java.security.SecureRandom; -import java.util.Base64; - -import io.quarkus.vertx.http.runtime.FormAuthConfig; -import io.quarkus.vertx.http.runtime.FormAuthRuntimeConfig; -import io.quarkus.vertx.http.runtime.HttpBuildTimeConfig; -import io.quarkus.vertx.http.runtime.HttpConfiguration; -import io.quarkus.vertx.http.runtime.security.FormAuthenticationMechanism; -import io.quarkus.vertx.http.runtime.security.HttpAuthenticationMechanism; -import io.quarkus.vertx.http.runtime.security.PersistentLoginManager; -import io.vertx.core.http.Cookie; -import io.vertx.core.http.impl.ServerCookie; -import io.vertx.ext.web.RoutingContext; -import jakarta.annotation.Priority; -import jakarta.inject.Singleton; -import lombok.experimental.Delegate; -import lombok.extern.slf4j.Slf4j; - -/** - * HttpAuthenticationMechanism that wraps FormAuthenticationMechanism and sets a Max-Age on the cookie because it has no value by default. - * - * This is a workaround for https://github.com/quarkusio/quarkus/issues/42463 - */ -@Priority(1) -@Singleton -@Slf4j -public class CookieMaxAgeFormAuthenticationMechanism implements HttpAuthenticationMechanism { - - // the temp encryption key, persistent across dev mode restarts - static volatile String encryptionKey; - - @Delegate - private final FormAuthenticationMechanism delegate; - - public CookieMaxAgeFormAuthenticationMechanism(HttpConfiguration httpConfiguration, HttpBuildTimeConfig buildTimeConfig) { - String key; - if (httpConfiguration.encryptionKey.isEmpty()) { - if (encryptionKey != null) { - // persist across dev mode restarts - key = encryptionKey; - } else { - byte[] data = new byte[32]; - new SecureRandom().nextBytes(data); - key = encryptionKey = Base64.getEncoder().encodeToString(data); - log.warn("Encryption key was not specified for persistent FORM auth, using temporary key {}", key); - } - } else { - key = httpConfiguration.encryptionKey.get(); - } - - FormAuthConfig form = buildTimeConfig.auth.form; - FormAuthRuntimeConfig runtimeForm = httpConfiguration.auth.form; - String loginPage = startWithSlash(runtimeForm.loginPage.orElse(null)); - String errorPage = startWithSlash(runtimeForm.errorPage.orElse(null)); - String landingPage = startWithSlash(runtimeForm.landingPage.orElse(null)); - String postLocation = startWithSlash(form.postLocation); - String usernameParameter = runtimeForm.usernameParameter; - String passwordParameter = runtimeForm.passwordParameter; - String locationCookie = runtimeForm.locationCookie; - String cookiePath = runtimeForm.cookiePath.orElse(null); - boolean redirectAfterLogin = landingPage != null; - String cookieSameSite = runtimeForm.cookieSameSite.name(); - - PersistentLoginManager loginManager = new PersistentLoginManager(key, runtimeForm.cookieName, runtimeForm.timeout.toMillis(), - runtimeForm.newCookieInterval.toMillis(), runtimeForm.httpOnlyCookie, cookieSameSite, cookiePath) { - @Override - public void save(String value, RoutingContext context, String cookieName, RestoreResult restoreResult, boolean secureCookie) { - super.save(value, context, cookieName, restoreResult, secureCookie); - - // add max age to the cookie - Cookie cookie = context.request().getCookie(cookieName); - if (cookie instanceof ServerCookie sc && sc.isChanged()) { - cookie.setMaxAge(runtimeForm.timeout.toSeconds()); - } - } - }; - - this.delegate = new FormAuthenticationMechanism(loginPage, postLocation, usernameParameter, passwordParameter, errorPage, - landingPage, redirectAfterLogin, locationCookie, cookieSameSite, cookiePath, loginManager); - } - - private static String startWithSlash(String page) { - if (page == null) { - return null; - } - return page.startsWith("/") ? page : "/" + page; - } -}