Sending an access token to server using AngularJS - angularjs

I'm trying to use Django REST Framework's token-based authentication scheme with an AngularJS client. I'm able to successfully retrieve and store a token from the server, but I'm having trouble figuring out how to attach the token to subsequent responses. Here's the service that manages logging in and saving a token:
angular.module('mymodule')
.factory('loginService', function ($http, $window) {
var api_base = "link to my api";
return {
async: function() {
return $http.get(api_base + "authentication/login/").then(function (response) {
return response.data;
}, function errorCallback(response) {
console.log("Testuser API Error: " + response);
return null;
});
},
loginUser: function(email, password) {
self.saveToken = function(auth_token) {
$window.localStorage['jwtToken'] = auth_token;
};
self.getToken = function() {
return $window.localStorage['jwtToken'];
};
console.log("...to listing " + email);
return $http.post("link to my api/authentication/login/", {
email: email,
password: password
}).then(function(response) {
if(response.config.url.indexOf("link to my api") === 0 && response.data.auth_token) {
self.saveToken(response.data.auth_token);
}
return response;
});
}
};
});
Here's the controller associated with the above service to handle logging in:
angular.module('mymodule').controller("LoginController", function(loginService, $scope) {
$scope.loginusers = [];
$scope.refresh = function() {
loginService.async().then(function(data) {
if (data == null)
return;
console.log(data[0]["email"]);
$scope.loginusers = [];
for (var loginuser in data)
$scope.loginusers.push(loginuser);
});
};
$scope.refresh();
// Test //
$scope.loginTestUser = function() {
console.log("something...");
loginService.loginUser(
$scope.email,
$scope.password
)
};
//////
});
And here's the service that I'd like to use for displaying a user's profile after the token is sent back to the server.
angular.module('mymodule').factory("profileService", function($http, loginService, $httpProvider) {
var api_base = "link to my api";
$httpProvider.interceptors.push(function($q, $window) {
return {
'request': function(config) {
config.headers['Token'] = $window.localStorage['jwtToken'];
return config;
}
};
});
return {
async: function() {
return $http.get(api_base + "authentication/me/").then(function (response) {
return response.data[0];
}, function errorCallback(response) {
console.log("Profile API Error: " + response);
return null;
});
}
};
});
How should I be approaching this?

Related

AngularJS & Express: $rootScope.$on not refreshing properly

Once users login, a json web token is created and then saved in local storage. However, my express middleware is returning message: 'No token provided'. Once i hit refresh in the browser, then it detects the token properly and the user is shown as logged in. I imagine this is a simple fix but I have not been able to figure it out. Any ideas please?
Angular Controller:
angular.module('mainController', ['authService'])
.controller('mainCtrl', function($http, $timeout, $location, Auth, $rootScope, $route) {
var app = this;
$rootScope.$on('$routeChangeStart', function() {
app.loggedIn = Auth.isLoggedIn();
Auth.getUser().then(function(data) {
app.user = data.data;
console.log(app.user);
});
});
app.doLogin = function(userData) {
app.user = false;
app.loading = true;
app.errorMsg = false;
Auth.doLogin(app.userData).then(function(data) {
if (data.data.success) {
app.loading = false;
app.successMsg = data.data.message + '...Redirecting';
$timeout(function() {
$location.path('/');
}, 2000);
} else {
app.loading = false;
app.errorMsg = data.data.message;
}
});
};
Angular Authentication Service:
angular.module('authService', [])
.factory('Auth', function($http, AuthToken, $q) {
var authFactory = {};
authFactory.doLogin = function(userData) {
return $http.post('/api/authenticate', userData).then(function(data) {
AuthToken.setToken(data.data.token);
return data;
});
};
authFactory.doLogout = function() {
AuthToken.setToken();
}
authFactory.isLoggedIn = function() {
if (AuthToken.getToken()) {
return true;
} else {
return false;
}
};
authFactory.getUser = function() {
if (AuthToken.getToken()) {
return $http.get('/api/me');
} else {
return $q.reject({ message: 'User has no token' });
}
}
return authFactory;
})
.factory('AuthToken', function($window) {
var authTokenFactory = {};
authTokenFactory.setToken = function(token) {
if (token) {
$window.localStorage.setItem('token', token);
} else {
$window.localStorage.removeItem('token');
}
};
authTokenFactory.getToken = function() {
return $window.localStorage.getItem('token');
};
return authTokenFactory;
})
.factory('AuthInterceptor', function($location, $q, AuthToken) {
var AuthInterceptorFactory = {};
var token = AuthToken.getToken();
AuthInterceptorFactory.request = function(config) {
if (token) config.headers['x-access-token'] = token;
return config;
};
AuthInterceptorFactory.responseError = function(response) {
if (response.status === 403) {
AuthToken.setToken();
$location.path('/login');
}
return $q.reject(response);
};
return AuthInterceptorFactory;
});
Angular Config File to Attach Tokens to all requests:
angular.module('userApp', ['appRoutes', 'userControllers', 'mainController', 'authService'])
.config(function($httpProvider) {
$httpProvider.interceptors.push('AuthInterceptor');
});
Express:
// middleware to check for tokens
router.use(function(req, res, next) {
var token = req.body.token || req.body.query || req.headers['x-access-token'];
if (token) {
jwt.verify(token, secret, function(err, decoded) {
if (err) {
res.json({ success: false, message: 'failed to authenticate token' });
} else {
req.decoded = decoded;
next();
}
});
} else {
res.json({ success: false, message: 'No token provided'});
}
});
// Route to get the current user
router.get('/me', function(req, res) {
res.send(req.decoded);
});

Making promises in modules

I try to make facebook registration module in my app. Facebook API is faster than my Angular controller, so promise should be used here. The problem is that $q seems to be an empty object and defer function is undefined.
module:
var module = angular.module('app.facebook', []);
module.constant("fbAppId", 'herecomesmycode');
module.factory('facebook', FacebookAPI);
FacebookAPI.$inject = ['$ionicLoading', '$q', '$ionicPlatform', '$state', 'authService', 'datacontext', '$location'];
function FacebookAPI(UserService, $q, $ionicLoading, fbAppId, $state, authService, datacontext, $location) {
return {
fbLoginSuccess: fbLoginSuccess,
fbLoginError: fbLoginError,
getFacebookProfileInfo: getFacebookProfileInfo,
fbLogin: fbLogin,
fbRegister: fbRegister
};
and here $q.defer is undefined:
function fbRegister() {
console.log($q.defer);
if (!cordova) {
facebookConnectPlugin.browserInit(fbAppId);
}
var data;
facebookConnectPlugin.getLoginStatus(function (response) {
if (response.status !== 'connected') {
facebookConnectPlugin.login(["email"],
function(response) {
data = getApiData();
},
function(response) {
});
} else {
data = getApiData();
}
});
}
Without using promise, it gets fast from API but all variables I want to fill with values from API, are initiated before API finishes and are undefined.
The whole module:
(function() {
'use strict';
var module = angular.module('app.facebook', []);
module.constant("fbAppId", 'myappkey');
module.factory('facebook', FacebookAPI);
FacebookAPI.$inject = ['$ionicLoading', '$ionicPlatform', '$state', 'authService', '$q'];
function FacebookAPI(UserService, $ionicLoading, fbAppId, $state, authService, $q) {
return {
fbLoginSuccess: fbLoginSuccess,
fbLoginError: fbLoginError,
getFacebookProfileInfo: getFacebookProfileInfo,
fbLogin: fbLogin,
fbRegister: fbRegister
}
function fbRegister() {
console.log($q);
if (!cordova) {
facebookConnectPlugin.browserInit(fbAppId);
}
var data;
facebookConnectPlugin.getLoginStatus(function (response) {
if (response.status !== 'connected') {
facebookConnectPlugin.login(["email"],
function(response) {
data = getApiData();
},
function(response) {
});
} else {
data = getApiData();
}
});
}
function getApiData() {
var formData = {};
facebookConnectPlugin.api("me/?fields=id,first_name,last_name,link,gender,email,birthday", ["public_profile", "email", "user_birthday"],
function (result) {
if (result.gender == "male") {
result.gender = '1';
} else {
result.gender = '2';
}
formData = {
name: result.first_name + " " + result.last_name,
email: result.email,
birthday: new Date(result.birthday),
gender: result.gender
}
console.log("moduĊ‚" + formData);//here we have nice and neat data
return formData;
}, function(res) {
});
}
};
//This is the success callback from the login method
function fbLoginSuccess(response) {
var fbLogged = $q.defer();
if (!response.authResponse) {
fbLoginError("Cannot find the authResponse");
return;
}
var expDate = new Date(
new Date().getTime() + response.authResponse.expiresIn * 1000
).toISOString();
var authData = {
id: String(response.authResponse.userID),
access_token: response.authResponse.accessToken,
expiration_date: expDate
}
authService.facebookLogin(response.authResponse.accessToken).then(function() {
fbLogged.resolve(authData);
});
};
//This is the fail callback from the login method
function fbLoginError(error) {
var fbLogged = $q.defer();
fbLogged.reject(error);
alert(error);
$ionicLoading.hide();
};
//this method is to get the user profile info from the facebook api
function getFacebookProfileInfo() {
var info = $q.defer();
facebookConnectPlugin.api('/me', "",
function(response) {
info.resolve(response);
},
function(response) {
info.reject(response);
}
);
return info.promise;
}
//This method is executed when the user press the "Login with facebook" button
function fbLogin() {
if (!cordova) {
//this is for browser only
facebookConnectPlugin.browserInit(fbAppId);
}
//check if we have user's data stored
var user = UserService.getUser();
facebookConnectPlugin.getLoginStatus(function(success) {
//alert(JSON.stringify(success, null, 3));
if (success.status === 'connected') {
// the user is logged in and has authenticated your app, and response.authResponse supplies
// the user's ID, a valid access token, a signed request, and the time the access token
// and signed request each expire
facebookConnectPlugin.api("me/?fields=id,first_name,last_name,link,gender,email", ["public_profile", "email"],
function(result) {
//alert("Result: " + JSON.stringify(result));
//alert(result.first_name);
})
var accessToken = success.authResponse.accessToken;
authService.facebookLogin(accessToken).then(function() {
$state.go('app.map');
}, function(err) { alert('auth failed: ' + JSON.stringify(err, null, 2)); });
} else {
//if (success.status === 'not_authorized') the user is logged in to Facebook, but has not authenticated your app
//else The person is not logged into Facebook, so we're not sure if they are logged into this app or not.
$ionicLoading.show({
template: 'Loging in...'
});
// permissions from facebook
facebookConnectPlugin.login([
'email',
'public_profile',
'user_about_me',
'user_likes',
'user_location',
'read_stream',
'user_photos'
], fbLoginSuccess, fbLoginError);
fbLogged.promise.then(function(authData) {
var fb_uid = authData.id,
fb_access_token = authData.access_token;
//get user info from FB
getFacebookProfileInfo().then(function(data) {
var user = data;
user.picture = "http://graph.facebook.com/" + fb_uid + "/picture?type=large";
user.access_token = fb_access_token;
//save the user data
//store it on local storage but it should be save it on a database
UserService.setUser(user);
$ionicLoading.hide();
$state.go('app.map');
});
});
}
});
}
})();

Configure angular $http with a value obtained from server

I'm building my (first) angular app that will have tokens inserted into headers (the content shown is for the most part taken from here)
angular.module('myApp')
.factory('sessionInjector', ['SessionService', function(SessionService) {
var sessionInjector = {
request: function(config) {
config.headers['x-session-token'] = SessionService.getToken();
return config;
}
};
return sessionInjector;
}])
.config(['$httpProvider', function($httpProvider) {
$httpProvider.interceptors.push('sessionInjector');
}])
The trouble I'm having is with SessionService - how can I initialize this with call to the server?
For example, this didn't work:
.factory('SessionService', ['$injector', function($injector){
var token = "";
return {
getToken: function () {
var http = $injector.get('$http');
if (token === "") {
http.get('http://localhost/api/auth/getToken').success(function (ret) {
token = ret;
});
}
return token;
//I can see a $q/deferred/promise should be used somehow here...
//but I'm not sure it solves the problem I'm having...
}
}
}]);
because it just overloads my cpu to 100%...
Since it's my first angular app, I'm sure I'm missing something, but... what?
EDIT:
Another take on the matter... still doesn't work though... (again, uses up cpu, probably infinite loop)
.factory('sessionData', function () {
var currentToken = '[uninitialized-token]';
return {
getToken: function () {
return currentToken;
},
setToken: function (token) {
currentToken = token;
}
}
})
.factory('sessionInjector', ['sessionData', '$injector', '$q', function (sessionData, $injector, $q) {
var sessionInjector = {
request: function (config) {
var deferred = $q.defer();
var http = $injector.get('$http');
http.get('http://localhost/api/auth/getToken').success(function (ret) {
sessionData.setToken(ret);
console.log("successfully authenticated with token " + sessionData.getToken());
config.headers['x-header-sessionID'] = sessionData.getToken();
deferred.resolve(config);
})
.error(function(){
console.log("failed to authenticate");
deferred.resolve(config);
});
return deferred.promise;
}
};
return sessionInjector;
}])
.config(['$httpProvider', function ($httpProvider) {
$httpProvider.interceptors.push('sessionInjector');
}])
.run(['$http', 'sessionData', function ($http, configs, sessionData) {
$http.get('http://localhost/api/auth/testMethod').then(function (ret) {
//do something...
});
}])
Check whether this modified code fragment will solve your issues.
.factory('SessionService', ['$http', '$q', function($http, $q) {
var token = null;
var sessionService = {};
var differred = $q.defer();
sessionService.readToken = function() {
return $http.get('http://localhost/api/auth/getToken')
.success(function (res) {
console.log('Auth Success and token received: ' + JSON.stringify(res.data));
// Extract the token details from the received JSON object
token = res.data;
differred.resolve(res);
}, function (res) {
console.log('Error occurred : ' + JSON.stringify(res));
differred.reject(res);
}
)
};
sessionService.getToken = function() {
return token;
};
sessionService.isAnonymous = function() {
if (token)
return true;
else
return false;
};
return sessionService;
}])
.factory('sessionInjector', ['SessionService', function(SessionService) {
var sessionInjector = {
request: function(config) {
if (!sessionService.isAnonymous) {
config.headers['x-session-token'] = SessionService.getToken();
return config;
}
}
};
return sessionInjector;
}])
.config(['$httpProvider', function($httpProvider) {
$httpProvider.interceptors.push('sessionInjector');
}])
Answer was actually pretty straight forward - if the targeted URL is for login, then don't inject anything (look for the comment the fix):
.factory('sessionData', function () {
var currentToken = '[uninitialized-token]';
return {
getToken: function () {
return currentToken;
},
setToken: function (token) {
currentToken = token;
}
}
})
.factory('sessionInjector', ['sessionData', '$injector', '$q', function (sessionData, $injector, $q) {
var sessionInjector = {
request: function (config) {
//The fix:
if(config.url === 'http://localhost/api/auth/getToken')
return config;
var deferred = $q.defer();
var http = $injector.get('$http');
http.get('http://localhost/api/auth/getToken').success(function (ret) {
sessionData.setToken(ret);
console.log("successfully authenticated with token " + sessionData.getToken());
config.headers['x-header-sessionID'] = sessionData.getToken();
deferred.resolve(config);
})
.error(function(){
console.log("failed to authenticate");
deferred.resolve(config);
});
return deferred.promise;
}
};
return sessionInjector;
}])
.config(['$httpProvider', function ($httpProvider) {
$httpProvider.interceptors.push('sessionInjector');
}])
.run(['$http', 'sessionData', function ($http, configs, sessionData) {
$http.get('http://localhost/api/auth/testMethod').then(function (ret) {
//do something...
});
}])

How to use multiple factories in Angular to access uid sent from server

I am creating and sending a UID on the server side to the client side when the user visits a web page. I would like to use that UID as the subfolder to store each project a particular user posts to the server. I'm trying to figure out how best to accomplish this. When I use the code below, I am unable to access the UID in the Projects factory from the UserFactory.
Javascript (Angular):
myApp.factory('UserFactory', function UserFactory($http, API_URL, AuthTokenFactory, $q) {
return $http.get(API_URL + '/api/authenticate').then(function success(response) {
AuthTokenFactory.setToken(response.data.token);
return response;
});
});
myApp.factory('AuthTokenFactory', function AuthTokenFactory($window) {
var store = $window.localStorage;
var key = 'auth-token';
return {
getToken: getToken,
setToken: setToken
};
function getToken() {
return store.getItem(key);
}
function setToken(token) {
if (token) {
store.setItem(key, token);
} else {
store.removeItem(key);
}
}
});
myApp.factory('Projects', function($http, API_URL, UserFactory, AuthTokenFactory) {
var uid = UserFactory.response.data.token
var Projects = {
};
Projects.get = function(id) {
return $http.get(API_URL + '/api/projects/' + uid + id);
};
Projects.create = function(userData) {
return $http.post(API_URL + '/api/projects/' + uid, userData).then(function error(response) {
var data = response.data;
});
};
return Projects;
});
Node
apiRouter.get('/authenticate', function(req, res) {
var uid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
return v.toString(16);
});
var token = jwt.sign({
uid: uid
}, superSecret, {
expiresInMinutes: 1440 // expires in 24 hours
});
res.json({
success: true,
message: 'Enjoy your token!',
uid: uid,
token: token
});
});

Angular Http Promise Result

I have a little abstraction I wrote around $http that I use to make calls from a security service. The problem I am having is that during a request that results in a 401, the deferred does not seem to propogate correctly.
I am using it like so, and even if it is a 401 it alerts good... I would expect this to alert bad.
security.login('test', 'password').then(function (r) {
alert('good');
}, function (r) {
alert('bad');
});
Http Wrapper
angular.module('app').provider('http', function () {
return {
$get: ['$http', 'url', function ($http, url) {
function http(config) {
config.headers = {
'X-Requested-With': 'XMLHttpRequest'
};
config.url = url.formatUrl(config.url);
return $http(config);
}
return {
delete: function (url, data) {
return http({ url: url, data: data, method: 'DELETE' });
},
get: function (url) {
return http({ url: url, method: 'GET' });
},
post: function (url, data) {
return http({ url: url, data: data, method: 'POST' });
},
put: function (url, data) {
return http({ url: url, data: data, method: 'PUT' });
}
};
}]
};
});
Security Service
function User() {
this.displayName = '';
this.permissions = [];
}
angular.module('app').provider('security', function () {
var _user = new User();
return {
$get: ['$rootScope', '$q', 'cookie', 'http', 'statePermissions', function ($rootScope, $q, cookie, http, statePermissions) {
function login(params) {
return http.post('SS/auth', params).then(function (response) {
cookie.set('authToken', response.data.authToken);
_user = response.data;
$rootScope.$broadcast('event:loginSuccessful', _user);
return response;
}, function (response) {
cookie.expireNow('authToken');
_user = new User();
$rootScope.$broadcast('event:loginUnsuccessful', _user);
$q.reject(response);
});
}
return {
doesAuthTokenExist: function () {
return angular.isDefined(cookie.get('authToken'));
},
getUser: function () {
return _user;
},
isUserAuthorizedForPermission: function (permission) {
var x;
for (x = 0; x < _user.permissions.length; x++) {
if (_user.permissions[x] === permission) {
return true;
}
}
return false;
},
isUserAuthorizedForState: function (stateName) {
if (angular.isDefined(statePermissions[stateName])) {
return this.isUserAuthorizedForPermission(statePermissions[stateName]);
}
return true;
},
login: function (userName, password) {
return login({ username: userName, password: password });
},
loginWithAuthToken: function () {
var authToken = cookie.get('authToken');
return login({ provider: 'token', authToken: authToken });
},
logout: function () {
return http.post('SS/auth/logout').then(function () {
cookie.expireNow('authToken');
_user = new User();
$rootScope.$broadcast('event:logoutSuccessful', _user);
return true;
});
}
};
}]
};
});
Your $http wrapper looks like it's duplicating the functionality of angular's $resource service. Check out the documentation here: http://docs.angularjs.org/api/ngResource.$resource
After a brief look at your code, it seems like the problem could be with how you're using $q. Try rewriting your login method along these lines.
With insight from here:
http://docs.angularjs.org/api/ng.$q
http://docs.angularjs.org/api/ng.$http
function login(params) {
var deferred = $q.defer();
http.post('SS/auth', params).success(function (response) {
cookie.set('authToken', response.data.authToken);
_user = response.data;
$rootScope.$broadcast('event:loginSuccessful', _user);
deferred.resolve(response);
}).error(function (error) {
cookie.expireNow('authToken');
_user = new User();
$rootScope.$broadcast('event:loginUnsuccessful', _user);
deferred.reject(error);
});
return deferred.promise;
}
Also, just curious why you are using providers here as it's not obvious to me?

Resources