SpringWebFlux security - update auth details in session

1k views Asked by At

I am using Spring boot 2.5.6 with webflux security.

@EnableWebFluxSecurity
public class AdminSecurityConfig {
          
    @Bean
    public SecurityWebFilterChain securitygWebFilterChain(final ServerHttpSecurity http,
        final ReactiveAuthenticationManager authManager,
        final ServerSecurityContextRepository securityContextRepository,
        final MyAuthenticationFailureHandler failureHandler) {
               
        http.securityContextRepository(securityContextRepository);

        return http.authorizeExchange().matchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
                        .pathMatchers(props.getSecurity().getIgnorePatterns()).permitAll()
                        .pathMatchers("/api/v1/service/test").hasAuthority("DEFAULT")
                        .anyExchange().authenticated()
                        .and()
                        .formLogin()
                        .loginPage("/login")
                        .authenticationSuccessHandler(authSuccessHandler())
                        .and()
                        .exceptionHandling()
                        .authenticationEntryPoint((exchange, exception) -> Mono.error(exception))
                        .accessDeniedHandler((exchange, exception) -> Mono.error(exception))
                        .and()
                        .build();
            }
    
    @Bean
    public PasswordEncoder passwordEncoder() {
        // return new BCryptPasswordEncoder();
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }
    
    @Bean
    public ReactiveAuthenticationManager authenticationManager() {
        final UserDetailsRepositoryReactiveAuthenticationManager authenticationManager = new UserDetailsRepositoryReactiveAuthenticationManager(
                    userDetailsService);
        authenticationManager.setPasswordEncoder(passwordEncoder());
        return authenticationManager;
    }
    
    @Bean
    public ServerSecurityContextRepository securityContextRepository() {
        final WebSessionServerSecurityContextRepository securityContextRepository = new WebSessionServerSecurityContextRepository();
        securityContextRepository.setSpringSecurityContextAttrName("my-security-context");
        return securityContextRepository;
    }
}
Mono<Principal> principal = ReactiveSecurityContextHolder.getContext().map(SecurityContext::getAuthentication).cast(Principal.class);

final MyAppUserDetails user = (MyAppUserDetails) ((UsernamePasswordAuthenticationToken) principal) .getPrincipal();

Here I am able to retrieve the logged in user details. The MyAppUserDetails will have user details like firstName, lastName, email, user id, org id, .... Now, I would like to update the user details in session after the user is logged in, say change the user name without asking the user to logout and login.

I tried the code below, but not sure how to set the credentials and set the updated user into the security context so that the next get current user call from security context will return the updated user.

final MyAppUserDetails appUser = new MyAppUserDetails("firstName", "lastName", "email", 1, 4);
        UsernamePasswordAuthenticationToken authentication  = new UsernamePasswordAuthenticationToken(appUser, ....);
        ReactiveSecurityContextHolder.withAuthentication(authentication);
1

There are 1 answers

1
user1578872 On BEST ANSWER
  1. Get session from request.
  2. Get context from session
  3. Create new user
  4. Set the new user in the context
public Mono<ServerResponse> updateSession(ServerRequest request) {
       return request.exchange().getSession().flatMap(session -> {
          final SecurityContext context = session.getAttribute("app-security-context");
          AppUserDetails newUser = AppUserDetails.of(...);
          UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(newUser, pwd, authorities);
          context.setAuthentication(token);
       }
    }

It is working, but not sure why do we need to save the context with the approach below.

serverSecurityContextRepository.save(request.exchange(), context);

It works without the above call.