Skip to content

Instantly share code, notes, and snippets.

@ericeslinger
Last active August 29, 2015 14:15
Show Gist options
  • Save ericeslinger/ed729beefd80d067115d to your computer and use it in GitHub Desktop.
Save ericeslinger/ed729beefd80d067115d to your computer and use it in GitHub Desktop.
angular token authentication
// based heavily on https://github.com/witoldsz/angular-http-auth/blob/master/src/http-auth-interceptor.js
var buffer401 = [];
angular.module('app.model').config(function($httpProvider) {
$httpProvider.interceptors.push(function($rootScope, $q) {
return {
responseError: function(rejection) {
if (rejection.status === 401) {
return $q(function(resolve, reject) {
buffer401.push({
config: rejection.config,
deferred: {
resolve: resolve,
reject: reject
}
});
return $rootScope.$emit('event:login-required');
//somewhere else, I am listening for the event to kick off the actual login flow.
});
} else {
return $q.reject(rejection);
}
}
};
});
}).service('Authenticate', function($rootScope, $q, $state, $window, Server, $http, $localForage, Globals) {
var authpopup = null;
$window.addEventListener('message', function(e) {
//the login flow opens a popup that doesn't live within the angular space
//since it is rendered by the server directly, so I use
//window.postMessage to hand the actual tokens back and forth
var initial;
if (e.data.token !== null) {
if (authpopup !== null) {
authpopup.postMessage('okay_done', '*');
}
$http.defaults.headers.common.Authorization = 'Bearer ' + e.data.token;
$localForage.setItem('apiToken', e.data.token);
Globals.apiToken = e.data.token;
$rootScope.$emit('event:login-successful', Globals.apiToken);
initial = $q.defer();
initial.resolve();
//this re-runs all the previously-cached 401s, so my
//promises eventually resolve.
return buffer401.reduce(function(thenable, req) {
return thenable.then(function() {
//I was having issues with the config getting the appropriate
//token even though I set them in common.
req.config.headers.Authorization = 'Bearer ' + e.data.token;
return $http(req.config).then(function(data) {
return req.deferred.resolve(data);
}).catch(function(err) {
return req.deferred.reject(err);
});
});
}, initial.promise).catch(function(err) {
console.log('error resolving 401 after successful login');
console.log(err);
});
}
}, false);
$rootScope.$on('event:login-required', function() {
//all I do here is set the apiToken to null
//because elsewhere I have a conditionally rendered modal DIV
//with a link to initiate the login flow via a popup in the
//angular HTML stuff.
if (Globals.apiToken !== null) {
Globals.apiToken = null;
}
});
return {
attemptGoogleLogin: function() {
authpopup = $window.open(Server.api + '/auth/google_oauth2/callback');
},
validateToken: function() {
return $localForage.getItem('apiToken').then(function(apiToken){
if (apiToken !== null) {
$http.defaults.headers.common.Authorization = 'Bearer ' + apiToken;
Globals.apiToken = apiToken;
}
});
},
devalidateToken: function() {
return delete Globals.apiToken;
},
logout: function() {
$localForage.clear().then(function(){
$window.location.reload();
});
}
};
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment