angularjsspring-securitypopupbasic-authenticationwww-authenticate

Spring Security (4.0.1) Integration with AngularJS. Getting Basic Authentication Popup every time user enters invalid credentials


I am trying to integrate Spring Security (4.0.1) with AngularJS. I am able to do basic authentication using XML based configuration. The problem is, Web browser displays Pop up every time user enters invalid credentials. I have tried to remove WWW-Authenticate repsone header using plain ServletFilters as well as using Spring security based Custom filters. No success yet. Can anybody help me on this?


Solution

  • Extend default ExceptionTranslationFilter that returns an HTTP 401 for all ajax requests instead of a basic authentication challenge:

    package mypackage;
    
    import java.io.IOException;
    
    import javax.servlet.FilterChain;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.core.AuthenticationException;
    import org.springframework.security.web.AuthenticationEntryPoint;
    import org.springframework.security.web.access.ExceptionTranslationFilter;
    
    public class AjaxExceptionTranslationFilter extends ExceptionTranslationFilter {
    
        @Autowired
        public AjaxExceptionTranslationFilter(AuthenticationEntryPoint authenticationEntryPoint) {
            super(authenticationEntryPoint);
        }
    
        @Override
        protected void sendStartAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, AuthenticationException reason)
                throws ServletException, IOException {
            boolean isAjax = "XMLHttpRequest".equals(request.getHeader("X-Requested-With"));
    
            if (isAjax) {
                response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
            } else {
                super.sendStartAuthentication(request, response, chain, reason);
            }
        }
    }
    

    Add AjaxExceptionTranslationFilter to your configuration right before default FILTER_SECURITY_INTERCEPTOR:

    <security:http pattern="/**">
        <security:custom-filter before="FILTER_SECURITY_INTERCEPTOR" ref="exceptionTranslationFilter"/>
        <!-- ... -->
    </security:http>
    
    <bean id="exceptionTranslationFilter" class="mypackage.AjaxExceptionTranslationFilter">
        <constructor-arg ref="loginUrlAuthenticationEntryPoint"/>
    </bean>
    

    You'll need to add an interceptor for HTTP header X-Requested-With to your ajax calls in your angular app in order to trigger AjaxExceptionTranslationFilter in the backend.

    Further you should catch an HTTP 401 response from the backend and handle it accordingly.

    $httpProvider.interceptors.push(['$q', function($q) {
        return {
            'responseError': function(rejection) {
                if(rejection.status === 401) {
                    // .. do something meaningful
                }
    
    
                return $q.reject(rejection);
            }
        };
    }]);
    
    $httpProvider.interceptors.push(['$q', function ($q) {
        return {
            'request': function (config) {
                config.headers["X-Requested-With"] = "XMLHttpRequest";
                return config || $q.when(config);
            }
        };
    }]);
    

    }]);