I have a NextJS application that uses a Backend for Frontend Architecture with Spring Security OAuth2 Client and Spring Cloud Gateway, which communicates to my Spring Authorization Server, very similar to this sample.
My webapp is working real nice, I'm getting the SESSION and X-CSRF token from my BFF and are being set in the browser on my NextJS app as cookies, so everything is cool to that point. But my doubt is that I closed the browser window and my session goes away, obviously it happens since both the cookies have MAX-AGE as "Session".
I know that the best practice is to let is as is, let the session either expire by the session timeout or when the browser session ends, but I'm curious to know how to persist the SESSION and X-CSRF cookies after the browser closes, so I have these questions:
- Is it just enough to set the MAX-AGE to something in both the BFF and Spring Authorization Server?
- Is Spring Security Remember Me needed? Though my BFF uses WebFlux Security so that functionality isn't available.
- Should the X-CSRF Cookie also be persisted after the browser is closed, just as the session?
- Should the session timeout equal the max age that I would set for both the cookies?
- Should the X-CSRF token be persisted in a database if I spun up multiple instances of the BFF?
Also I'm confused on how to setup this because of the fact that I do the login on the Spring Authorization Server but I'm also logged in in the BFF since I have the SESSION and X-CSRF token to communicate with my BFF, so I guess that both session configuration should be the same on these two apps since they both create a session cookie even though the browser only gets the BFF one.
Also worth noting that both my BFF and my Spring Authorization Server, use Spring Session with Redis using different namespaces.
Relevant Spring Security configuration in my BFF:
@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
http
.authorizeExchange(authorizeExchange ->
authorizeExchange.anyExchange().authenticated()
)
.exceptionHandling(exceptionHandling ->
exceptionHandling.authenticationEntryPoint(authenticationEntryPoint())
)
.csrf(csrf ->
csrf.csrfTokenRepository(csrfTokenRepository())
)
.cors(Customizer.withDefaults())
.oauth2Login(oauth2 ->
oauth2.authenticationSuccessHandler(authenticationSuccessHandler())
)
.logout(logout ->
logout
.logoutHandler(logoutHandler())
.logoutSuccessHandler(logoutSuccessHandler())
)
.oauth2Client(Customizer.withDefaults());
return http.build();
}
Security Configuration on my Spring Authorization Server:
// AuthorizationServerConfig class
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
return http.cors(Customizer.withDefaults())
.formLogin(Customizer.withDefaults())
.build();
}
// WebSecurityConfig class
@Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeRequests(authorizeRequests ->
authorizeRequests.anyRequest().authenticated()
)
.cors(withDefaults())
.formLogin(withDefaults());
return http.build();
}