Collin Allen Collin Allen - 4 months ago 105
Ajax Question

Preventing HTTP Basic Auth Dialog using AngularJS Interceptors

I'm building an AngularJS (1.2.16) web app with a RESTful API, and I'd like to send 401 Unauthorized responses for requests where authentication information is invalid or not present. When I do so, even with an HTTP interceptor present, I see the browser-presented basic "Authentication Required" dialog when an AJAX request is made via AngularJS. My interceptor runs after that dialog, which is too late to do something useful.

A concrete example:

My backend API returns 401 for

/api/things
unless an authorization token is present. Nice and simple.

On the AngularJS app side, I've looked at the docs and set up an interceptor like this in the
config
block:

$httpProvider.interceptors.push(['$q', function ($q) {
return {
'responseError': function (rejection) {
if (rejection.status === 401) {
console.log('Got a 401')
}
return $q.reject(rejection)
}
}
}])


When I load my app, remove the authentication token, and perform an AJAX call to
/api/things
(to hopefully trigger the above interceptor), I see this:

Authentication Required

If I cancel that dialog, I see the
console.log
output of "Got a 401" that I was hoping to see instead of that dialog:

Got a 401

Clearly, the interceptor is working, but it's intercepting too late!

I see numerous posts on the web regarding authentication with AngularJS in situations just like this, and they all seem to use HTTP interceptors, but none of them mention the basic auth dialog popping up. Some erroneous thoughts I had for its appearance included:


  • Missing
    Content-Type: application/json
    header on the response? Nope, it's there.

  • Need to return something other than promise rejection? That code always runs after the dialog, no matter what gets returned.



Am I missing some setup step or using the interceptor incorrectly?

Answer

Figured it out!

The trick was to send a WWW-Authenticate response header of some value other than Basic. You can then capture the 401 with a basic $http interceptor, or something even more clever like angular-http-auth.