Skip to content

SessionCreationPolicy.STATELESS is not applied in custom DSL #14237

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
nils-christian opened this issue Dec 2, 2023 · 3 comments
Closed

SessionCreationPolicy.STATELESS is not applied in custom DSL #14237

nils-christian opened this issue Dec 2, 2023 · 3 comments
Assignees
Labels
in: config An issue in spring-security-config status: declined A suggestion or change that we don't feel we should currently apply type: bug A general bug

Comments

@nils-christian
Copy link

Hi,

I am aware that this has already been discussed in #13840, but- in my opinion - this is a bug. Let me provide some details.

Describe the bug

There seems to be a difference in the configuration depending on whether the code

.sessionManagement(session -> session
  .sessionCreationPolicy(SessionCreationPolicy.STATELESS))

is used either in a custom DSL or in the creation of the security filter chain

To Reproduce

Please take a look at the following example:

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @Bean
    SecurityFilterChain restSecurityFilterChain(HttpSecurity http) throws Exception {
        return http
                .authorizeHttpRequests(a -> a
                        .anyRequest()
                        .authenticated())
                // The session management is only stateless if those lines are active
                // .sessionManagement(session -> session
                //         .sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .with(PlatformDefaults.forRest(), Customizer.withDefaults())
                .build();
    }

    @Bean
    public UserDetailsService users() {
        UserDetails user = User
                .withDefaultPasswordEncoder()
                .username("user")
                .password("password")
                .build();
        return new InMemoryUserDetailsManager(user);
    }

    @RestController
    @RequestMapping("/api")
    static public class APIController {

        @GetMapping("/")
        ResponseEntity<String> getMessage() {
            return ResponseEntity.ok("Hello from the API Controller");
        }

    }

    public interface PlatformDefaults {

        static PlatformDefaultsForRest forRest() {
            return new PlatformDefaultsForRest();
        }

        final class PlatformDefaultsForRest extends AbstractHttpConfigurer<PlatformDefaultsForRest, HttpSecurity> {

            @Override
            public void init(HttpSecurity http) throws Exception {
                http
                        .sessionManagement(session -> session
                                .sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                        .httpBasic(Customizer.withDefaults());
            }

            @Override
            public void configure(HttpSecurity http) throws Exception {
            }

        }

    }

}

Notice that the stateless session management is configured in the init method as stated in the documentation ("any method that adds another configurer must be done in the init method").

Now I call the rest controller with curl:

curl --header "Authorization: Basic dXNlcjpwYXNzd29yZA==" --header "X-Requested-With: XMLHttpRequest" -i http://localhost:8080/api/

The server sets a session cookie:

Set-Cookie: JSESSIONID=DDF801BE60FD705D98001936219E04D9; Path=/; HttpOnly

If, on the other hand, I use the same lines inside the restSecurityFilterChain, the same request does not trigger a session cookie. Also, if I add a HttpSessionEventPublisher, the effect can also be seen on the server side, as the HttpSessionCreatedEvent is published if the management is only configured in the custom DSL.

Expected behavior

The session should not be created in either case. The code in both cases should result in the same configuration.

Even if this is not a bug, this is at least a very weird behaviour and should be documented. If this is - from your side - the expected behaviour, the documentation does not state clearly what a custom DSL is allowed to do and what code it must not execute.

Thank you and best regards

Nils

@nils-christian nils-christian added status: waiting-for-triage An issue we've not yet triaged type: bug A general bug labels Dec 2, 2023
@marcusdacoregio marcusdacoregio self-assigned this Dec 6, 2023
@marcusdacoregio marcusdacoregio added in: config An issue in spring-security-config and removed status: waiting-for-triage An issue we've not yet triaged labels Dec 6, 2023
@marcusdacoregio
Copy link
Contributor

Hi, @nils-christian. Thanks for the report.

Would you mind sharing what are you trying to achieve?

If you do not want authentication to be saved in the HTTP Session, you can provide another SecurityContextRepository implementation that fits your needs, like the RequestAttributeSecurityContextRepository.

I see that that is not an ideal behavior and looks like a bug, however, we are considering deprecating the SessionManagementFilter and its configurations since all of them could be achieved by other means and the security builders are a really sensitive part to touch.

@marcusdacoregio marcusdacoregio added the status: waiting-for-feedback We need additional information before we can continue label Dec 6, 2023
@nils-christian
Copy link
Author

nils-christian commented Dec 7, 2023

Hi @marcusdacoregio ,

Sure. I need to provide various default security configurations for different use cases (stateless REST API without CSRF, stateful frontend API with CSRF, ...).

In some cases applications want to define a security chain for a frontend API under path A and another security chain for a REST API under path B. So I want to make sure that the REST API calls to path B are stateless, while the calls to path A create a session.

In the meantime we solved the issue by providing configuration methods that get the HttpSecurity (the builder) as input, so this ticket is no longer an actual issue for us, but I still think that the behaviour of the DSL is weird at this specific point.

Best regards

Nils

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Dec 7, 2023
@marcusdacoregio
Copy link
Contributor

So I want to make sure that the REST API calls to path B are stateless, while the calls to path A create a session.

To avoid saving the SecurityContext in an HTTP session you can use the RequestAttributeSecurityContextRepository with the securityContext DSL.

Disabling configurers, like CSRF, is fine to do in a custom DSL, however, when we are dealing with shared objects, that becomes a little trickier and I'd rather put effort into simplifying session management via #12612 instead of trying to fix that bug which is very likely to produce side effects.

That said, I will close this issue since we do have workarounds and there is a plan to replace the SessionManagementFilter and related DSL, thus making the fix of this bug not worth the effort for now.

If someone still thinks that this can produce errors that do not have a proper workaround, we can reopen it. Thank you so much for your contribution @nils-christian.

@marcusdacoregio marcusdacoregio added status: declined A suggestion or change that we don't feel we should currently apply and removed status: feedback-provided Feedback has been provided labels Dec 8, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: config An issue in spring-security-config status: declined A suggestion or change that we don't feel we should currently apply type: bug A general bug
Projects
None yet
Development

No branches or pull requests

3 participants