Spring security BasicAuthenticationFilter returns 403 instead of 401

Viewed 1075

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!

1 Answers

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:

  • Throw BadCredentialsException where you are returning null
  • Do response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase());
Related