Logout

Default Logout Behaviour

LogoutFilter handles POST /logout. It:

  1. invalidates the HTTP session,
  2. clears the SecurityContext and the remember-me cookie (if any),
  3. redirects to /login?logout.

Note: Logout must be a POST with a CSRF token. Otherwise an evil page could log you out with a simple <img src="/logout">. With CSRF enabled, GET /logout just shows a confirmation page.

Customising Logout

http
    .logout(logout -> logout
        .logoutUrl("/logout")                    // default
        .logoutSuccessUrl("/?loggedOut")         // where to go afterwards
        .invalidateHttpSession(true)             // default
        .clearAuthentication(true)               // default
        .deleteCookies("JSESSIONID"));
<form th:action="@{/logout}" method="post">
  <button type="submit">Log out</button>
</form>

Logout for an API

A JSON API does not want a redirect. Return a status code instead with HttpStatusReturningLogoutSuccessHandler:

.logout(logout -> logout
    .logoutUrl("/api/auth/logout")
    .logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler(HttpStatus.NO_CONTENT)))

Response

curl -i -X POST -b cookies.txt -H "X-XSRF-TOKEN: 4f1d..." localhost:8080/api/auth/logout

Example output

HTTP/1.1 204

Exercise: Configure logout so that it redirects to /goodbye and deletes the JSESSIONID cookie. Remember to make /goodbye public.

Show answer
http
    .authorizeHttpRequests(auth -> auth
        .requestMatchers("/goodbye").permitAll()
        .anyRequest().authenticated())
    .formLogin(Customizer.withDefaults())
    .logout(logout -> logout
        .logoutSuccessUrl("/goodbye")
        .deleteCookies("JSESSIONID"));