class AmbariJwtAuthenticationFilter implements AmbariAuthenticationFilter {

  @Override
  public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {

    eventHandler.beforeAttemptAuthentication(this, servletRequest, servletResponse);

    JwtAuthenticationProperties jwtProperties = propertiesProvider.get();
    if (jwtProperties == null || !jwtProperties.isEnabledForAmbari()) {
      //disable filter if not configured
      chain.doFilter(servletRequest, servletResponse);
      return;
    }

    HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
    HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;

    try {
      String serializedJWT = getJWTFromCookie(httpServletRequest);
      if (serializedJWT != null && isAuthenticationRequired(serializedJWT)) {
        try {
          SignedJWT jwtToken = SignedJWT.parse(serializedJWT);

          boolean valid = validateToken(jwtToken);

          if (valid) {
            String userName = jwtToken.getJWTClaimsSet().getSubject();

            Authentication authentication = authenticationProvider.authenticate(new JwtAuthenticationToken(userName, serializedJWT, null));
            SecurityContextHolder.getContext().setAuthentication(authentication);

            eventHandler.onSuccessfulAuthentication(this, httpServletRequest, httpServletResponse, authentication);
          } else {
            throw new BadCredentialsException("Invalid JWT token");
          }
        } catch (ParseException e) {
          LOG.warn("Unable to parse the JWT token", e);
          throw new BadCredentialsException("Unable to parse the JWT token - " + e.getLocalizedMessage());
        }
      } else {
        LOG.trace("No JWT cookie found, do nothing");
      }

      chain.doFilter(servletRequest, servletResponse);
    } catch (AuthenticationException e) {
      LOG.warn("JWT authentication failed - {}", e.getLocalizedMessage());

      //clear security context if authentication was required, but failed
      SecurityContextHolder.clearContext();

      AmbariAuthenticationException cause;
      if (e instanceof AmbariAuthenticationException) {
        cause = (AmbariAuthenticationException) e;
      } else {
        cause = new AmbariAuthenticationException(null, e.getMessage(), false, e);
      }

      eventHandler.onUnsuccessfulAuthentication(this, httpServletRequest, httpServletResponse, cause);

      //used to indicate authentication failure, not used here as we have more than one filter
      ambariEntryPoint.commence(httpServletRequest, httpServletResponse, e);
    }
  }

    AmbariJwtAuthenticationFilter(AuthenticationEntryPoint ambariEntryPoint,
                                JwtAuthenticationPropertiesProvider propertiesProvider,
                                AmbariJwtAuthenticationProvider authenticationProvider,
                                AmbariAuthenticationEventHandler eventHandler);

}