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

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);
});

Related

Sending an access token to server using 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?

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...
});
}])

Storing tokens with OAuth 2.0 in Angular

I have an app which displays Google Calendar data, but it requires an initial login. I know it's possible to store tokens using OAuth 2.0, but I'm not sure how to go about doing it. Here is my code below. I'd like for the webpage to display the a calendar using JSON data from a google calendar without login.
Controller
angular.module('demo', ["googleApi"])
.config(function(googleLoginProvider) {
googleLoginProvider.configure({
clientId: '239511214798.apps.googleusercontent.com',
scopes: ["https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/calendar", "https://www.googleapis.com/auth/plus.login"]
});
})
.controller('DemoCtrl', ['$scope', 'googleLogin', 'googleCalendar', 'googlePlus', function ($scope, googleLogin, googleCalendar, googlePlus) {
$scope.login = function () {
googleLogin.login();
};
$scope.$on("googlePlus:loaded", function() {
googlePlus.getCurrentUser().then(function(user) {
$scope.currentUser = user;
});
})
$scope.currentUser = googleLogin.currentUser;
$scope.loadEvents = function() {
this.calendarItems = googleCalendar.listEvents({calendarId: this.selectedCalendar.id});
}
$scope.loadCalendars = function() {
$scope.calendars = googleCalendar.listCalendars();
}
}]);
googleAPi
angular.module('googleApi', [])
.value('version', '0.1')
.service("googleApiBuilder", function($q) {
this.loadClientCallbacks = [];
this.build = function(requestBuilder, responseTransformer) {
return function(args) {
var deferred = $q.defer();
var response;
request = requestBuilder(args);
request.execute(function(resp, raw) {
if(resp.error) {
deferred.reject(resp.error);
} else {
response = responseTransformer ? responseTransformer(resp) : resp;
deferred.resolve(response);
}
});
return deferred.promise;
}
};
this.afterClientLoaded = function(callback) {
this.loadClientCallbacks.push(callback);
};
this.runClientLoadedCallbacks = function() {
for(var i=0; i < this.loadClientCallbacks.length; i++) {
this.loadClientCallbacks[i]();
}
};
})
.provider('googleLogin', function() {
this.configure = function(conf) {
this.config = conf;
};
this.$get = function ($q, googleApiBuilder, $rootScope) {
var config = this.config;
var deferred = $q.defer();
return {
login: function () {
gapi.auth.authorize({ client_id: config.clientId, scope: config.scopes, immediate: false}, this.handleAuthResult);
return deferred.promise;
},
handleClientLoad: function () {
gapi.auth.init(function () { });
window.setTimeout(checkAuth, 1);
},
checkAuth: function() {
gapi.auth.authorize({ client_id: config.clientId, scope: config.scopes, immediate: true }, this.handleAuthResult );
},
handleAuthResult: function(authResult) {
if (authResult && !authResult.error) {
var data = {};
$rootScope.$broadcast("google:authenticated", authResult);
googleApiBuilder.runClientLoadedCallbacks();
deferred.resolve(data);
} else {
deferred.reject(authResult.error);
}
},
}
};
})
.service("googleCalendar", function(googleApiBuilder, $rootScope) {
var self = this;
var itemExtractor = function(resp) { return resp.items; };
googleApiBuilder.afterClientLoaded(function() {
gapi.client.load('calendar', 'v3', function() {
self.listEvents = googleApiBuilder.build(gapi.client.calendar.events.list, itemExtractor);
self.listCalendars = googleApiBuilder.build(gapi.client.calendar.calendarList.list, itemExtractor);
self.createEvent = googleApiBuilder.build(gapi.client.calendar.events.insert);
$rootScope.$broadcast("googleCalendar:loaded")
});
});
})
.service("googlePlus", function(googleApiBuilder, $rootScope) {
var self = this;
var itemExtractor = function(resp) { return resp.items; };
googleApiBuilder.afterClientLoaded(function() {
gapi.client.load('plus', 'v1', function() {
self.getPeople = googleApiBuilder.build(gapi.client.plus.people.get);
self.getCurrentUser = function() {
return self.getPeople({userId: "me"});
}
$rootScope.$broadcast("googlePlus:loaded")
});
});
})
What you will want to do is after the result comes back you will want to save it off to localStorage or a cookie and then use that in the future if it exists.
Essentially you will need to update your handleAuthResult to store the result from the Google API:
handleAuthResult: function (authResult) {
if (authResult && !authResult.error) {
var data = {};
$rootScope.$broadcast("google:authenticated", authResult);
googleApiBuilder.runClientLoadedCallbacks();
// here you will store the auth_token
window.localStorage.setItem('auth_token', authResult.token /*I don't know what this response looks like, but it should be similar to this*/ );
deferred.resolve(data);
} else {
deferred.reject(authResult.error);
}
},
Live Demo

Angularjs async callback return undefined under $scope.$apply();

This is my factory code. The callback is async so i put it under $rootScope.safeApply().
Then I call console.log(authService.authUser) in my controller but it still return undefined when user logged in. But it is find if user not login and will show 'not login' in console. Any idea?
myapp.factory('authService', ['$rootScope', function($rootScope) {
var auth = {};
$rootScope.safeApply = function(fn) {
var phase = this.$root.$$phase;
if (phase == '$apply' || phase == '$digest') {
if(fn && (typeof(fn) === 'function')) {
fn();
}
} else {
this.$apply(fn);
}
};
auth.firebaseAuthClient = new FirebaseAuthClient(FIREBASEREF, function(error, user) {
$rootScope.safeApply(function() {
if (user) {
auth.authUser = user;
//auth.isLoggedIn = true;
} else if (error) {
auth.authError = error;
} else {
auth.not = 'not login';
//auth.isLoggedIn = false;
}
});
});
auth.login = function() {
this.firebaseAuthClient.login('facebook');
};
auth.logout = function() {
this.firebaseAuthClient.logout();
};
return auth;
}]);
UPDATED
auth.callback = function(error, user) {
if (user) {
deferred.resolve(user);
} else if (error) {
deferred.reject(error);
} else {
//deferred.reject('not login'); // there is no callback value here
}
return deferred.promise;
}
in controller
callback().then(function(response) {
$scope.isLoggedIn = true;
}, function(response) {
$scope.isLoggedIn = false //How can i set false here?
});
UPDATE 2
Now every thing work fine, I'm able to monitoring user login state. But still having a problem. Check the code below
authService.callback().then(function(success){
$rootScope.isLoggedIn = true; //If promise return success set isLoggedIn true
}, function(fail){
**//If user not login set isLoggedIn false;
//I have problem here because i'm not able to deferred.reject below**
$rootScope.isLoggedIn = false;
})
auth.callback = function(error, user) {
$timeout(function() {
if (user) {
deferred.resolve(user);
} else if (error) {
deferred.reject(error);
} else {
//If this line is added,
//.then() will not return anything not even undefined with no error,
//No mater user logged-in or not login.
//If I comment it out, everything will work fine but how can I
//set isLoggedIn = false?
deferred.reject();
}
}, 0);
return deferred.promise;
}
Wrap the outside service's deferred resolve in a $timeout block to let angular know when its resolved. This way when your controller runs then callback, it'll be in a $digest cycle.
See this fiddle as a working proof of concept: http://jsfiddle.net/Zmetser/rkJKt/
// in controller
authService.login().then(success, error);
// service
myapp.factory('authService', ['$q', '$timeout', function( $q, $timeout ) {
var auth = {},
deferred;
firebaseAuthClient = new FirebaseAuthClient(FIREBASEREF, afterAuth);
function afterAuth( error, user ) {
// Let angular know the deferred has been resolved.
$timeout(function () {
if (user) {
deferred.resolve(user);
} else if (error) {
deferred.reject(error);
} else {
deferred.reject(); // there is no callback value here
}
}, 0);
}
auth.login = function() {
deferred = $q.defer();
firebaseAuthClient.login('facebook');
return deferred.promise;
};
auth.logout = function() {
deferred = $q.defer();
firebaseAuthClient.logout();
return deferred.promise;
};
return auth;
}]);

Resources