Reputation: 3296
I'm quite new to Angular. I'm using token authentication in my Angular app. I use HTTP interceptor to check when my backend returns 401, and than do a login using a refresh token:
myapp.config(...)
...
$httpProvider.interceptors.push(['$q', '$injector', function($q, $injector) {
var sessionRecoverer = {
responseError: function(response) {
// Session has expired
if (response.status === 401) {
var $http = $injector.get('$http');
var deferred = $q.defer();
var $auth = $injector.get('$auth');
$auth.login({
refresh_token: ????,
grant_type: 'refresh_token',
event_client: 'client',
client_id: 'id'
});
// When the session recovered, make the same backend call again and chain the request
return deferred.promise.then(function() {
return $http(response.config);
});
}
return $q.reject(response);
}
};
return sessionRecoverer;
}]);
Now, the refresh_token comes from my login controller (which pulls it from the api backend). So the controller has to pass it somehow to the interceptor. The problem is that the interceptor is in the config block so there are no services, values, etc. - only providers. But the providers are not injectable into the controller. So is there a way to pass data from controller to app.config? If not, is there a workaround? Can the injector be anywhere else other than app.config?
Upvotes: 2
Views: 2277
Reputation: 15222
Yes you can do it simply like this :
myapp.config(...)
...
$httpProvider.interceptors.push(['$q', '$injector', function($q, $injector, TokenFactory) {
var sessionRecoverer = {
responseError: function(response) {
// Session has expired
if (response.status === 401) {
var $http = $injector.get('$http');
var deferred = $q.defer();
var $auth = $injector.get('$auth');
$auth.login({
refresh_token: TokenFactory.getRefreshToken(),
grant_type: 'refresh_token',
event_client: 'client',
client_id: 'id'
});
// When the session recovered, make the same backend call again and chain the request
return deferred.promise.then(function() {
return $http(response.config);
});
}
return $q.reject(response);
}
};
return sessionRecoverer;
}]);
As you say, the block config can only inject providers, but the interceptor, itself, is a factory, thus you can inject other factories, for example, a factory called TokenFactory that should provide a method that return the refresh token when needed.
If the refresh_token is a thing that comes from the backend and you want to set a value within TokenFactory from your login controller, you can do something like this to implement your factory and your controller :
myapp.factory('TokenFactory',function(){
var currentRefreshToken;
return {
setRefreshToken: function(token){
currentRefreshToken = token;
},
getRefreshToken: function(){
return currentRefreshToken:
}
};
});
myapp.controller('MyLoginCtrl',function($scope,TokenFactory,$http){
$scope.login = function(){
$http.post('http://myapp.com/refreshtoken',$scope.credentials)
.then(TokenFactory.setRefreshToken)
.then(function(){ /* ... */})
.catch(function(err){ console.error(err) })
;
};
});
If you want to make your tokens to persist, you can write a factory that use the LocalStorage HTML5 API
and use it in your TokenFactory
:
myapp.factory('TokenFactory',function(LocalStorage){
// load the value from localstorage (hard disk) on app starts
var currentRefreshToken = LocalStorage.get('myapp.currentRefreshToken');
return {
setRefreshToken: function(token){
currentRefreshToken = token; // save value in RAM
LocalStorage.set('myapp.currentRefreshToken',token); // and sync the localstorage value
},
getRefreshToken: function(){
return currentRefreshToken; // quick access to the value from RAM
}
};
});
myapp.factory('LocalStorage',function($window) {
var localStorage = {};
localStorage.set = function(key, value) {
$window.localStorage[key] = value;
};
localStorage.get = function(key, defaultValue) {
return $window.localStorage[key] || defaultValue;
};
localStorage.setObject = function(key, value) {
$window.localStorage[key] = JSON.stringify(value);
};
localStorage.getObject = function(key) {
return (!$window.localStorage[key] || $window.localStorage[key] === undefined) ? {} : JSON.parse($window.localStorage[key]);
};
localStorage.setArray = function(key, array){
if (!array.length) {
console.debug(array);
$window.localStorage[key] = '[]';
} else{
this.setObject(key, array);
}
};
localStorage.getArray = function(key){
return (!$window.localStorage[key] || $window.localStorage[key] === undefined) ? [] : JSON.parse($window.localStorage[key]);
};
localStorage.exportAsFile = function(key, fileName){
var data = [$window.localStorage[key]] || ['{}'];
var blob = new Blob(data,{type:'application/json;charset=utf-8'});
$window.saveAs(blob,fileName);
};
return localStorage;
});
Upvotes: 1