Reputation: 1219
I have implemented the JWT authentication and authorization. Everything is working fine, besides the unauthorized scenario
Unauthorized scenario: making a http call to a route without providing a authorization token.
Result: 403 forbidden instead of unauthorized
Here is my code:
@Override
protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, ServletException {
String header = req.getHeader(HEADER_STRING);
if (header == null || !header.startsWith(TOKEN_PREFIX)) {
chain.doFilter(req, res);
return;
}
UsernamePasswordAuthenticationToken authentication = getAuthentication(req);
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(req, res);
}
After the
if (header == null || !header.startsWith(TOKEN_PREFIX)) {
chain.doFilter(req, res);
return;
}
Executes, the response is 403
Here is my full class:
public class JWTAuthorizationFilter extends BasicAuthenticationFilter {
public JWTAuthorizationFilter(AuthenticationManager authenticationManager) {
super(authenticationManager);
}
@Override
protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, ServletException {
String header = req.getHeader(HEADER_STRING);
if (header == null || !header.startsWith(TOKEN_PREFIX)) {
chain.doFilter(req, res);
return;
}
UsernamePasswordAuthenticationToken authentication = getAuthentication(req);
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(req, res);
}
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
String token = request.getHeader(HEADER_STRING);
if (token != null) {
// setting the user in the security context
String user = JWT.require(Algorithm.HMAC512(SECRET.getBytes()))
.build()
.verify(token.replace(TOKEN_PREFIX, ""))
.getSubject();
if(user != null){
return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
}
return null;
}
return null;
}
}
Remark:
I had the same problem with UsernamePasswordAuthenticationFilter, and I solved it by overriding the default authenticationFailureHandler:
setAuthenticationFailureHandler(new JWTAuthenticationFailureHandler());
How can I get the correct 401 response code and body?
Thanks!
Upvotes: 4
Views: 2872
Reputation: 896
Perhaps this will help someone encountering similar behavior in using spring boot 3.1.x I had a similar issue and fixed it in my securityFilterChain method. The solution was to add the below line to the filterChain.
.httpBasic(Customizer.withDefaults());
Without HTTP Basic authentication configured, the server won't send a 401 Unauthorized status with a WWW-Authenticate header in response to unauthorized requests; instead, it might simply deny access or redirect to a login page depending on how your security is set up.
Upvotes: 1
Reputation: 8213
If you look at what BasicAuthenticationFilter
which you are overriding with JWTAuthorizationFilter
does when authentication fails, it calls authenticationEntryPoint.commence(request, response, failed)
which sends 401
BasicAuthenticationEntryPoint
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException {
response.addHeader("WWW-Authenticate", "Basic realm=\"" + realmName + "\"");
response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase());
}
But you have behaviour that overridden and returning null. So instead of that try one of the following:
BadCredentialsException
where you are returning nullresponse.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase());
Upvotes: 3