angular multiple interceptors in app.config - angularjs

in my angular app, I want to add informations to all calls to an external API.
To do so, I use interceptors that I call from within app.config.
app.config(['$httpProvider', ... , function($httpProvider, ...){
$httpProvider.interceptors.push('globalInterceptorService');
$httpProvider.interceptors.push('authInterceptorService');
}]);
It works fine when I only use one interceptor. But when I use 2 of them (as in the example above) the action of one gets overridden by the other.
Any idea about how to deal with multiple interceptors? Maybe is it recommended to have only 1? Any help is much appreciate.
Interceptor 1 :
function globalInterceptorService ($q, localStorageService) {
var service = {};
var _request = function (config) {
config.headers = config.headers || {};
var AreaId = localStorageService.get('AreaId');
if (AreaId) {
config.headers.AreaId = 'AreaId ' + AreaId;
}
return config;
};
service.request = _request;
return service;
}
Interceptor 2 :
function authInterceptorService ($q, $location, localStorageService) {
var service = {};
var _request = function (config) {
config.headers = config.headers || {};
var authData = localStorageService.get('authorizationData');
if (authData) {
config.headers.Authorization = 'Bearer ' + authData.token;
}
return config;
};
service.request = _request;
return service;
}

I think you should push the function, not its string name.
function globalInterceptorService($q, localStorageService){...}
$httpProvider.interceptors.push(globalInterceptorService);
example: http://jsfiddle.net/aartek/tbhobfbu
Or
function globalInterceptorService($q, localStorageService){...}
$provide.factory('globalInterceptorService',globalInterceptorService)
$httpProvider.interceptors.push('globalInterceptorService');
example: http://jsfiddle.net/aartek/tbhobfbu/2/
It's well described in the documentation

Related

How to override header Token with AngularJS

I have problem to override header token with AngularJS becauase already set with AuthInterceptor.
app.factory('authInterceptor', function ($rootScope, $q, $window) {
return {
request: function (config) {
config.headers = config.headers || {};
if (localStorage.getItem("token")!=='') {
config.headers.Authorization = 'Bearer ' + localStorage.getItem("token");
}
return config;
},
};
});
app.config(function ($httpProvider) {
$httpProvider.interceptors.push('authInterceptor');
});
I would access another third party URL where using another Token,but always failed, because Token back to current Token in localStorage. I have used TransformRequest, but not work too, what is best practice to solve the problem?
You could check that the URL in the intercepted request is a URL that you want to pass an authorization token. For instance, if the URL to your own API always starts with https://api.myapp.com, you could do it like this:
app.factory('authInterceptor', function ($rootScope, $q, $window) {
var urlForAuthorization = 'https://api.myapp.com';
return {
request: function (config) {
config.headers = config.headers || {};
if (config.url.startsWith(urlForAuthorization)
&& localStorage.getItem("token") !== '') {
config.headers.Authorization = 'Bearer ' + localStorage.getItem("token");
}
return config;
}
};
});

Token Based Authentication AngularJS

I am new to AngularJS. What I want is getting a token coming from a server with $http post and then use that token coming from the request to use as an authorization header for access in other page and following data requests to the server. Here is my existing code:
var peopleApp = angular.module('peopleApp', ['ngRoute', 'ngAnimate']);
peopleApp.config(function($interpolateProvider, $httpProvider) {
// Change template tags
$interpolateProvider.startSymbol('[[');
$interpolateProvider.endSymbol(']]');
// Enabling CORS
$httpProvider.defaults.xsrfCookieName = 'csrftoken';
$httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
// $httpProvider.defaults.withCredentials = true;
});
peopleApp.controller('formController', function($scope, $http, $location) {
$scope.logIn = function(json, url){
$http.post(url, json)
.then(function(response){
token = response.data.token;
window.location.href = 'crew-menu';
},function(response){
alert('Please check the following validations on the next alert and contact the creators regarding this error');
alert(JSON.stringify(response.data.errors));
});
}
});
P.S.
I am aware that this can be done by using the .run like this:
peopleApp.run(function($http) {
$http.defaults.headers.common.Authorization = 'YmVlcDpib29w';
});
However the token Authorization will be coming from a login authentication via post request
Step 1
Take the token from login response and save it somewhere in the app, most common solution is to store it in local storage so it will be available after browser restart.
$scope.logIn = function(json, url){
$http.post(url, json)
.then(function(response){
localStorageService.set('authorizationData', { token: response.data.token });
window.location.href = 'crew-menu';
},function(response){
alert('Please check the following validations on the next alert and contact the creators regarding this error');
alert(JSON.stringify(response.data.errors));
});
}
Step 2
Use angularjs $http interceptor to automatically add authentication header to every http request:
app.factory('authInterceptorService', ['$q', '$location', 'localStorageService', function ($q, $location, localStorageService) {
var authInterceptorServiceFactory = {};
var _request = function (config) {
config.headers = config.headers || {};
var authData = localStorageService.get('authorizationData');
if (authData) {
config.headers.Authorization = 'Bearer ' + authData.token;
}
return config;
}
var _responseError = function (rejection) {
if (rejection.status === 401) {
$location.path('/login');
}
return $q.reject(rejection);
}
authInterceptorServiceFactory.request = _request;
authInterceptorServiceFactory.responseError = _responseError;
return authInterceptorServiceFactory;
}]);
Or put it manualy every time you make http request:
function buildConfig() {
var c = {};
var authData = localStorageService.get('authorizationData');
if (authData) {
c.headers.Authorization = 'Bearer ' + authData.token;
}
return c;
}
function post(url, model) {
return $http.post(url, model, buildConfig());
}
More info: here and
my angular webapi project
Already solved it. The solution is to store the token in a localstorage first then use run function for it be a default. Here is the code:
var peopleApp = angular.module('peopleApp', ['ngRoute', 'ngAnimate']);
peopleApp.config(function($interpolateProvider, $httpProvider) {
// Change template tags
$interpolateProvider.startSymbol('[[');
$interpolateProvider.endSymbol(']]');
// Enabling CORS
$httpProvider.defaults.xsrfCookieName = 'csrftoken';
$httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
// $httpProvider.defaults.withCredentials = true;
});
peopleApp.controller('formController', function($scope, $http, $location, $window) {
$scope.logIn = function(json, url){
$http.post(url, json)
.then(function(response){
token = response.data.token;
$window.localStorage.token = token;
window.location.href = 'crew-menu';
},function(response){
alert('Please check the following validations on the next alert and contact the creators regarding this error');
alert(JSON.stringify(response.data.errors));
});
}
});
peopleApp.run(function($window, $http){
if ($window.localStorage.token){
$http.defaults.headers.common.Authorization = "Token "+$window.localStorage.token;
}
});

Reset Angular Service

Is there a good way to reset the data in a factory/service in angular without creating a dependency to it?
I currently have an AuthService that takes a username and password, and gets an oauth token from a server. I also have an http interceptor that adds the token to all requests.
If I get a 401 (unauthorized) response then my token is no longer valid and I want to set _AuthData inside AuthService to null. But I have no good way of doing that right now.
If I add an AuthService dependancy into the interceptor (to be able to call LogOut()) then I get a circular reference since AuthService uses $http.
I keep re-reading the token from the localstorageservice inside AuthService for methods like IsAuthenticated() and Username(), but I'd like to avoid that if possible to avoid the performance hit.
Is there a way to "reset" AuthService from the AuthInterceptorService without creating a dependancy?
AuthService
appRoot.factory("AuthService", ["$http", "$q", "localStorageService", function ($http, $q, localStorageService) {
var _AuthData;
var AuthServiceFactory = {};
AuthServiceFactory.Username = function () {
return _AuthData.Username;
};
AuthServiceFactory.Roles = function () {
return _AuthData.Roles;
};
AuthServiceFactory.IsAuthenticated = function () {
return _AuthData != null;
};
AuthServiceFactory.LogOut = function () {
_AuthData = null;
localStorageService.remove("AuthData");
};
AuthServiceFactory.Login = function (Username, Password) {
var Deferred = $q.defer();
$http.post(ApiBaseUrl + "token", Username, { headers: { 'Content-Type': "application/x-www-form-urlencoded" } }).success(function (Response) {
_AuthData = {
Token: Response.access_token,
Username: Username,
Roles: Response.Roles
};
localStorageService.set("AuthData", _AuthData);
Deferred.resolve(Response);
}).error(function (err, status) {
Deferred.reject(err);
});
return Deferred.promise;
};
return AuthServiceFactory;
}]);
AuthInterceptorService
appRoot.factory("AuthInterceptorService", ["$q", "$location", "localStorageService", function ($q, $location, localStorageService) {
var AuthInterceptorServiceFactory = {};
AuthInterceptorServiceFactory.request = function (config) {
config.headers = config.headers || {};
var AuthData = localStorageService.get("AuthData");
if (AuthData) {
config.headers.Authorization = "Bearer " + AuthData.Token;
}
return config;
};
AuthInterceptorServiceFactory.responseError = function (Rejection) {
if (Rejection.status === 401) {
localStorageService.remove("AuthData");
//AuthService.LogOut(); //Need to reset token here
$location.url("/Login");
}
return $q.reject(Rejection);
};
return AuthInterceptorServiceFactory;
}]);
I can think of a few options, varyingly reasonable.
Just the first thing to consider - is the performance hit from local storage really an issue for you? Your current solution is straightforward and easy to understand, and that's a feature in itself.
Split AuthService into an Authorizer and AuthStorage. That way Authorizer can depend on $http, AuthStorage doesn't need to, and AuthInterceptorService can then depend on AuthStorage, where you can put the reset function.
This one feels like a big hammer, but AuthInterceptorService can broadcast an auth_failed event on appRoot, which AuthService can listen for to perform the reset. That's heading towards pretty global message passing, so I'd be concerned about its maintainability.

Should I use Service or Factory for my $http interceptor

I need to implement an interceptor for the $http service. I have tried 2 implementations using a factory and a service, they both work OK. this is the implementation in type script:
Service:
export class AuthInterceptorService {
private _authService:Services.IAuthService;
static $inject = ['$q', '$location', '$injector'];
constructor(private $q: ng.IQService, private $location: ng.ILocationService, private $injector: ng.auto.IInjectorService) {
}
private getAuthService = (): Services.IAuthService=> {
if (this._authService == null) {
this._authService = this.$injector.get('authService');
}
return this._authService;
}
public request = (config: ng.IRequestConfig) => {
config.headers = config.headers || {};
var authData = this.getAuthService().authData;
if (authData) {
config.headers.Authorization = 'Bearer ' + authData.token;
}
return config;
}
public responseError = (rejection)=> {
if (rejection.status === 401) {
this.getAuthService().logOut();
}
return this.$q.reject(rejection);
}
}
In app init:
.service('authInterceptorService', Services.AuthInterceptorService)
Factory:
export function AuthInterceptorFactory($q: ng.IQService, $injector: ng.auto.IInjectorService) {
return {
request: (config: ng.IRequestConfig)=> {
config.headers = config.headers || {};
var authData = $injector.get('authService').authData;
if (authData) {
config.headers.Authorization = 'Bearer ' + authData.token;
}
return config;
},
responseError: (rejection)=> {
if (rejection.status === 401) {
$injector.get('authService').logOut();
}
return $q.reject(rejection);
}
};
}
In app init:
.factory('authInterceptorFactory', ['$q', '$injector', Services.AuthInterceptorFactory])
and then on configuration of the interceptor:
.config(['$httpProvider', ($httpProvider:ng.IHttpProvider) => {
$httpProvider.interceptors.push('authInterceptorxxxxx');
}])
As you can see I am using the service location pattern to inject a dependency (using $injector), this is to avoid a circular dependency since the injected service has a dependency on $http.
As I said they both work, I prefer the Service version as it allows me to cache the injection of the dependent service 'authService', in the factory flavour it gets resolved and injected all the time on every request. Is there any problems in using the Service implementation? in Angular docs they refer you should use a factory.
There is nothing wrong with using a service for an HttpInterceptor. Angular will simply call injector.get to resolve it, so using a service is fine.
From source $injector.get(interceptorFactory) https://github.com/angular/angular.js/blob/6f19a6fd33ab72d3908e3418fba47ee8e1598fa6/src/ng/http.js#L207-L210:
forEach(interceptorFactories, function(interceptorFactory) {
reversedInterceptors.unshift(isString(interceptorFactory)
? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory));
});

How to share a token between factories?

I have a LoginFactory which returns an auth object with a token as a part of it. I need to set this token in the header of any REST API call I make, post the user logging in.
What is the best way to share this token across factoires so that the value can be used in said factories?
I tried using angular.module.value but this does not seem to work. (The value never gets set.)
in your factory make two method
_gettoken(){
return token;
}
_settoken(token_temp){
token=token_temp;
}
now use this factory method to use it any where
I hope it will work
app.factory('authInterceptorService', ['$q', '$location', 'localStorageService', function ($q, $location, localStorageService) {
var authInterceptorServiceFactory = {};
var _request = function (config) {
config.headers = config.headers || {};
var authData = localStorageService.get('authorizationData');
if (authData) {
**config.headers.Authorization = 'Bearer ' + authData.token;**
}
return config;
}
var _responseError = function (rejection) {
if (rejection.status === 401) {
$location.path('/login');
}
return $q.reject(rejection);
}
authInterceptorServiceFactory.request = _request;
authInterceptorServiceFactory.responseError = _responseError;
return authInterceptorServiceFactory;
}]);
For more details,
http://bitoftech.net/2014/06/09/angularjs-token-authentication-using-asp-net-web-api-2-owin-asp-net-identity/

Resources