Last active
August 29, 2015 14:15
-
-
Save ericeslinger/ed729beefd80d067115d to your computer and use it in GitHub Desktop.
angular token authentication
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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