This is an automated email from the ASF dual-hosted git repository.

dakirily pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/qpid-broker-j.git

commit 23003b1db8b591ad686501b5df151f64246db34b
Author: Daniil Kirilyuk <[email protected]>
AuthorDate: Sat Sep 19 19:48:34 2026 +0200

    NO-JIRA: [Broker-J] Renew HTTP management sessions after authentication
---
 .../server/management/plugin/HttpManagement.java   |   2 +
 .../management/plugin/HttpManagementUtil.java      |  27 +-
 .../management/plugin/HttpManagementUtilTest.java  | 114 ++++++-
 .../auth/InteractiveAuthenticationSessionTest.java | 380 +++++++++++++++++++++
 .../auth/OAuth2InteractiveAuthenticatorTest.java   |   7 +
 .../PreemptiveAuthenticationTest.java              | 110 +++++-
 .../qpid/tests/http/authentication/SaslTest.java   |  61 ++--
 .../authentication/SpnegoAuthenticationTest.java   | 235 +++++++++++++
 8 files changed, 894 insertions(+), 42 deletions(-)

diff --git 
a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java
 
b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java
index 07b0e4e550..3eb3b1256e 100644
--- 
a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java
+++ 
b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java
@@ -46,6 +46,7 @@ import javax.net.ssl.SSLSessionContext;
 
 import jakarta.servlet.DispatcherType;
 import jakarta.servlet.MultipartConfigElement;
+import jakarta.servlet.SessionTrackingMode;
 import jakarta.servlet.http.HttpServletRequest;
 
 import org.eclipse.jetty.ee11.servlet.ErrorPageErrorHandler;
@@ -357,6 +358,7 @@ public class HttpManagement extends 
AbstractPluginAdapter<HttpManagement> implem
         corsHandler.setAllowCredentials(getCorsAllowCredentials());
 
         final ServletContextHandler root = new ServletContextHandler("/",  
ServletContextHandler.SESSIONS);
+        
root.getSessionHandler().setSessionTrackingModes(Set.of(SessionTrackingMode.COOKIE));
         root.insertHandler(rewriteHandler);
         root.insertHandler(corsHandler);
         server.setHandler(root);
diff --git 
a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagementUtil.java
 
b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagementUtil.java
index dbe02091b9..9e60fb4d91 100644
--- 
a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagementUtil.java
+++ 
b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagementUtil.java
@@ -152,18 +152,25 @@ public class HttpManagementUtil
         SubjectExecutionContext.withSubject(subject, () -> 
broker.authorise(MANAGE_ACTION));
     }
 
-    public static void saveAuthorisedSubject(HttpServletRequest request, 
Subject subject)
+    public static void saveAuthorisedSubject(final HttpServletRequest request, 
final Subject original)
     {
-        HttpSession session = request.getSession();
-        Broker<?> broker = getBroker(session.getServletContext());
-        HttpPort<?> port =  HttpManagementUtil.getPort(request);
+        final HttpSession session = request.getSession();
+        try
+        {
+            request.changeSessionId();
+        }
+        catch (final IllegalStateException e)
+        {
+            throw new SessionInvalidatedException();
+        }
+
+        final Subject subject = createServletConnectionSubject(request, 
original);
+        final Broker<?> broker = getBroker(session.getServletContext());
+        final HttpPort<?> port = HttpManagementUtil.getPort(request);
         setSessionAttribute(ATTR_SUBJECT, subject, session, request);
-        setSessionAttribute(ATTR_LOGIN_LOGOUT_REPORTER,
-                            new LoginLogoutReporter(subject, broker),
-                            session,
-                            request);
+        setSessionAttribute(ATTR_LOGIN_LOGOUT_REPORTER, new 
LoginLogoutReporter(subject, broker), session, request);
 
-        long absoluteSessionTimeout = port.getAbsoluteSessionTimeout();
+        final long absoluteSessionTimeout = port.getAbsoluteSessionTimeout();
         if (absoluteSessionTimeout > 0)
         {
             scheduleAbsoluteSessionTimeout(request, session, broker, 
absoluteSessionTimeout);
@@ -358,6 +365,6 @@ public class HttpManagementUtil
     {
         final Subject subject = createServletConnectionSubject(request, 
original);
         assertManagementAccess(broker, subject);
-        saveAuthorisedSubject(request, subject);
+        saveAuthorisedSubject(request, original);
     }
 }
diff --git 
a/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/HttpManagementUtilTest.java
 
b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/HttpManagementUtilTest.java
index 719de54ffe..e3af7216e1 100644
--- 
a/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/HttpManagementUtilTest.java
+++ 
b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/HttpManagementUtilTest.java
@@ -22,24 +22,82 @@
 package org.apache.qpid.server.management.plugin;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
-
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
 import java.util.concurrent.atomic.AtomicReference;
 
 import javax.security.auth.Subject;
 
+import jakarta.servlet.ServletContext;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpSession;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.mockito.InOrder;
 
+import 
org.apache.qpid.server.management.plugin.servlet.ServletConnectionPrincipal;
 import org.apache.qpid.server.model.Broker;
+import org.apache.qpid.server.model.port.HttpPort;
+import org.apache.qpid.server.security.AccessDeniedException;
 import org.apache.qpid.server.security.SubjectExecutionContext;
 import org.apache.qpid.server.security.access.Operation;
+import org.apache.qpid.server.security.auth.AuthenticatedPrincipal;
 import org.apache.qpid.test.utils.UnitTestBase;
 
 public class HttpManagementUtilTest extends UnitTestBase
 {
+    private HttpServletRequest _request;
+    private HttpSession _session;
+    private Broker<?> _broker;
+    private Map<String, Object> _attributes;
+
+    @BeforeEach
+    public void setUp()
+    {
+        _request = mock(HttpServletRequest.class);
+        _session = mock(HttpSession.class);
+        _broker = mock(Broker.class);
+        _attributes = new HashMap<>();
+        final HttpPort<?> port = mock(HttpPort.class);
+        final ServletContext context = mock(ServletContext.class);
+        final AtomicReference<String> sessionId = new 
AtomicReference<>("initial-session");
+        
when(_request.getAttribute("org.apache.qpid.server.model.Port")).thenReturn(port);
+        when(port.getId()).thenReturn(UUID.randomUUID());
+        when(_request.getSession()).thenReturn(_session);
+        when(_request.getSession(false)).thenReturn(_session);
+        when(_request.getRemoteHost()).thenReturn("localhost");
+        when(_session.getServletContext()).thenReturn(context);
+        
when(context.getAttribute(HttpManagementUtil.ATTR_BROKER)).thenReturn(_broker);
+        when(_session.getId()).thenAnswer(invocation -> sessionId.get());
+        when(_request.changeSessionId()).thenAnswer(invocation ->
+        {
+            sessionId.set("renewed-session");
+            return sessionId.get();
+        });
+        when(_session.getAttribute(anyString())).thenAnswer(invocation -> 
_attributes.get(invocation.getArgument(0)));
+        doAnswer(invocation ->
+        {
+            _attributes.put(invocation.getArgument(0), 
invocation.getArgument(1));
+            return null;
+        }).when(_session).setAttribute(anyString(), any());
+    }
+
     @Test
     public void testEnsureFilenameIsRfc2183()
     {
@@ -50,18 +108,64 @@ public class HttpManagementUtilTest extends UnitTestBase
     @Test
     public void testAssertManagementAccessUsesSubject()
     {
-        final Broker<?> broker = mock(Broker.class);
         final Subject subject = new Subject();
         final AtomicReference<Subject> capturedSubject = new 
AtomicReference<>();
-
         doAnswer(invocation ->
         {
             capturedSubject.set(SubjectExecutionContext.currentSubject());
             return null;
-        }).when(broker).authorise(any(Operation.class));
+        }).when(_broker).authorise(any(Operation.class));
 
-        HttpManagementUtil.assertManagementAccess(broker, subject);
+        HttpManagementUtil.assertManagementAccess(_broker, subject);
 
         assertEquals(subject, capturedSubject.get(), "Unexpected subject");
     }
+
+    @Test
+    public void testAuthenticationRenewsSessionBeforePublishingSubject()
+    {
+        final AuthenticatedPrincipal principal = new AuthenticatedPrincipal(() 
-> "user");
+        final Subject original = new Subject(true, Set.of(principal), 
Set.of(), Set.of());
+        _attributes.put("negotiationState", "retained");
+
+        
HttpManagementUtil.createServletConnectionSubjectAssertManagementAccessAndSave(_broker,
 _request, original);
+
+        final InOrder order = inOrder(_broker, _request, _session);
+        order.verify(_broker).authorise(any(Operation.class));
+        order.verify(_request).changeSessionId();
+        order.verify(_session).setAttribute(eq(HttpManagementUtil
+                .getRequestSpecificAttributeName("Qpid.subject", _request)), 
any(Subject.class));
+        final Subject saved = 
HttpManagementUtil.getAuthorisedSubject(_request);
+        assertNotNull(saved);
+        assertTrue(saved.isReadOnly());
+        assertEquals(Set.of(principal), 
saved.getPrincipals(AuthenticatedPrincipal.class));
+        assertEquals("retained", _attributes.get("negotiationState"));
+        assertEquals(1, 
saved.getPrincipals(ServletConnectionPrincipal.class).size());
+        assertEquals(new ServletConnectionPrincipal(_request).getSessionId(),
+                
saved.getPrincipals(ServletConnectionPrincipal.class).iterator().next().getSessionId());
+        assertEquals(Set.of(principal), original.getPrincipals());
+    }
+
+    @Test
+    public void testFailedRenewalDoesNotPublishSubject()
+    {
+        when(_request.changeSessionId()).thenThrow(new 
IllegalStateException("Session expired"));
+
+        assertThrows(SessionInvalidatedException.class, () ->
+                HttpManagementUtil.saveAuthorisedSubject(_request, new 
Subject()));
+
+        verify(_session, never()).setAttribute(anyString(), any());
+    }
+
+    @Test
+    public void testDeniedManagementAccessDoesNotRenewSession()
+    {
+        doThrow(new 
AccessDeniedException("Denied")).when(_broker).authorise(any(Operation.class));
+
+        assertThrows(AccessDeniedException.class, () -> HttpManagementUtil
+                
.createServletConnectionSubjectAssertManagementAccessAndSave(_broker, _request, 
new Subject()));
+
+        verify(_request, never()).changeSessionId();
+        verify(_session, never()).setAttribute(anyString(), any());
+    }
 }
diff --git 
a/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/auth/InteractiveAuthenticationSessionTest.java
 
b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/auth/InteractiveAuthenticationSessionTest.java
new file mode 100644
index 0000000000..39278f9f20
--- /dev/null
+++ 
b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/auth/InteractiveAuthenticationSessionTest.java
@@ -0,0 +1,380 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.qpid.server.management.plugin.auth;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.io.Serial;
+import java.security.cert.X509Certificate;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.security.auth.Subject;
+import javax.security.auth.x500.X500Principal;
+
+import jakarta.servlet.ServletException;
+import jakarta.servlet.SessionTrackingMode;
+import jakarta.servlet.http.HttpServlet;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletRequestWrapper;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpSession;
+import jakarta.servlet.http.HttpSessionBindingEvent;
+import jakarta.servlet.http.HttpSessionBindingListener;
+import jakarta.servlet.http.HttpSessionIdListener;
+import org.eclipse.jetty.ee11.servlet.ServletContextHandler;
+import org.eclipse.jetty.ee11.servlet.ServletHolder;
+import org.eclipse.jetty.http.HttpTester;
+import org.eclipse.jetty.server.LocalConnector;
+import org.eclipse.jetty.server.Server;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
+
+import org.apache.qpid.server.logging.EventLogger;
+import org.apache.qpid.server.management.plugin.HttpManagementConfiguration;
+import org.apache.qpid.server.management.plugin.HttpManagementUtil;
+import 
org.apache.qpid.server.management.plugin.HttpRequestInteractiveAuthenticator;
+import 
org.apache.qpid.server.management.plugin.servlet.ServletConnectionPrincipal;
+import org.apache.qpid.server.model.Broker;
+import org.apache.qpid.server.model.port.HttpPort;
+import org.apache.qpid.server.security.SubjectCreator;
+import org.apache.qpid.server.security.auth.AuthenticatedPrincipal;
+import org.apache.qpid.server.security.auth.AuthenticationResult;
+import org.apache.qpid.server.security.auth.SubjectAuthenticationResult;
+import 
org.apache.qpid.server.security.auth.manager.ExternalAuthenticationManager;
+import 
org.apache.qpid.server.security.auth.manager.KerberosAuthenticationManager;
+import org.apache.qpid.test.utils.UnitTestBase;
+
+public class InteractiveAuthenticationSessionTest extends UnitTestBase
+{
+    private final AtomicReference<HttpSession> _session = new 
AtomicReference<>();
+    private final AtomicReference<Runnable> _expiry = new AtomicReference<>();
+    private final AtomicInteger _renewals = new AtomicInteger();
+    private final AtomicInteger _unbindings = new AtomicInteger();
+    private Server _server;
+    private LocalConnector _connector;
+    private Broker<?> _broker;
+    private HttpPort<?> _port;
+    private HttpManagementConfiguration<?> _configuration;
+    private HttpRequestInteractiveAuthenticator _authenticator;
+    private boolean _certificateAvailable;
+    private ScheduledFuture<?> _expiryFuture;
+
+    @BeforeEach
+    public void setUp() throws Exception
+    {
+        _session.set(null);
+        _expiry.set(null);
+        _renewals.set(0);
+        _unbindings.set(0);
+        _certificateAvailable = false;
+        _broker = mock(Broker.class);
+        _port = mock(HttpPort.class);
+        _configuration = mock(HttpManagementConfiguration.class);
+        _expiryFuture = mock(ScheduledFuture.class);
+        when(_broker.getEventLogger()).thenReturn(mock(EventLogger.class));
+        when(_port.getId()).thenReturn(UUID.randomUUID());
+        doReturn(_port).when(_configuration).getPort(any());
+        doAnswer(invocation ->
+        {
+            _expiry.set(invocation.getArgument(2));
+            return _expiryFuture;
+        }).when(_broker).scheduleTask(eq(60000L), eq(TimeUnit.MILLISECONDS), 
any(Runnable.class));
+
+        _server = new Server();
+        _connector = new LocalConnector(_server);
+        _server.addConnector(_connector);
+        final ServletContextHandler context = new 
ServletContextHandler(ServletContextHandler.SESSIONS);
+        context.setContextPath("/");
+        
context.getSessionHandler().setSessionTrackingModes(Set.of(SessionTrackingMode.COOKIE));
+        
context.getServletContext().setAttribute(HttpManagementUtil.ATTR_BROKER, 
_broker);
+        context.addEventListener((HttpSessionIdListener) (event, oldId) -> 
_renewals.incrementAndGet());
+        context.addServlet(new ServletHolder(new SessionServlet()), "/*");
+        _server.setHandler(context);
+        _server.start();
+    }
+
+    @AfterEach
+    public void tearDown() throws Exception
+    {
+        if (_server != null)
+        {
+            _server.stop();
+        }
+    }
+
+    @ParameterizedTest
+    @EnumSource(Mechanism.class)
+    public void testAuthenticationRenewsExistingSession(final Mechanism 
mechanism) throws Exception
+    {
+        configure(mechanism, true);
+        final String previousCookie = cookie(request("/prepare", null));
+        final HttpSession session = _session.get();
+        final String previousId = session.getId();
+        final long creationTime = session.getCreationTime();
+        session.setMaxInactiveInterval(60);
+        final HttpTester.Response response = request("/login", previousCookie);
+        final String renewedCookie = cookie(response);
+
+        assertNotEquals(previousCookie, renewedCookie);
+        assertNotEquals(previousId, session.getId());
+        assertEquals(creationTime, session.getCreationTime());
+        assertEquals(60, session.getMaxInactiveInterval());
+        assertEquals(1, _renewals.get());
+        assertNotNull(session.getAttribute("loginState"));
+        assertEquals(0, _unbindings.get());
+        assertAuditSessionId(session);
+        assertEquals("authenticated", request("/state", 
renewedCookie).getContent());
+        assertEquals("anonymous", request("/state", 
previousCookie).getContent());
+
+        final CompletableFuture<?>[] requests = new CompletableFuture<?>[3];
+        for (int i = 0; i < requests.length; i++)
+        {
+            requests[i] = CompletableFuture.runAsync(() ->
+            {
+                try
+                {
+                    assertEquals("authenticated", request("/state", 
renewedCookie).getContent());
+                }
+                catch (Exception e)
+                {
+                    throw new CompletionException(e);
+                }
+            });
+        }
+        CompletableFuture.allOf(requests).get(10, TimeUnit.SECONDS);
+        assertEquals(1, _renewals.get());
+    }
+
+    @ParameterizedTest
+    @EnumSource(Mechanism.class)
+    public void testAuthenticationCreatesSession(final Mechanism mechanism) 
throws Exception
+    {
+        configure(mechanism, true);
+        final HttpTester.Response response = request("/login", null);
+
+        assertNotNull(cookie(response));
+        assertNotNull(_session.get());
+        assertAuditSessionId(_session.get());
+        assertEquals(1, _renewals.get());
+        assertEquals("authenticated", request("/state", 
cookie(response)).getContent());
+    }
+
+    @ParameterizedTest
+    @EnumSource(Mechanism.class)
+    public void testFailedAuthenticationDoesNotRenewSession(final Mechanism 
mechanism) throws Exception
+    {
+        configure(mechanism, false);
+        final String cookie = cookie(request("/prepare", null));
+
+        assertEquals(HttpServletResponse.SC_UNAUTHORIZED, request("/login", 
cookie).getStatus());
+        assertEquals(0, _renewals.get());
+        assertEquals("anonymous", request("/state", cookie).getContent());
+    }
+
+    @ParameterizedTest
+    @EnumSource(Mechanism.class)
+    public void testAbsoluteTimeoutInvalidatesRenewedSession(final Mechanism 
mechanism) throws Exception
+    {
+        configure(mechanism, true);
+        when(_port.getAbsoluteSessionTimeout()).thenReturn(60000L);
+        final String cookie = cookie(request("/prepare", null));
+        final String renewedCookie = cookie(request("/login", cookie));
+        assertNotNull(_expiry.get());
+        assertEquals(0, _unbindings.get());
+
+        _expiry.get().run();
+
+        assertEquals(1, _unbindings.get());
+        assertEquals("anonymous", request("/state", 
renewedCookie).getContent());
+        verify(_expiryFuture).cancel(false);
+    }
+
+    @ParameterizedTest
+    @EnumSource(Mechanism.class)
+    public void testLogoutInvalidatesRenewedSession(final Mechanism mechanism) 
throws Exception
+    {
+        configure(mechanism, true);
+        final String cookie = cookie(request("/prepare", null));
+        final String renewedCookie = cookie(request("/login", cookie));
+
+        request("/logout", renewedCookie);
+
+        assertEquals(1, _unbindings.get());
+        assertEquals("anonymous", request("/state", 
renewedCookie).getContent());
+    }
+
+    @Test
+    public void testCookieTrackingDoesNotEncodeSessionIdentifiers() throws 
Exception
+    {
+        assertEquals("/resource\n/resource", request("/encode", 
null).getContent());
+    }
+
+    private void configure(final Mechanism mechanism, final boolean successful)
+    {
+        final SubjectCreator creator = mock(SubjectCreator.class);
+        final Subject subject = new Subject(true, Set.of(new 
AuthenticatedPrincipal(() -> "user")), Set.of(), Set.of());
+        when(_port.getSubjectCreator(anyBoolean(), 
anyString())).thenReturn(creator);
+        if (mechanism == Mechanism.SPNEGO)
+        {
+            final KerberosAuthenticationManager provider = 
mock(KerberosAuthenticationManager.class);
+            
doReturn(provider).when(_configuration).getAuthenticationProvider(any());
+            doReturn(_broker).when(provider).getParent();
+            final AuthenticationResult result = 
mock(AuthenticationResult.class);
+            when(result.getStatus()).thenReturn(successful ? 
AuthenticationResult.AuthenticationStatus.SUCCESS :
+                    AuthenticationResult.AuthenticationStatus.ERROR);
+            when(provider.authenticate(any())).thenReturn(result);
+            final SubjectAuthenticationResult authentication = 
mock(SubjectAuthenticationResult.class);
+            when(authentication.getSubject()).thenReturn(subject);
+            
when(creator.createResultWithGroups(result)).thenReturn(authentication);
+            _authenticator = new SpnegoInteractiveAuthenticator();
+        }
+        else
+        {
+            final ExternalAuthenticationManager<?> provider = 
mock(ExternalAuthenticationManager.class);
+            
doReturn(provider).when(_configuration).getAuthenticationProvider(any());
+            doReturn(_broker).when(provider).getParent();
+            
when(creator.createSubjectWithGroups(any(AuthenticatedPrincipal.class))).thenReturn(subject);
+            _certificateAvailable = successful;
+            _authenticator = new SSLClientCertInteractiveAuthenticator();
+        }
+    }
+
+    private void assertAuditSessionId(final HttpSession session)
+    {
+        final HttpServletRequest request = mock(HttpServletRequest.class);
+        when(request.getRemoteHost()).thenReturn("localhost");
+        when(request.getSession(false)).thenReturn(session);
+        final Subject subject = (Subject) session.getAttribute("Qpid.subject." 
+ _port.getId());
+        assertNotNull(subject);
+        assertEquals(new ServletConnectionPrincipal(request).getSessionId(),
+                
subject.getPrincipals(ServletConnectionPrincipal.class).iterator().next().getSessionId());
+    }
+
+    private HttpTester.Response request(final String path, final String 
cookie) throws Exception
+    {
+        return HttpTester.parseResponse(_connector.getResponse("GET " + path + 
" HTTP/1.1\r\nHost: localhost\r\n" +
+                (cookie == null ? "" : "Cookie: " + cookie + "\r\n") + 
"Connection: close\r\n\r\n"));
+    }
+
+    private String cookie(final HttpTester.Response response)
+    {
+        final String cookie = response.get("Set-Cookie");
+        assertNotNull(cookie, "Expected a session cookie");
+        return cookie.split(";", 2)[0];
+    }
+
+    private class SessionServlet extends HttpServlet
+    {
+        @Serial
+        private static final long serialVersionUID = 1L;
+
+        @Override
+        protected void doGet(final HttpServletRequest request, final 
HttpServletResponse response)
+                throws IOException, ServletException
+        {
+            request.setAttribute("org.apache.qpid.server.model.Port", _port);
+            final String path = request.getPathInfo();
+            if ("/prepare".equals(path))
+            {
+                final HttpSession session = request.getSession();
+                session.setAttribute("loginState", new 
HttpSessionBindingListener()
+                {
+                    @Override
+                    public void valueUnbound(final HttpSessionBindingEvent 
event)
+                    {
+                        _unbindings.incrementAndGet();
+                    }
+                });
+                _session.set(session);
+            }
+            else if ("/login".equals(path))
+            {
+                if (_certificateAvailable)
+                {
+                    final X509Certificate certificate = 
mock(X509Certificate.class);
+                    when(certificate.getSubjectX500Principal()).thenReturn(new 
X500Principal("CN=user"));
+                    
request.setAttribute("jakarta.servlet.request.X509Certificate", new 
X509Certificate[]{certificate});
+                }
+                final HttpServletRequest secureRequest = new 
HttpServletRequestWrapper(request)
+                {
+                    @Override
+                    public boolean isSecure()
+                    {
+                        return true;
+                    }
+
+                    @Override
+                    public HttpSession getSession()
+                    {
+                        final HttpSession session = super.getSession();
+                        _session.set(session);
+                        return session;
+                    }
+                };
+                _authenticator.getAuthenticationHandler(secureRequest, 
_configuration).handleAuthentication(response);
+            }
+            else if ("/encode".equals(path))
+            {
+                request.getSession();
+                response.getWriter().write(response.encodeURL("/resource") + 
"\n" +
+                        response.encodeRedirectURL("/resource"));
+            }
+            else if ("/logout".equals(path))
+            {
+                request.getSession(false).invalidate();
+            }
+            else
+            {
+                
response.getWriter().write(HttpManagementUtil.getAuthorisedSubject(request) == 
null ?
+                        "anonymous" : "authenticated");
+            }
+        }
+    }
+
+    private enum Mechanism
+    {
+        SPNEGO,
+        CLIENT_CERTIFICATE
+    }
+}
diff --git 
a/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/auth/OAuth2InteractiveAuthenticatorTest.java
 
b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/auth/OAuth2InteractiveAuthenticatorTest.java
index 9058166e34..e681a16df2 100644
--- 
a/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/auth/OAuth2InteractiveAuthenticatorTest.java
+++ 
b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/auth/OAuth2InteractiveAuthenticatorTest.java
@@ -29,7 +29,9 @@ import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.matches;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -57,6 +59,7 @@ import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
 import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
 
 import org.apache.qpid.server.management.plugin.HttpManagementConfiguration;
 import org.apache.qpid.server.management.plugin.HttpManagementUtil;
@@ -168,6 +171,9 @@ public class OAuth2InteractiveAuthenticatorTest extends 
UnitTestBase
         ArgumentCaptor<String> argument = 
ArgumentCaptor.forClass(String.class);
         verify(mockResponse).sendRedirect(argument.capture());
 
+        final InOrder order = inOrder(mockRequest, mockResponse);
+        order.verify(mockRequest).changeSessionId();
+        order.verify(mockResponse).sendRedirect(TEST_REQUEST);
         assertEquals(TEST_REQUEST, argument.getValue(), "Wrong redirect");
         String attrSubject = 
HttpManagementUtil.getRequestSpecificAttributeName(ATTR_SUBJECT, mockRequest);
         assertNotNull(sessionAttributes.get(attrSubject), "No subject on 
session");
@@ -284,6 +290,7 @@ public class OAuth2InteractiveAuthenticatorTest extends 
UnitTestBase
         HttpServletResponse mockResponse = mock(HttpServletResponse.class);
         authenticationHandler.handleAuthentication(mockResponse);
         verify(mockResponse).sendError(eq(403), any(String.class));
+        verify(mockRequest, never()).changeSessionId();
     }
 
     private Map<String, String> getRedirectParameters(final String 
redirectLocation)
diff --git 
a/systests/qpid-systests-http-management/src/test/java/org/apache/qpid/tests/http/authentication/PreemptiveAuthenticationTest.java
 
b/systests/qpid-systests-http-management/src/test/java/org/apache/qpid/tests/http/authentication/PreemptiveAuthenticationTest.java
index 8fef21364a..611437f09b 100644
--- 
a/systests/qpid-systests-http-management/src/test/java/org/apache/qpid/tests/http/authentication/PreemptiveAuthenticationTest.java
+++ 
b/systests/qpid-systests-http-management/src/test/java/org/apache/qpid/tests/http/authentication/PreemptiveAuthenticationTest.java
@@ -23,15 +23,21 @@ package org.apache.qpid.tests.http.authentication;
 import static jakarta.servlet.http.HttpServletResponse.SC_CREATED;
 import static jakarta.servlet.http.HttpServletResponse.SC_OK;
 import static jakarta.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;
+import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.greaterThan;
 import static org.hamcrest.Matchers.hasKey;
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.not;
 import static org.hamcrest.Matchers.startsWith;
-import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.io.IOException;
+import java.net.HttpCookie;
 import java.net.HttpURLConnection;
 import java.net.InetAddress;
 import java.security.KeyStore;
@@ -133,6 +139,76 @@ public class PreemptiveAuthenticationTest extends 
HttpTestBase
         assertThat(status, is(equalTo(HttpURLConnection.HTTP_OK)));
     }
 
+    @Test
+    public void clientAuthenticationRenewsSession() throws Exception
+    {
+        final HttpTestHelper helper = configForClientAuth("CN=localhost");
+        helper.setUserName(null);
+        final String initialCookie = prepareSession(helper);
+
+        final HttpURLConnection login = 
helper.openManagementConnection(HttpManagement.DEFAULT_LOGIN_URL, "GET");
+        final String renewedCookie;
+        try
+        {
+            login.setInstanceFollowRedirects(false);
+            login.setRequestProperty("Cookie", initialCookie);
+            assertEquals(HttpURLConnection.HTTP_MOVED_TEMP, 
login.getResponseCode());
+            final String header = login.getHeaderField("Set-Cookie");
+            assertNotNull(header);
+            final HttpCookie cookie = HttpCookie.parse(header).get(0);
+            assertTrue(cookie.getSecure(), "HTTPS session cookies must remain 
secure");
+            assertTrue(cookie.isHttpOnly(), "Session cookies must remain 
HttpOnly");
+            renewedCookie = cookie.getName() + "=" + cookie.getValue();
+        }
+        finally
+        {
+            login.disconnect();
+        }
+
+        assertNotEquals(initialCookie, renewedCookie, "Authentication must 
renew the session cookie");
+        assertEquals("localhost", getSessionUser(helper, renewedCookie));
+        assertNull(getSessionUser(helper, initialCookie));
+
+        final HttpURLConnection logout = 
helper.openManagementConnection("/logout", "GET");
+        try
+        {
+            logout.setInstanceFollowRedirects(false);
+            logout.setRequestProperty("Cookie", renewedCookie);
+            assertEquals(HttpURLConnection.HTTP_MOVED_TEMP, 
logout.getResponseCode());
+        }
+        finally
+        {
+            logout.disconnect();
+        }
+        assertNull(getSessionUser(helper, renewedCookie));
+    }
+
+    @Test
+    public void anonymousAuthenticationRenewsSession() throws Exception
+    {
+        final HttpTestHelper helper = configForAnonymous();
+        helper.setUserName(null);
+        final String initialCookie = prepareSession(helper);
+        final HttpURLConnection login = 
helper.openManagementConnection(HttpManagement.DEFAULT_LOGIN_URL, "GET");
+        final String renewedCookie;
+        try
+        {
+            login.setInstanceFollowRedirects(false);
+            login.setRequestProperty("Cookie", initialCookie);
+            assertEquals(SC_OK, login.getResponseCode());
+            final String cookie = login.getHeaderField("Set-Cookie");
+            assertNotNull(cookie);
+            renewedCookie = cookie.split(";", 2)[0];
+        }
+        finally
+        {
+            login.disconnect();
+        }
+        assertNotEquals(initialCookie, renewedCookie);
+        assertEquals("ANONYMOUS", getSessionUser(helper, renewedCookie));
+        assertNull(getSessionUser(helper, initialCookie));
+    }
+
     @Test
     public void clientAuthUnrecognisedCert() throws Exception
     {
@@ -195,6 +271,38 @@ public class PreemptiveAuthenticationTest extends 
HttpTestBase
         assertThat("Unexpected cookie", conn.getHeaderFields(), 
not(hasKey("Set-Cookie")));
     }
 
+    private String prepareSession(final HttpTestHelper helper) throws 
IOException
+    {
+        final HttpURLConnection connection = 
helper.openManagementConnection("/service/sasl", "GET");
+        try
+        {
+            assertEquals(SC_OK, connection.getResponseCode());
+            assertNull(helper.readJsonResponseAsMap(connection).get("user"));
+            final String cookie = connection.getHeaderField("Set-Cookie");
+            assertNotNull(cookie);
+            return cookie.split(";", 2)[0];
+        }
+        finally
+        {
+            connection.disconnect();
+        }
+    }
+
+    private Object getSessionUser(final HttpTestHelper helper, final String 
cookie) throws IOException
+    {
+        final HttpURLConnection connection = 
helper.openManagementConnection("/service/sasl", "GET");
+        try
+        {
+            connection.setRequestProperty("Cookie", cookie);
+            assertEquals(SC_OK, connection.getResponseCode());
+            return helper.readJsonResponseAsMap(connection).get("user");
+        }
+        finally
+        {
+            connection.disconnect();
+        }
+    }
+
     private void verifyGetBroker(int expectedResponseCode) throws Exception
     {
         assertThat(getHelper().submitRequest("broker", "GET"), 
is(equalTo(expectedResponseCode)));
diff --git 
a/systests/qpid-systests-http-management/src/test/java/org/apache/qpid/tests/http/authentication/SaslTest.java
 
b/systests/qpid-systests-http-management/src/test/java/org/apache/qpid/tests/http/authentication/SaslTest.java
index a77fc10ac7..59ee926edf 100644
--- 
a/systests/qpid-systests-http-management/src/test/java/org/apache/qpid/tests/http/authentication/SaslTest.java
+++ 
b/systests/qpid-systests-http-management/src/test/java/org/apache/qpid/tests/http/authentication/SaslTest.java
@@ -32,7 +32,10 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.io.IOException;
 import java.io.OutputStream;
+import java.net.CookieManager;
+import java.net.CookiePolicy;
 import java.net.HttpURLConnection;
+import java.net.URI;
 import java.util.Base64;
 import java.util.List;
 import java.util.Map;
@@ -104,9 +107,10 @@ public class SaslTest extends HttpTestBase
         try
         {
             assertEquals(SC_OK, connection.getResponseCode(), "Unexpected 
response");
-            handleChallengeAndSendResponse(connection, _userName, 
_userPassword, PlainNegotiator.MECHANISM, SC_OK);
+            final List<String> cookies = 
handleChallengeAndSendResponse(connection, _userName, _userPassword,
+                    PlainNegotiator.MECHANISM, SC_OK);
 
-            assertAuthenticatedUser(_userName, 
connection.getHeaderFields().get(SET_COOKIE_HEADER));
+            assertAuthenticatedUser(_userName, cookies);
         }
         finally
         {
@@ -265,8 +269,8 @@ public class SaslTest extends HttpTestBase
     }
 
     private List<String> plainSASLAuthenticationWithInitialResponse(final 
String userName,
-                                                                    final 
String userPassword,
-                                                                    final int 
expectedResponseCode) throws Exception
+                                                                   final 
String userPassword,
+                                                                   final int 
expectedResponseCode) throws Exception
     {
         byte[] responseBytes = generatePlainClientResponse(userName, 
userPassword);
         String responseData = 
Base64.getEncoder().encodeToString(responseBytes);
@@ -299,8 +303,7 @@ public class SaslTest extends HttpTestBase
         HttpURLConnection connection = requestSASLAuthentication(mechanism);
         try
         {
-            handleChallengeAndSendResponse(connection, userName, userPassword, 
mechanism, expectedResponseCode);
-            return connection.getHeaderFields().get(SET_COOKIE_HEADER);
+            return handleChallengeAndSendResponse(connection, userName, 
userPassword, mechanism, expectedResponseCode);
         }
         finally
         {
@@ -309,41 +312,47 @@ public class SaslTest extends HttpTestBase
     }
 
 
-    private void handleChallengeAndSendResponse(HttpURLConnection 
requestChallengeConnection,
-                                                String userName,
-                                                String userPassword,
-                                                String mechanism,
-                                                final int expectedResponseCode)
+    private List<String> handleChallengeAndSendResponse(final 
HttpURLConnection requestChallengeConnection,
+                                                        final String userName,
+                                                        final String 
userPassword,
+                                                        final String mechanism,
+                                                        final int 
expectedResponseCode)
             throws Exception
     {
-        Map<String, Object> response = 
getHelper().readJsonResponseAsMap(requestChallengeConnection);
-        String challenge = (String) response.get("challenge");
+        final Map<String, Object> response = 
getHelper().readJsonResponseAsMap(requestChallengeConnection);
+        final String challenge = (String) response.get("challenge");
         assertNotNull(challenge, "Challenge is not found");
 
-        byte[] challengeBytes = Base64.getDecoder().decode(challenge);
-        byte[] responseBytes = generateClientResponse(mechanism, userName, 
userPassword, challengeBytes);
-        String responseData = 
Base64.getEncoder().encodeToString(responseBytes);
-        String requestParameters = (String.format("id=%s&response=%s", 
response.get("id"), responseData));
+        final byte[] challengeBytes = Base64.getDecoder().decode(challenge);
+        final byte[] responseBytes = generateClientResponse(mechanism, 
userName, userPassword, challengeBytes);
+        final String responseData = 
Base64.getEncoder().encodeToString(responseBytes);
+        final String requestParameters = (String.format("id=%s&response=%s", 
response.get("id"), responseData));
 
-        
postResponse(requestChallengeConnection.getHeaderFields().get(SET_COOKIE_HEADER),
-                     requestParameters,
-                     expectedResponseCode);
+        return 
postResponse(requestChallengeConnection.getHeaderFields().get(SET_COOKIE_HEADER),
+                requestParameters, expectedResponseCode);
     }
 
-    private void postResponse(final List<String> cookies,
-                              final String requestParameters,
-                              final int expectedResponseCode) throws 
IOException
+    private List<String> postResponse(final List<String> cookies,
+                                      final String requestParameters,
+                                      final int expectedResponseCode) throws 
IOException
     {
-        HttpURLConnection authenticateConnection = 
getHelper().openManagementConnection(SASL_SERVICE, "POST");
+        final HttpURLConnection authenticateConnection = 
getHelper().openManagementConnection(SASL_SERVICE, "POST");
         try
         {
             applyCookiesToConnection(cookies, authenticateConnection);
-            try (OutputStream os = authenticateConnection.getOutputStream())
+            try (final OutputStream os = 
authenticateConnection.getOutputStream())
             {
                 os.write(requestParameters.getBytes());
                 os.flush();
-                assertEquals(expectedResponseCode, 
authenticateConnection.getResponseCode(), "Unexpected response code");
+                assertEquals(expectedResponseCode, 
authenticateConnection.getResponseCode(),
+                        "Unexpected response code");
             }
+            final CookieManager cookieManager = new CookieManager(null, 
CookiePolicy.ACCEPT_ALL);
+            final URI uri = 
URI.create(authenticateConnection.getURL().toExternalForm());
+            cookieManager.put(uri, Map.of(SET_COOKIE_HEADER, cookies));
+            cookieManager.put(uri, authenticateConnection.getHeaderFields());
+            return cookieManager.getCookieStore().getCookies().stream()
+                    .map(cookie -> cookie.getName() + "=" + 
cookie.getValue()).toList();
         }
         finally
         {
diff --git 
a/systests/qpid-systests-http-management/src/test/java/org/apache/qpid/tests/http/authentication/SpnegoAuthenticationTest.java
 
b/systests/qpid-systests-http-management/src/test/java/org/apache/qpid/tests/http/authentication/SpnegoAuthenticationTest.java
new file mode 100644
index 0000000000..d7a714cd08
--- /dev/null
+++ 
b/systests/qpid-systests-http-management/src/test/java/org/apache/qpid/tests/http/authentication/SpnegoAuthenticationTest.java
@@ -0,0 +1,235 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.qpid.tests.http.authentication;
+
+import static jakarta.servlet.http.HttpServletResponse.SC_CREATED;
+import static jakarta.servlet.http.HttpServletResponse.SC_OK;
+import static jakarta.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;
+import static org.apache.qpid.server.test.KerberosUtilities.ACCEPT_SCOPE;
+import static 
org.apache.qpid.server.test.KerberosUtilities.CLIENT_PRINCIPAL_FULL_NAME;
+import static 
org.apache.qpid.server.test.KerberosUtilities.CLIENT_PRINCIPAL_NAME;
+import static org.apache.qpid.server.test.KerberosUtilities.HOST_NAME;
+import static org.apache.qpid.server.test.KerberosUtilities.REALM;
+import static 
org.apache.qpid.server.test.KerberosUtilities.SERVICE_PRINCIPAL_NAME;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import java.io.File;
+import java.net.HttpCookie;
+import java.net.HttpURLConnection;
+import java.util.ArrayDeque;
+import java.util.Base64;
+import java.util.Deque;
+import java.util.Map;
+import java.util.Set;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import org.apache.qpid.server.management.plugin.HttpManagement;
+import org.apache.qpid.server.model.AuthenticationProvider;
+import org.apache.qpid.server.model.ConfiguredObject;
+import org.apache.qpid.server.model.Port;
+import org.apache.qpid.server.model.Protocol;
+import org.apache.qpid.server.model.Transport;
+import 
org.apache.qpid.server.security.auth.manager.KerberosAuthenticationManager;
+import org.apache.qpid.server.security.auth.manager.SpnegoAuthenticator;
+import org.apache.qpid.server.test.KerberosUtilities;
+import org.apache.qpid.test.utils.EmbeddedKdcExtension;
+import org.apache.qpid.test.utils.SystemPropertySetter;
+import org.apache.qpid.tests.http.HttpTestBase;
+import org.apache.qpid.tests.http.HttpTestHelper;
+
+public class SpnegoAuthenticationTest extends HttpTestBase
+{
+    private static final String SASL_SERVICE = "/service/sasl";
+    private static final KerberosUtilities UTILS = new KerberosUtilities();
+
+    @RegisterExtension
+    public static final EmbeddedKdcExtension KDC = new 
EmbeddedKdcExtension(HOST_NAME, 0, "QpidHttpTestKerberosServer",
+            REALM);
+
+    @RegisterExtension
+    public static final SystemPropertySetter SYSTEM_PROPERTY_SETTER = new 
SystemPropertySetter();
+
+    private static File _clientKeyTabFile;
+
+    private final Deque<String> _createdObjects = new ArrayDeque<>();
+    private HttpTestHelper _kerberosHelper;
+
+    @BeforeAll
+    public static void configureKerberos() throws Exception
+    {
+        UTILS.prepareConfiguration(HOST_NAME, SYSTEM_PROPERTY_SETTER);
+        _clientKeyTabFile = UTILS.prepareKeyTabs(KDC);
+    }
+
+    @BeforeEach
+    public void configureHttpPort() throws Exception
+    {
+        final String provider = getTestName() + "-kerberos";
+        final String port = getTestName() + "-http";
+        final Map<String, String> context = 
Map.of("qpid.auth.gssapi.spnegoConfigScope", ACCEPT_SCOPE);
+        getHelper().submitRequest("authenticationprovider/" + provider, "PUT",
+                Map.of(AuthenticationProvider.TYPE, 
KerberosAuthenticationManager.PROVIDER_TYPE,
+                        ConfiguredObject.CONTEXT, context), SC_CREATED);
+        _createdObjects.addFirst("authenticationprovider/" + provider);
+        getHelper().submitRequest("port/" + port, "PUT",
+                Map.of(Port.TYPE, "HTTP", Port.PORT, 0, 
Port.AUTHENTICATION_PROVIDER, provider,
+                        Port.PROTOCOLS, Set.of(Protocol.HTTP), 
Port.TRANSPORTS, Set.of(Transport.TCP)), SC_CREATED);
+        _createdObjects.addFirst("port/" + port);
+        final int boundPort = ((Number) getHelper().getJsonAsMap("port/" + 
port).get("boundPort")).intValue();
+        _kerberosHelper = new HttpTestHelper(getBrokerAdmin(), null, 
boundPort);
+        _kerberosHelper.setUserName(null);
+    }
+
+    @AfterEach
+    public void removeHttpPort() throws Exception
+    {
+        while (!_createdObjects.isEmpty())
+        {
+            getHelper().submitRequest(_createdObjects.removeFirst(), "DELETE", 
SC_OK);
+        }
+    }
+
+    @Test
+    public void testInteractiveAuthenticationRenewsSession() throws Exception
+    {
+        final String initialCookie = prepareSession();
+        final String renewedCookie = authenticate(initialCookie);
+
+        assertNotEquals(initialCookie, renewedCookie, "Authentication must 
renew the session cookie");
+        assertEquals(CLIENT_PRINCIPAL_FULL_NAME, 
getSessionUser(renewedCookie));
+        assertNull(getSessionUser(initialCookie));
+    }
+
+    @Test
+    public void testInteractiveAuthenticationCreatesSession() throws Exception
+    {
+        assertEquals(CLIENT_PRINCIPAL_FULL_NAME, 
getSessionUser(authenticate(null)));
+    }
+
+    @Test
+    public void testChallengeDoesNotAuthenticateSession() throws Exception
+    {
+        final String cookie = prepareSession();
+        final HttpURLConnection connection = _kerberosHelper
+                .openManagementConnection(HttpManagement.DEFAULT_LOGIN_URL, 
"GET");
+        try
+        {
+            connection.setRequestProperty("Cookie", cookie);
+            assertEquals(SC_UNAUTHORIZED, connection.getResponseCode());
+            
assertEquals(SpnegoAuthenticator.RESPONSE_AUTH_HEADER_VALUE_NEGOTIATE,
+                    
connection.getHeaderField(SpnegoAuthenticator.RESPONSE_AUTH_HEADER_NAME));
+        }
+        finally
+        {
+            connection.disconnect();
+        }
+        assertNull(getSessionUser(cookie));
+    }
+
+    @Test
+    public void testManagementSessionsUseCookieTracking() throws Exception
+    {
+        final String cookie = authenticate(prepareSession());
+        final String sessionId = HttpCookie.parse(cookie).get(0).getValue();
+        final HttpURLConnection connection = _kerberosHelper
+                .openManagementConnection(SASL_SERVICE + ";jsessionid=" + 
sessionId, "GET");
+        try
+        {
+            assertEquals(SC_OK, connection.getResponseCode());
+            
assertNull(_kerberosHelper.readJsonResponseAsMap(connection).get("user"));
+        }
+        finally
+        {
+            connection.disconnect();
+        }
+        assertEquals(CLIENT_PRINCIPAL_FULL_NAME, getSessionUser(cookie));
+    }
+
+    private String prepareSession() throws Exception
+    {
+        final HttpURLConnection connection = 
_kerberosHelper.openManagementConnection(SASL_SERVICE, "GET");
+        try
+        {
+            assertEquals(SC_OK, connection.getResponseCode());
+            
assertNull(_kerberosHelper.readJsonResponseAsMap(connection).get("user"));
+            return getSessionCookie(connection);
+        }
+        finally
+        {
+            connection.disconnect();
+        }
+    }
+
+    private String authenticate(final String cookie) throws Exception
+    {
+        final byte[] token = UTILS.buildToken(CLIENT_PRINCIPAL_NAME, 
_clientKeyTabFile, SERVICE_PRINCIPAL_NAME);
+        final HttpURLConnection connection = _kerberosHelper
+                .openManagementConnection(HttpManagement.DEFAULT_LOGIN_URL, 
"GET");
+        try
+        {
+            connection.setInstanceFollowRedirects(false);
+            if (cookie != null)
+            {
+                connection.setRequestProperty("Cookie", cookie);
+            }
+            
connection.setRequestProperty(SpnegoAuthenticator.REQUEST_AUTH_HEADER_NAME,
+                    SpnegoAuthenticator.RESPONSE_AUTH_HEADER_VALUE_NEGOTIATE + 
" " +
+                    Base64.getEncoder().encodeToString(token));
+            assertEquals(SC_OK, connection.getResponseCode());
+            return getSessionCookie(connection);
+        }
+        finally
+        {
+            connection.disconnect();
+        }
+    }
+
+    private Object getSessionUser(final String cookie) throws Exception
+    {
+        final HttpURLConnection connection = 
_kerberosHelper.openManagementConnection(SASL_SERVICE, "GET");
+        try
+        {
+            connection.setRequestProperty("Cookie", cookie);
+            assertEquals(SC_OK, connection.getResponseCode());
+            return 
_kerberosHelper.readJsonResponseAsMap(connection).get("user");
+        }
+        finally
+        {
+            connection.disconnect();
+        }
+    }
+
+    private String getSessionCookie(final HttpURLConnection connection)
+    {
+        final String cookie = connection.getHeaderField("Set-Cookie");
+        assertNotNull(cookie, "Expected a management session cookie");
+        return cookie.split(";", 2)[0];
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to