Describe the bug

When my application is running on Tomcat with context path /foo,
JSESSIONID session cookie is NOT deleted although http.logout().deleteCookies("JSESSIONID") is set.
As the result, old (invalid) session cookie will send from browser just after logout succeeded.

Tomcat set Set-Cookie response header with Path=/foo attribute. Path does not have trailing slash, which is the default behaviour since Tomcat 8.0.37.
On the other hand, when user logs out, Set-Cookie header with Max-Age=0; Path=/foo/ is set by CookieClearingLogoutHandler. The point is that Path attribute has trailing slash so the cookie saved in browser is not deleted.

public final class CookieClearingLogoutHandler implements LogoutHandler {
    private final List<Function<HttpServletRequest, Cookie>> cookiesToClear;

    public CookieClearingLogoutHandler(String... cookiesToClear) {
        Assert.notNull(cookiesToClear, "List of cookies cannot be null");
        List<Function<HttpServletRequest, Cookie>> cookieList = new ArrayList<>();
        for (String cookieName : cookiesToClear) {
            Function<HttpServletRequest, Cookie> f = (request) -> {
                Cookie cookie = new Cookie(cookieName, null);
                String cookiePath = request.getContextPath() + "/";        // <-- Always add trailing slash! It does not correspond to Tomcat default behaviour.
                cookie.setPath(cookiePath);
                cookie.setMaxAge(0);
                cookie.setSecure(request.isSecure());
                return cookie;
            };
            cookieList.add(f);
        }
        this.cookiesToClear =  cookieList;
    }
   // ...
}

To Reproduce

In application.properties, change servlet context path from default to other path value. Here I set /foo.

server.servlet.context-path=/foo

Secuirty config settings.

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().permitAll()
            .and()
            .logout()
                .deleteCookies("JSESSIONID")
                .logoutSuccessUrl("/")
            .and()
            .sessionManagement()
                .invalidSessionUrl("/sessionTimeout");  // to visualize that invalid session cookie is send.
    }
}

When a user logout the application, redirect to /foo/ with old JSESSIONID cookie and then redirect to /error.

Expected behavior

When user logout, JSESSIONID cookie with Path=/foo should be deleted with above settings.

Though some persons may use tomcat with sessionCookiePathUsesTrailingSlash=true option, the default value of current Tomcat is false.
In addition, With current Spring Boot, 2.3, the option cannot be changed in application.properties.
So CookieClearingLogoutHandler should be able to delete cookies without trailing slash.

Sample

https://github.com/wkwkhautbois/session-cookie-delete-bug

Related Issue

2325

Comment From: eleftherias

Thanks for the sample @wkwkhautbois!

I have updated the title because this appears to be an issue for any cookie, not specifically JSESSIONID.

We will be updating the default cookie path to not include the trailing slash. As a workaround for now, you could use your own CookieClearingLogoutHandler that sets the correct path.

@Override
protected void configure(HttpSecurity http) throws Exception {
    Cookie sessionCookie = new Cookie("JSESSIONID", null);
    sessionCookie.setPath("/foo");
    sessionCookie.setMaxAge(0);
    CookieClearingLogoutHandler logoutHandler = new CookieClearingLogoutHandler(sessionCookie);
    http.authorizeRequests()
            .anyRequest().permitAll()
        .and()
        .logout()
            .addLogoutHandler(logoutHandler);
}

Comment From: wkwkhautbois

Thank you for updateing the title, and providing a workaround! I try to use the workaround you told me. And I'm looking forward to updating the default cookie path behaviour.

Comment From: wkwkhautbois

Any updates on this issue? It seems that CookieClearingLogoutHandler has not been changed except for refactoring.

Comment From: eleftherias

Change in Passivity

This applies to applications that customize the servlet context path. The CookieClearingLogoutHandler will no longer append a trailing slash (/) to the context path when setting the cookie path. Instead, it will simply set the cookie path to the context path.

This fixes the issue of not properly deleting cookies when the servlet context path is set.

Background

In gh-2325, we changed the default behaviour of the CookieClearingLogoutHandler to set the cookie path to the context path plus a trailing slash (/), when deleting a cookie. This is because, at the time, Tomcat set the default value for sessionCookiePathUsesTrailingSlash to true, meaning it would append a trailing slash to the cookie path (/foo became /foo/).

As of Tomcat 8.0.37 and 7.0.71 the default value for sessionCookiePathUsesTrailingSlash changed to false. See the changelogs for 8.0.37 and 7.0.71. This means that Tomcat no longer adds a trailing slash to the path associated with the session cookie. (/foo remains /foo).

This aligns with how Jetty and Undertow behave as well.

Because the defaults no longer append a trailing slash, the default behaviour of CookieClearingLogoutHandler does not append a trailing slash either.