Oirampok
Oirampok

Reputation: 649

Spring security custom UsernamePasswordAuthenticationFilter not replacing default UsernamePasswordAuthenticationFilter

I'm trying to implement my own UsernamePasswordAuthenthicationFilter that authenticates every request from my frontend using firebase auth.

public class FireBaseAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
    
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { 
        
        .
        .
        //Assigning roles happens here
        List<GrantedAuthority> authorities = new ArrayList<>();
        if (user.getCustomClaims() != null) {
            if (user.getCustomClaims().get("boss").equals("true")) {
                authorities.add(new SimpleGrantedAuthority("boss"));
            }
            if (user.getCustomClaims().get("admin").equals("true")) {
                authorities.add(new SimpleGrantedAuthority("admin"));
            }
            if (user.getCustomClaims().get("office").equals("true")) {
                authorities.add(new SimpleGrantedAuthority("office"));
            }
            if (user.getCustomClaims().get("warehouse").equals("true")) {
                authorities.add(new SimpleGrantedAuthority("warehouse"));
            }
            if (user.getCustomClaims().get("store").equals("true")) {
                authorities.add(new SimpleGrantedAuthority("store"));
            }

            SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(user.getEmail(), user.getUid(), authorities));

   
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
            user.getEmail(),
                user.getUid(),
                authorities
        );
    
            }

        filterChain.doFilter(request, response);
    }

}

Then i try and replace the default auth in my security config:

public class ApplicationSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().disable().csrf().disable().httpBasic().disable().formLogin().disable()
                .addFilter(new FireBaseAuthenticationFilter())
                .sessionManagement().sessionCreationPolicy(STATELESS).and()
                .authorizeRequests()
                .anyRequest().authenticated();
    }
}

But for some reason my custom filter is never called during runtime? What am I missing here?

Upvotes: 0

Views: 610

Answers (2)

zzpzaf
zzpzaf

Reputation: 158

Your custom implementation extends the UsernamePasswordAuthenticationFilter (which in its turn extends the AbstractAuthenticationProcessingFilter). The UsernamePasswordAuthenticationFilter, by default, is used for .formLogin authentication, handling the default AntRequestMatcher "/login". If you use a different protected endpoint, the filter's attemptAuthentication() method never gets action. So, if you want to use a different matcher (a different protected endpoint), you have to override the default AntRequestMatcher. For instance, you can do so within your custom filter constructor, by using something like that:

super.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/auth/signin", "GET"));

Upvotes: 0

Gurkan İlleez
Gurkan İlleez

Reputation: 1573

Example :

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable().cors().and().authorizeRequests()
                .antMatchers(HttpMethod.POST, ChallengeConstant.AUTHORIZE_ENDPOINT).permitAll()
                .antMatchers(HttpMethod.POST, ChallengeConstant.TOKEN_ENDPOINT).permitAll()
                .antMatchers(HttpMethod.GET, "/*").permitAll()
                .antMatchers(HttpMethod.GET, "/assets/**").permitAll()
                .anyRequest().authenticated()
                .and()
                .addFilterBefore(new JWTFilter(userService, objectMapper), UsernamePasswordAuthenticationFilter.class)
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }

If you want to validate token :

@AllArgsConstructor
public class JWTFilter extends OncePerRequestFilter {

    private static final Logger LOGGER = LoggerFactory.getLogger(JWTFilter.class);
    private final UserService userService;
    private final ObjectMapper objectMapper;

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        String token = httpServletRequest.getHeader(ChallengeConstant.AUTH_HEADER);
        if (token != null) {
            LOGGER.info("The request is authenticated. Performing Token validity");
            String userName;
            try {
                userName = JWT.require(Algorithm.HMAC512(ChallengeConstant.DUMMY_SIGN.getBytes()))
                        .build()
                        .verify(token.replace(ChallengeConstant.TOKEN_PREFIX, ""))
                        .getSubject();
            } catch (JWTVerificationException ex) {
                LOGGER.warn(String.format("Token is not valid. Token: %s", token), ex);
                generateErrorResponse(httpServletResponse, ExceptionResponse.UNAUTHORIZED);
                return;
            }
            LOGGER.info("Token is valid for username: {}", userName);
            try {
                UserEntity userEntity = userService.findUserByName(userName);
                List<GrantedAuthority> authList = userEntity.getAuthorizations()
                        .stream()
                        .map(authorizationEntity -> new SimpleGrantedAuthority(authorizationEntity.getAuth()))
                        .collect(Collectors.toList());
                SecurityContextHolder.getContext().setAuthentication(
                        new UsernamePasswordAuthenticationToken(userEntity.getUserName(), userEntity.getPassword(), authList));
                LOGGER.debug("User has been found by given username: {} with authorities: {}", userName, authList.toString());
            } catch (NotFoundException ex) {
                LOGGER.warn("User couldn't be found with given username: {}", userName);
                generateErrorResponse(httpServletResponse, ExceptionResponse.NOT_FOUND);
                return;
            }
        }
        LOGGER.info("The request is NOT authenticated. It will continue to request chain.");
        filterChain.doFilter(httpServletRequest, httpServletResponse);
    }

    private void generateErrorResponse(HttpServletResponse httpServletResponse, ExceptionResponse exceptionResponse) throws IOException {
        LOGGER.trace("Generating http error response");
        httpServletResponse.setContentType(MediaType.APPLICATION_JSON_VALUE);
        httpServletResponse.setStatus(exceptionResponse.getStatus().value());
        ErrorResource errorResource = new ErrorResource(exceptionResponse.getCode(),
                exceptionResponse.getMessage());
        httpServletResponse.getWriter().write(objectMapper.writeValueAsString(errorResource));
        LOGGER.trace("Error response is {}", errorResource.toString());
        httpServletResponse.getWriter().flush();
    }

I think you can validate in Filter and return error response if it is not valid.

Upvotes: 1

Related Questions