How should you getCurrentUser after a page reload? - angularjs

Here is how I'm doing it so far:
angular
.module('mean-starter')
.factory('Auth', function($http, $state, $window, $cookies) {
var currentUser = {};
return {
signup: function(user) {
return $http
.post('/users', user)
.then(function(data, status, headers, config) {
angular.copy(data, currentUser);
$cookies.put('userId', data._id);
$window.location.href = '/';
})
;
},
login: function(user) {
return $http
.post('/login', user)
.then(function(data) {
angular.copy(data, currentUser);
$cookies.put('userId', data._id);
$window.location.href = '/';
})
;
},
logout: function() {
$http
.get('/logout')
.then(function() {
angular.copy({}, currentUser);
$cookies.remove('userId');
$window.location.href = '/';
})
.catch(function() {
console.log('Problem logging out.');
})
;
},
getCurrentUser: function() {
// user is logged in
if (currentUser._id) {
return currentUser;
}
// user is logged in, but page has been refreshed and currentUser is lost
if ($cookies.get('userId')) {
return $http.get('/current-user')
.then(function(data) {
angular.copy(data, currentUser);
})
;
}
// user isn't logged in
else {
return currentUser;
}
},
isLoggedIn: function() {
return !!currentUser._id;
}
};
})
;
After a page reload, the Auth factory gets re-run and currentUser is reassigned to {}. So if the user was logged in, currentUser won't reflect it. So I have to check for the case where !currentUser._id && $cookies.get('userId') and if so, query the database for the currently logged in user.
Now I want to access currentUser:
angular
.module('mean-starter')
.run(run)
;
function run($rootScope, Auth, $state) {
$rootScope.$on('$stateChangeStart', function(event, toState, toParams) {
if (typeof toState.authenticate !== 'undefined') {
var currentUser = Auth.getCurrentUser();
var admin = currentUser.role === 'admin';
var authorized = currentUser._id.toString() === toParams.id;
if (!Auth.isLoggedIn()) {
event.preventDefault();
alert('Must be logged in to access this route.');
$state.go('login');
}
else if (toState.authenticate.authorized) {
if (!admin && !authorized) {
event.preventDefault();
alert('You are not authorized to access that route.');
}
}
else if (toState.authenticate.isAdmin) {
if (!admin) {
event.preventDefault();
alert('You must be an admin to access this route.');
}
}
}
});
}
The problem is that I don't know whether or not Auth.getCurrentUser() will return the user or a promise. How can I check for this? How should this be architected?

Why not just always return a promise in your getCurrentUser with the help of $q?
So something like this
getCurrentUser: function() {
if (currentUser._id || !$cookies.get('userId')) {
// $q.when will wrap your currentUser into a promise
return $q.when(currentUser);
}
return $http.get('/current-user')
.then(function(data) {
angular.copy(data, currentUser);
return currentUser;
});
}
}
and in your controller:
Auth.getCurrentUser().then(function(currentUser) {
// Your code
})

You can adapt your function to return a promise in both cases using $q. In this case, all three logical paths should result in the same outcome, albeit by different sets of operations in between, therefore a promise would work perfectly here. Especially because you can have very specific control over the error handling flow (if needed)
http://markdalgleish.com/2013/06/using-promises-in-angularjs-views/

Related

AngularJs block ulr navigation when user is not logged in

I have to allow user to access application when user is logged in
and if user is not logged in then user should not be able to access the application by simply redirecting through url
In above case user should redirect to login page if he is not logged in
I tried in below way
$rootScope.tempObj = false;
$rootScope.$on('$stateChangeStart', function (event, toState) {
if($rootScope.tempObj) {
return;
} else {
$state.go('login');
event.preventDefault();
return;
}
});
Here i am doing $rootScope.tempObj = true in login success callback
But I am getting an error - RangeError: Maximum call stack size exceeded
My question is how to block url redirection if user is not logged in..
and allow few state like forgot password, reset password, etc without user logged in
use it with the event.preventDefault();
$rootScope.$on('$stateChangeStart', function(event, toState) {
if ($rootScope.tempObj) {
event.preventDefault();
} else if (!$rootScope.tempObj) {
$state.go('login');
event.preventDefault();
}
});
check the similar question
i think this way isn't perfect way to check user, if your application deals with backend by sending http requests you should test user privileges from $httpProvider, so in $httpProvider if you get a http code in 400 range, you should redirect user to login page,
i'll put a snipping code to do this
.config(["$httpProvider", "$cookiesProvider", function($httpProvider, $cookiesProvider) {
$cookiesProvider.defaults.expires = new Date(new Date().getTime() + 120 * 60 * 1000).toString();
// $httpProvider.defaults.timeout = 50;
var interceptor = function($q, $rootScope, $injector, $location, $filter) {
return {
request: function(config) {
return config;
},
requestError: function(config) {
return config;
},
response: function(res) {
return res;
},
responseError: function(res) {
var AUTH_EVENTS = $injector.get('AUTH_EVENTS');
var $toastContent = $('<span>' + $filter('translate')(res.data.message) + '</span>');
if (res.status == 403) {
$location.path('/logOut');
}
if (res.status == 402) {
$location.path('/home');
Materialize.toast($toastContent, 4000, 'danger');
}
if (res.status == 401) {
Materialize.toast($toastContent, 4000, 'danger');
}
// }
return $q.reject(res);
}
}
}
interceptor.$inject = ["$q", "$rootScope", "$injector", "$location", "$filter"];
$httpProvider.interceptors.push(interceptor);
}])

authentication mechanism in angularjs

i am newbie in AngularJS, i have read a tutorial about login and authentication with angular js but i still confused in many points of my code, for now i have arrived to login and to store a token in browser's session, but i can't redirect to home page after loggin in,
here is myservice :
function authenticationSvc ($http, $q, $window, auth_uri) {
var userInfo;
function login(username, password) {
var deferred = $q.defer();
$http.post(auth_uri, {
username: username,
password: password
}).then(function(result) {
userInfo = {
accessToken: result.data.token
};
$window.sessionStorage["pallas_token"] = result.data.token;
deferred.resolve(userInfo);
},
function(error) {
deferred.reject(error);
});
return deferred.promise;
}
function getUserInfo() {
return userInfo;
}
return {
login: login,
getUserInfo: getUserInfo
};
};
and this is my state config
.state('dashboard', {
url:'/dashboard',
controller: 'HomeController',
templateUrl: 'partials/dashboard/main.html',
resolve:{
auth: function($q, authenticationSvc) {
var userInfo = authenticationSvc.getUserInfo();
if (userInfo) {
return $q.when(userInfo);
} else {
return $q.reject({ authenticated: false });
}
}
}
}
finally this my .run block:
angular
.module ( 'mainApp' )
.run ( function ( $rootScope, $state, $location) {
$rootScope.$on('$stateChangeSuccess', function( userInfo) {
console.log( userInfo );
});
$rootScope.$on('$stateChangeError', function(evt, toState, toParams, fromState, fromParams, error) {
if (error.authenticated == false) {
$state.transitionTo("login");
}
});
});
please help me to resolve this issue, i need help my friends :(
i am sorry for missing to post my login controller, there is:
function LoginController($scope, $state, authenticationSvc){
$scope.submit = function(credentials){
authenticationSvc.login(credentials.username, credentials.password);
};
};
Your login method return a success promise when the user pass the authentication. So.. you can edit your controller in this way:
function LoginController($scope, $state, authenticationSvc){
$scope.submit = function(credentials){
authenticationSvc.login(credentials.username, credentials.password).then(function(){
$state.go('dashboard');
console.log('User logged in!');
}).catch(function(){
console.log('User NOT logged in!');
});
};
};
UPDATE
To maintain the state after a page refresh you need to restore the userInfo object from sessionStorage. I also added the logout logic! Take a look:
function authenticationSvc ($http, $q, $window, auth_uri) {
var userInfo;
function login(username, password) {
...
}
function logout() {
$window.sessionStorage.removeItem("pallas_token");
userInfo = null;
}
function getUserInfo() {
return userInfo;
}
function init(){
if ($window.sessionStorage["pallas_token"]){
userInfo = {
accessToken: $window.sessionStorage["pallas_token"]
};
}
}
init();
return {
login: login,
logout: logout,
getUserInfo: getUserInfo
};
};
Logout:
function LoginController($scope, $state, authenticationSvc){
$scope.submit = function(credentials){
...
};
$scope.logout = function(){
authenticationSvc.logout();
$state.go('login');
console.log('User logged out!');
};
};
Enjoy!

Creating an AuthService to check if user is logged in

I have an AngularJS app which communicates with a Laravel PHP backend. Sending a GET request to /api/checkLogin will return { logged: false, username: undefined, id: undefined } if the user is not logged in, otherwise, it will return something like { logged: true, username: 'John', id: 123 }.
I am not too familiar with using AngularJS services, but I would like to set up a service called AuthService that can, well, perform my app's authentication services.
I would like to implement the following functions: AuthService.loggedIn, AuthService.isAdmin, AuthService.username, and AuthService.id.
I want these functions implemented in such a way that calling one will set the values for all the rest. For example, let's say I call AuthService.isAdmin. This function will check if isAdmin is set, if so, it will return the value of isAdmin. If isAdmin is not set, it will make an HTTP request to /api/checkLogin, set the values for loggedIn, isAdmin, username, and id, and then return the value of isAdmin. How can I accomplish this?
Here is the service I have tried putting together:
angular.module('myApp').factory('AuthService', ['$http', function($http) {
var loggedIn;
var isAdmin;
var username;
var id;
var checkLogin = function() {
if(loggedIn != undefined) {
return loggedIn
} else {
setUserData(checkLogin);
}
}
var checkAdmin = function() {
if(isAdmin != undefined) {
return isAdmin
} else {
setUserData(checkLogin);
}
}
var returnUsername = function() {
if(username != undefined) {
return username
} else {
setUserData(checkLogin);
}
}
var returnId = function() {
if(id != undefined) {
return id
} else {
setUserData(checkLogin);
}
}
// Our function call which will set our loggedIn, isAdmin, username, and id values
var setUserData = function(callback) {
$http.get(baseURL+'/api/checkLogin').success(function(data) {
loggedIn = data.logged;
if(loggedIn) {
isAdmin = data.is_admin;
username = data.username;
id = data.id;
}
callback();
});
}
return {
loggedIn: function() { return checkLogin(); },
isAdmin: function() { return checkAdmin(); },
username: function() { return returnUsername(); },
id: function() { return returnId(); },
}
}]);
It looks you want to use checkLogin as a callback, but instead of doing it the way you have it, return the promise back to checkLogin from setUserData. Then in checkLogin, create your own deferred to handle the results.
You are acting on asynchronous logic by introducing the $http call, so checkLogin is going to need to return a promise in all cases:
var checkLogin = function() {
// Create a custom deferred
var defer = $q.defer();
if(loggedIn != undefined) {
// Resolve your deferred with the value of logged in
defer.resolve(loggedIn);
} else {
setUserData().then(function (data) {
console.log('Set user data returned successfully');
loggedIn = data.logged;
if(loggedIn) {
isAdmin = data.is_admin;
username = data.username;
id = data.id;
defer.resolve(loggedIn);
} else {
defer.reject();
}
}, function () {
console.log('setUserData failed');
defer.reject();
});
}
return defer.promise;
}
var setUserData = function() {
return $http.get(baseURL+'/api/checkLogin');
}
AuthService.loggedIn() will now return a promise. You have to resolve the promise to get the value out of it:
AuthService.loggedIn().then(function (data) {
console.log(data);
});
The function passed to then above will be called when the promise is resolved with the value that the promise was resolved with. In this case, its the value of data.logged because thats what was passed to defer.resolve in your checkLogin function.
Here's some reading on Promises:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
http://www.html5rocks.com/en/tutorials/es6/promises/
https://docs.angularjs.org/api/ng/service/$q
use this
loginService this
var app=angular.module('myApp');
app.factory('loginService', function ($http, $location) {
return {
login: function (data, scope) {
var $promise = $http.post('api.php/site/login', data);
$promise.then(function (msg) {
var uId = msg.data.key;
if (msg.data.key) {
$location.path('/abAdmin/home');
} else {
$location.path('/abAdmin');
}
});
},
logout: function () {
$http.post('api.php/site/logout');
$location.path('/abAdmin');
},
isLogged: function () {
var $check = $http.post('api.php/site/checkSession');
return $check;
}
}
});
and your app.js
var app=angular.module('myApp');
app.run(function ($rootScope, $location, loginService) {
var routPermission = [
'/abAdmin/home',
'/abAdmin/category',
'/abAdmin/category/:id'];
$rootScope.$on('$routeChangeStart', function (e, current) {
if ( routPermission.indexOf(current.$$route.originalPath) != -1) {
var connected = loginService.isLogged();
connected.then(function (data) {
if (!data.data.isLogged) {
logged=true;
$location.path('abAdmin');
}
})
}
});
});

Deferred promise not deferring

Hi in the following Angular controller i try to initiate facebook login with Parse.com.
So I created a promise triggered on fbLogIn. What it is supposed to do, is first login to facebook, and grab first_name and store it in fieldValuesService.ff.
THEN, it is supposed to access this value and do something with it. For illustration purpose I just used console logs.
What happens is that the second console.log in second then is triggered before the first one from first .then thus is undefined.
I don't understand why anything in the second .then can be triggered before first one in this situation.
Also second problem, after a logout, the fbLogIn function is sometime inactive: it won't trigger the login process again.
If you have a clue on this issue your help will be greatly appreciated.
.controller('logController',
function ($scope, $q, fieldValuesService) {
var defer = $q.defer();
defer.promise
.then(function() {
Parse.FacebookUtils.logIn(null, {
success: function(user) {
if (!user.existed()) {
alert("User signed up and logged in through Facebook!");
} else {
$scope.currentUser = user;
$scope.$apply();
FB.api('/me', function(response) {
fieldValuesService.ff = response.first_name;
console.log(fieldValuesService.ff); //logs bob
});
}
},
error: function(user, error) {
alert("User cancelled the Facebook login or did not fully authorize.");
}
});
})
.then(function(){
console.log(fieldValuesService.ff); //logs undefined
});
$scope.fbLogIn = function() {
defer.resolve();
};
// Parse log out
$scope.logOut = function(form) {
Parse.User.logOut();
$scope.currentUser = null;
};
});
Maybe if you restructure your code, things will become a little bit easier.
I recommend to refactor everything FB related into its own service like:
module.factory('FBService', function ($q) {
var login,
logout,
getInformation;
login = function () {
var defered = $q.defer();
Parse.FacebookUtils.logIn(null, {
success: function (user) {
defered.resolve(user);
},
error: function (user, error) {
defered.reject(user, error);
}
});
return defered.promise;
};
logout = function () {
var defered = $q.defer();
Parse.User.logOut();
defered.resolve();
return defered.promise;
};
getInformation = function () {
var defered = $q.defer();
FB.api('/me', function (response) {
defered.resolve(response);
});
return defered.promise;
}
return {
login: login,
logout: logout,
getInformation: getInformation
};
});
module.controller("LoginCtrl", function ($scope, FBService, fieldValuesService) {
$scope.fbLogIn = function () {
FBService.login().then(function (user) {
$scope.currentUser = user;
return FBService.getInformation();
}).then(function (information) {
fieldValuesService.ff = information.first_name;
console.log(fieldValuesService.ff);
});
};
$scope.logOut = function () {
FBService.logout().then(function () {
$scope.currentUser = null;
});
};
});

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