diff --git a/src/main/java/se/su/dsv/oauth2/AuthorizationServer.java b/src/main/java/se/su/dsv/oauth2/AuthorizationServer.java
index 478e989..c123b0f 100644
--- a/src/main/java/se/su/dsv/oauth2/AuthorizationServer.java
+++ b/src/main/java/se/su/dsv/oauth2/AuthorizationServer.java
@@ -9,7 +9,6 @@ import org.springframework.context.annotation.Profile;
 import org.springframework.core.annotation.Order;
 import org.springframework.http.MediaType;
 import org.springframework.security.config.Customizer;
-import org.springframework.security.config.ObjectPostProcessor;
 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
 import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
 import org.springframework.security.config.http.SessionCreationPolicy;
@@ -24,10 +23,8 @@ import org.springframework.security.web.SecurityFilterChain;
 import org.springframework.security.web.access.intercept.AuthorizationFilter;
 import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
 import org.springframework.security.web.authentication.preauth.j2ee.J2eePreAuthenticatedProcessingFilter;
-import org.springframework.security.web.context.RequestAttributeSecurityContextRepository;
 import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;
 import se.su.dsv.oauth2.shibboleth.Entitlement;
-import se.su.dsv.oauth2.shibboleth.ShibbolethAuthenticationDetailsSource;
 import se.su.dsv.oauth2.shibboleth.ShibbolethConfigurer;
 import se.su.dsv.oauth2.shibboleth.ShibbolethTokenPopulator;
 import se.su.dsv.oauth2.staging.StagingSecurityConfigurer;
@@ -140,30 +137,11 @@ public class AuthorizationServer extends SpringBootServletInitializer {
                 .accessDeniedPage("/forbidden")
                 .authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login")));
 
-        http.jee(jee -> jee
-                .withObjectPostProcessor(makeShibbolethAware()));
+        http.with(new ShibbolethConfigurer(), Customizer.withDefaults());
 
         return http.build();
     }
 
-    private static ObjectPostProcessor<J2eePreAuthenticatedProcessingFilter> makeShibbolethAware()
-    {
-        return new ObjectPostProcessor<>() {
-            @Override
-            public <O extends J2eePreAuthenticatedProcessingFilter> O postProcess(final O object) {
-                // Using a custom authentication details source to extract the Shibboleth attributes
-                // and convert them to the relevant Spring Security objects.
-                object.setAuthenticationDetailsSource(new ShibbolethAuthenticationDetailsSource());
-
-                // Prevent session creation
-                // It can cause conflicts when running on the same host as an embedded docker container
-                // as it overwrites the session cookie (it does not factor in port)
-                object.setSecurityContextRepository(new RequestAttributeSecurityContextRepository());
-                return object;
-            }
-        };
-    }
-
     @Bean
     public OAuth2TokenCustomizer<JwtEncodingContext> jwtCustomizer() {
         return new ShibbolethTokenPopulator();
diff --git a/src/main/java/se/su/dsv/oauth2/shibboleth/ShibbolethAuthenticatedProcessingFilter.java b/src/main/java/se/su/dsv/oauth2/shibboleth/ShibbolethAuthenticatedProcessingFilter.java
new file mode 100644
index 0000000..b642843
--- /dev/null
+++ b/src/main/java/se/su/dsv/oauth2/shibboleth/ShibbolethAuthenticatedProcessingFilter.java
@@ -0,0 +1,29 @@
+package se.su.dsv.oauth2.shibboleth;
+
+import jakarta.servlet.http.HttpServletRequest;
+import org.springframework.core.log.LogMessage;
+import org.springframework.security.web.authentication.preauth.j2ee.J2eePreAuthenticatedProcessingFilter;
+
+import java.security.Principal;
+
+/// Can't use the built-in [J2eePreAuthenticatedProcessingFilter] because Tomcat
+/// unconditionally provides a [HttpServletRequest#getUserPrincipal()] with a
+/// blank [Principal#getName()] that causes the authentication process to fail.
+class ShibbolethAuthenticatedProcessingFilter extends J2eePreAuthenticatedProcessingFilter {
+    @Override
+    protected Object getPreAuthenticatedPrincipal(final HttpServletRequest httpRequest) {
+        Principal userPrincipal = httpRequest.getUserPrincipal();
+        if (userPrincipal == null) {
+            return null;
+        }
+
+        // Tomcat provides a blank Principal name when the user is not authenticated.
+        String principalName = userPrincipal.getName();
+        if (principalName == null || principalName.isBlank()) {
+            return null;
+        }
+
+        this.logger.debug(LogMessage.format("PreAuthenticated J2EE principal: %s", principalName));
+        return principalName;
+    }
+}
diff --git a/src/main/java/se/su/dsv/oauth2/shibboleth/ShibbolethConfigurer.java b/src/main/java/se/su/dsv/oauth2/shibboleth/ShibbolethConfigurer.java
index 1aa0252..5a9a511 100644
--- a/src/main/java/se/su/dsv/oauth2/shibboleth/ShibbolethConfigurer.java
+++ b/src/main/java/se/su/dsv/oauth2/shibboleth/ShibbolethConfigurer.java
@@ -20,7 +20,7 @@ public class ShibbolethConfigurer extends AbstractHttpConfigurer<ShibbolethConfi
 
     @Override
     public void configure(final HttpSecurity http) {
-        J2eePreAuthenticatedProcessingFilter filter = new J2eePreAuthenticatedProcessingFilter();
+        J2eePreAuthenticatedProcessingFilter filter = new ShibbolethAuthenticatedProcessingFilter();
         filter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
         filter.setAuthenticationDetailsSource(new ShibbolethAuthenticationDetailsSource());
         filter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());