I am facing an issue related to the authentication manager. My stack includes Spring Security 6.2 and Spring Boot 3.2.
When I authenticate with an existing user and its password, Spring Boot responds pretty quickly. However, when I use an incorrect password, Spring Boot attempts to authenticate until it reaches a catch exception for wrong credentials, which is expected. The problem is that the process continues through the method, gets stuck, and never returns a response (inside of ProviderManager file).
AuthController.java
@PostMapping("login")
public ResponseEntity<LoginResponseDTO> login(@RequestBody LoginRequestDTO loginRequestDTO){
User user = userService.findUserByEmail(loginRequestDTO.getEmail());
if(user == null)
return new ResponseEntity<>(null, HttpStatus.NOT_FOUND);
UsernamePasswordAuthenticationToken upat = new UsernamePasswordAuthenticationToken(
loginRequestDTO.getEmail(), loginRequestDTO.getPassword()
);
Authentication authentication;
try {
authentication = authenticationManager.authenticate(upat); // I added the try-catch block hoping to reach the catch, but it never does, and the method hangs without returning a response
} catch (AuthenticationException e){ // this catch is never reached
return new ResponseEntity<>(null, HttpStatus.UNAUTHORIZED);
}
SecurityContextHolder.getContext().setAuthentication(authentication);
String token = TokenUtils.createToken(user);
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.set("Authorization", "Bearer " + token);
return new ResponseEntity<>(new LoginResponseDTO(user, token, TokenUtils.createRefreshToken()), responseHeaders, HttpStatus.OK);
}
ProviderManager.java (file created by springframework)
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException { [...Start of the method]
try {
result = provider.authenticate(authentication);
if (result != null) {
copyDetails(authentication, result);
break;
}
}
catch (AccountStatusException | InternalAuthenticationServiceException ex) {
prepareException(ex, authentication);
// SEC-546: Avoid polling additional providers if auth failure is due to
// invalid account status
throw ex;
}
catch (AuthenticationException ex) {
lastException = ex; // This catch is invoked but the process keeps going
}
}
if (result == null && this.parent != null) { // The process goes inside this and gets stuck
// Allow the parent to try.
try {
parentResult = this.parent.authenticate(authentication);
result = parentResult;
}
catch (ProviderNotFoundException ex) {
// ignore as we will throw below if no other exception occurred prior to
// calling parent and the parent
// may throw ProviderNotFound even though a provider in the child already
// handled the request
}
catch (AuthenticationException ex) {
parentException = ex;
lastException = ex;
}
} [...Rest of the code]
}
SecurityConfig.java
@Configuration @AllArgsConstructor @EnableMethodSecurity public class SecurityConfig {
@Autowired
private final UserDetailsService userDetailsService;
@Autowired
private final JWTAuthorizationFilter jwtAuthorizationFilter;
@Bean
SecurityFilterChain filterChain (HttpSecurity http, AuthenticationManager authManager) throws Exception {
http
.addFilterBefore(jwtAuthorizationFilter, UsernamePasswordAuthenticationFilter.class)
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers("/api/auth*", "/api/auth/**").permitAll()
.requestMatchers("/api/leader*", "/api/leader/**").hasAnyAuthority("LEADER", "ADMIN")
.requestMatchers("/api/admin*", "/api/admin/**").hasAnyAuthority("ADMIN", "SUBADMIN")
.anyRequest().authenticated()
).csrf(AbstractHttpConfigurer::disable);
// @formatter:on
return http.build();
}
@Bean
public UserDetailsService userDetailsService() {
UserDetails user = User.builder().passwordEncoder(passwordEncoder()::encode)
.username("user")
.password("password")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
@Bean
AuthenticationManager authManager(HttpSecurity httpSecurity) throws Exception {
AuthenticationManagerBuilder authManagerBuilder = httpSecurity.getSharedObject(AuthenticationManagerBuilder.class);
authManagerBuilder.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
return authManagerBuilder.build();
}
@Bean
PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
} }