I'm building an API with Springboot and Springboot Security, alongside a frontend to interact with this API. I've tested all the endpoints which work fine in Postman, but when sending POST requests with Axios to the API (in my case, for the login request to generate the JWT), I'm receiving a 401 error, which is odd since there shouldn't be any authorization required for this request.
When trying to troubleshoot this, most posts were just issues from incorrectly passing the Auth token into the request, but that isn't the case here since the endpoint requires no auth token (see the successful postman request). I found other similar posts pointing out that it may be a cors related issue, but after tweaking the Controller to include the relevant @CrossOrigin
annotation, and testing the results when exluding .cors()
from filterChain
, the issue persisted with little for me to utilize for troubleshooting.
SecurityFilterChain
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig {
@Autowired
private AuthEntryPointJwt authorizationHandler;
@Autowired
UserDetailsServiceImpl userDetailsService;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().exceptionHandling().authenticationEntryPoint(authorizationHandler).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests()
.antMatchers("/api/v1/auth/**").permitAll()
.antMatchers("/api/v1/reviews/**").permitAll()
.antMatchers("/api/v1/test/**").permitAll().anyRequest().authenticated();
http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); }
@Bean
public AuthTokenFilter authenticationJwtTokenFilter() { return new AuthTokenFilter(); }
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
}
Controller
@CrossOrigin(origins = "http://localhost:5173", maxAge = 3600)
@RestController
@RequestMapping("/api/v1/auth")
public class AuthController {
@Autowired
AuthenticationManager authenticationManager;
@Autowired
UserRepository userRepository;
@Autowired
PasswordEncoder encoder;
@Autowired
JwtUtils jwtUtils;
@PostMapping("/signin")
public ResponseEntity<?> authenticateUser(@RequestBody LoginRequest loginRequest) {
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword())
);
SecurityContextHolder.getContext().setAuthentication(authentication);
String jwt = jwtUtils.generateJwtToken(authentication);
UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal();
return ResponseEntity.ok(new JwtResponse(jwt, userDetails.getId(), userDetails.getUsername(), userDetails.getEmail()));
}
auth.service.js
import axios from "axios";
const API_URL = "http://localhost:8080/api/v1/auth/";
export default class AuthService {
login(username, password) {
console.log(API_URL + "signin");
console.log(username, password);
return axios
.post(API_URL + "signin", { username, password })
.then((response) => {
if (response.data.accessToken) {
localStorage.setItem("user", JSON.stringify(response.data));
}
return response.data;
});
}
logout() {
localStorage.removeItem("user");
}
register(username, email, password) {
return axios.post(API_URL + "signup", {
username,
email,
password,
});
}
}
Successful Postman Request
Unsuccessful Axios Request
Request Headers (From Network Tab)
// GENERAL
Request URL: http://localhost:8080/api/v1/auth/signin
Request Method: POST
Status Code: 401
Remote Address: [::1]:8080
Referrer Policy: strict-origin-when-cross-origin
// RESPONSE HEADERS
HTTP/1.1 401
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Access-Control-Allow-Origin: http://localhost:5173
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Content-Length: 0
Date: Tue, 27 Jun 2023 20:38:38 GMT
Keep-Alive: timeout=60
Connection: keep-alive
// REQUEST HEADERS
POST /api/v1/auth/signin HTTP/1.1
Accept: application/json, text/plain, */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-GB,en-US;q=0.9,en;q=0.8
Connection: keep-alive
Content-Length: 49
Content-Type: application/json
Host: localhost:8080
Origin: http://localhost:5173
Referer: http://localhost:5173/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36
sec-ch-ua: "Not.A/Brand";v="8", "Chromium";v="114", "Google Chrome";v="114"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
I've tried to include as much information as I can but if there's anything else I can provide that I've missed out that'd help troubleshoot the issue, let me know.