FED-icons-01 FED-icons-02 FED-icons-03 FED-icons-04 FED-icons-05 FED-icons-06 FED-icons-07 FED-icons-08 FED-icons-09 FED-icons-10 FED-icons-11

The first time I discovered Angular's http interceptors it felt like a holiday. If you're unfamiliar with them, they are basically just factories that function exactly like they are named. Every request (or response) that passes through Angular's $http service first gets intercepted, allowing you to muck about with it before sending it on it's way. Here's a basic shell of one:

  .factory('myHttpResponseInterceptor', [, function() {
      return {
        request: function(request) {
            return request
        },
        response: function(response) {
          return response
        },
        responseError: function(rejection) {
            return rejection;
          }
        }
      }
  ])

Once it's built, you need to register it with $httpProvider. Like so:

  .config(['$httpProvider', function($httpProvider) {
      $httpProvider.interceptors.push('myHttpResponseInterceptor');
    }
  ])

One pretty handy use for these would be to manage your users' logged in state. Stuff an auth token into the header with every request and, redirect them to the login screen if their session expires. Let's take a look at a quick and easy way to do that.

First we'll need a service to tell us if we're currently authenticated as well as give us our auth token when we ask for it.

Let's make a few assumptions for how this should flow.

  • Our login screen will call this service to save the token once a successful login has occurred
  • Our tokens will contain a couple of properties like 'issued' and 'expires_in' that we'll use those to guesstimate if our token has expired
  • We'll just use Angular's $sessionStorage to hang on to the tokens.
angular.module('views').factory('TokenService', ['$q', '$sessionStorage', function ($q, $sessionStorage) {
      var _authenticated = false;

      var _isAuthenticated = function () {
        var deferAuth = $q.defer();
        var currentToken = _getToken();
        var now = new Date()
        var issued = new Date(currentToken.issued).getTime();
        var expires_in = currentToken.expires_in;
        var expires = new Date(issued + expires_in);
        
        if (expires < now){
          deferAuth.resolve(true);
        } else {
          deferAuth.resolve(false);
        }
        
        return deferAuth.promise;
      };

      var _getToken = function () {
        return $sessionStorage.myToken;
      };

      var _setToken = function (objToken) {
        var deferSetToken = $q.defer();

        if (objToken) {
          $sessionStorage.myToken = objToken;
          _authenticated = true;
          deferSetToken.resolve(objToken);
        } else {
            delete $sessionStorage.myToken;
            _authenticated = false;
            deferSetToken.resolve(objToken);
          } catch (err) {
            console.log(err);
          }
        }
        return deferSetToken.promise;
      };
      
      return {
        getToken : _getAuthToken,
        isAuthenticated : _isAuthenticated,
        setToken : _setToken
      }
    } ]);

Now we can use this in our interceptor.

  .factory('myHttpResponseInterceptor', '$q', '$location', 'TokenService' [, function($q, $location, TokenService) {
      return {
        request: function(request) {
            var currentToken = TokenService.getToken();
              if(currentToken.access_token){
                request.headers.authorization = 'Bearer ' + currentToken.access_token;
              }
              return request;
        },
        response: function(response) {
          return response
        },
        responseError: function(rejection) {
            if (rejection.status === 401) {
              return TokenService.isAuthenticated().then(function(authenticated) {
                if (authenticated) {
                  return $q.reject(rejection);
                } else {
                  var oldPath = $location.path();
                  $location.path('/login').search('returnTo', oldPath);
                  return $q.reject(rejection);
                }
              });
            } else {
              return $q.reject(rejection);
            }
          }
        }
      }
  ])

Notice, in the 'request' we go get our token and sneak it into the authorization header. If we get a response error, we check to see if it was a 401. If so, confirm the token expiration and either pass the error back to $http or redirect our user to the login screen.

This is just a simple example of what can be done with an interceptor. I encourage you to use your imagination and think up clever ways to use this powerful little tool in your own Angular toolbox.


Gary Acord

Lead UI Developer