Angular : Wait for app run to get response - angularjs

As of this post, I'm trying to figure out if the user is logged in (using a token based authentication).
The scheme is following :
1/ The page loads, app run is called, and authenticated is set to false as default
app.run(function($http, UserService) {
UserService.requestCurrentUser();
$http.defaults.xsrfHeaderName = 'X-CSRFToken';
$http.defaults.xsrfCookieName = 'csrftoken';
});
app.constant('AUTHENTICATED', false);
2/ UserService call for its method requestCurrentUser() in which a http get is sent to the correct url with the token in its header.
If token is correct, this sends back the user (success case, we're authenticated).
If not, I get a permission error (error case, we're not authenticated).
This updates currentUserproperty and AUTHENTICATED constant.
app.factory('UserService', function ($http, $q, $window, AUTHENTICATED) {
var _currentUser = {};
return {
getCurrentUser: function() {
return _currentUser;
},
setCurrentUser: function(user) {
_currentUser = user;
},
requestCurrentUser: function() {
return $http.get('/accounts/api/').then(
function (response) {
_currentUser = response.data;
AUTHENTICATED = true;
},
function (error) {
AUTHENTICATED = false;
}
);
},
};
});
3/ Controller is called and authenticated and currentUser scope values are updated.
app.controller('AuthCtrl', function ($scope, AuthService, UserService, AUTHENTICATED) {
$scope.authenticated = AUTHENTICATED;
$scope.currentUser = UserService.getCurrentUser();
});
Problem is that controller tries to reach the values before requestCurrentUser method (launched in app run) has received a response. So where should I launch requestCurrentUser to get the expected behavior ?
Thanks

What you could do it wrap your user state object in a parent object. For example:
var state = {
_currentUser: {}
};
return {
getUserState: function(){ return state; }
};
Then inside your controller:
$scope.state = UserService.getUserState();
This way, when your user updates (no matter when or how in your service), anything bound to the state will receive the update. So your controller will have access to state._currentUser when it is available.

Related

Angularjs: allow unauthorized access only for one page

I am using ui-router. I am trying to authenticate all pages except sign up page.
Here are important parts of code:
In app.js:
$transition.onStart({ to: function(state) {
return state.data != null && state.data.authRequired === true;
}},function(trans){
var AuthService = trans.injector().get('AuthService');
....
});
In routes.js:
$stateProvider.state('signup', {
url: '/signup',
templateUrl:'views/signeup.html',
controller: 'SigneUp',
data: {
authRequired: false
}
});
But I am not allowed to go to signup page unless I am authenticated.
You will need to have a service that does Authorization and stores state of current auth for any given user. From there, in each of your controllers, check for auth status, where required, allow access when not logged in; where not, make a stop gate.
eg:
angular.module('app', [])
.controller('ctrlname', ['$scope', '$location', 'myAuthenticationService', function($scope, $location, myAuthenticationService){
//userId and Password to be bound to partials via ng-model.
if (myAuthenticationService.authorizeUser(userId, password)){
// DO what you have to for an authorized user.
}
else{
//
$location.route('/unauthorized');
}
}]
.service('myAuthenticationService', ['$http', function($http){
var self = this;
//This is just for reference, might need more sophesticated authentication on server side anyways.
self.authorizeUser = function(userId, password){
return $http.get(`url/?userId=${userId}&password=${password}`)
.success(function(response){
//If user is validated,
return true;
})
.error(function(error){
return false;
})
}
return {
authorizeUser: function(userId, password){
return self.authorizeUser(userId, password);
}
}
}]
You could define your routes and add corresponsing controllers in routes.js.

Angular passing data from factory to controller

I'm trying to store an authorized user id variable, which I can pass to controllers. I know there's an issue with how I'm trying to pass the data from inside the closure of my factory object, but I'm stuck on how to fix it.
Here is my factory:
myApp.factory('Authentication', function($firebase,
$firebaseAuth, FIREBASE_URL, $location) {
var ref = new Firebase(FIREBASE_URL);
var simpleLogin = $firebaseAuth(ref);
var authorized;
var myObject = {
login : function() {
return simpleLogin.$authAnonymously().then(function(authData) {
authorized = authData.uid;
console.log("Logged in as:", authData.uid);
}).catch(function(error) {
console.error("Authentication failed:", error);
});
},
auth : authorized
} //myObject
return myObject;
});
Here is my controller:
myApp.controller('MeetingsController',
function($scope, $firebase, Authentication) {
var ref = new Firebase('http://i2b2icons.firebaseio.com/');
var meetings = $firebase(ref);
$scope.authid = Authentication.auth;
$scope.meetings = meetings.$asObject();
// $scope.id = = Authentication.login.id;
$scope.addMeeting=function() {
meetings.$push({
name: $scope.meetingname,
date: Firebase.ServerValue.TIMESTAMP
}).then(function() {
$scope.meetingname = '';
});
} //addmeeting
$scope.deleteMeeting=function(key) {
meetings.$remove(key);
} //deletemeeting
}); //MeetingsController
I'm really just trying to get the $scope.authid variable to pick up the value of auuthorized from the the login function of myObject.
The login method should have been called already by logging in via this controller:
myApp.controller('RegistrationController',
function($scope, $firebaseAuth, $location, Authentication) {
$scope.login = function() {
Authentication.login();
} //login
}); //RegistrationController
You are just setting the local variable authorized in your factory, it has no relation to the Authentication.auth that you are trying to access in your controller (unless ofcourse you set the value to it while creating the factor and which is not the intention anyway). Instead return a predefined object in your factory and return that object from it. Set the property on the object reference.
myApp.factory('Authentication', function($firebase,
$firebaseAuth, FIREBASE_URL, $location) {
var ref = new Firebase(FIREBASE_URL);
var simpleLogin = $firebaseAuth(ref);
//Predefine the factory
var factory = {
login: login,
authorized: null
};
function login() {
return simpleLogin.$authAnonymously().then(function(authData) {
factory.authorized = authData.uid; //Set the property here
}).catch(function(error) {});
}
//return it
return factory;
});
With this provided you have the reference of the factory and updates to its property will reflect (provided you invoke the method that populates the data) in your controller. Another way would be to use a getter function in your factory to return the auth object, or you can as well cache the promise returned by login function and return it and invalidate it when an event occurs that log the user out.
As others have already pointed out you only update the variable authorized, not the property auth. One rather simple solution is to change authto a getter, that always returns the current value:
var myObject = {
login : function() {
...
},
get auth() {
return authorized;
}
You don't have to change any other code.

AngularFire: how to set $location.path in onAuth()?

I am building an angular+firebase app with user authentication (with angularfire 0.8).
I need to use onAuth() auth event handler, since I will provide multiple authentication paths, included social, and want to avoid code duplication. Inside onAuth callback, I need to reset location.path to '/'.
Usually everything works nicely, but, if app is loaded on an already authenticated session (<F5>, for example), on $scope.$apply() I get "Error: [$rootScope:inprog] $apply already in progress"
(if I don't use $scope.$apply(), location path is not applyed to scope, and no page change happens).
I suspect I make some stupid mistake, but can't identify it...
This is my workflow:
app.controller('AuthCtrl', function ($scope, $rootScope, User) {
var ref = new Firebase(MY_FIREBASE_URL);
$scope.init = function () {
$scope.users = [];
User.all.$bindTo($scope, 'users').then(function () {
console.info('$scope.users bound:', $scope.users);
});
};
$scope.login = function () {
ref.authWithPassword({
email: $scope.user.email,
password: $scope.user.password,
}, function(err) {
if (err) {
console.error('Error during authentication:', err);
}
});
};
ref.onAuth(function(authData) {
if (authData) {
console.info('Login success');
var $rootScope.currentUser = $scope.users[authData.uid];
$location.path('/');
$scope.$apply();
} else {
console.info('Logout success');
}
});
};
app.factory('User', function ($firebase) {
var ref = $firebase(new Firebase(MY_FIREBASE_URL + 'users'));
return {
all: ref.$asObject()
};
});
Just as a reference, I want to post the solution I found, and I'm currently adopting:
$scope.init = function () {
$scope.params = $routeParams;
$scope.debug = CFG.DEBUG;
$scope.lastBuildDate = lastBuildDate;
$scope.error = null;
$scope.info = null;
$scope.users = [];
User.all.$bindTo($scope, 'users').then(function () {
// watch authentication events
refAuth.onAuth(function(authData) {
$scope.auth(authData);
});
});
...
};
...
I.e., it was enough to move the watch on authentication events inside the callback to bindTo on users object from Firebase.

class variable in service in angularjs

Im developing a simple Account Management page where a user can login, register etc
Each section has its own controller. i.e The login page has a login controller.
These controllers share a common service AccountService to handle the login, register mechanics.
This is my AccountService:
app.service('AccountService', function($http){
var user = {};
this.isLoggedIn = function(){
return user.isAuth;
};
this.login = function(user, callback){
//...//
if(success){
//Mark user as authenticated
user.isAuth = true;
}
};
});
As you can see, if I login by doing AccountService.login(user) the variable user.isAuth is set to true upon success. BUT the problem is if I then call AccountService.isLoggedIn() I expect the function to return true but it returns undefined
For example if I do:
app.controller('LoginController', function($scope, AccountService) {
$scope.login = function(user) {
//Do Login
AccountService.login(user, function(r){
result = r;
}); //At this point user.isAuth should be true
//Check if user is logged in
console.log(AccountService.isLoggedIn()); //This returns false
}
}
So how do I persist the variables in a service?
Edit
#itcouldevenbeaboat suggested that the value is not set before the second function is called due to the asynchronous call. However it still doesn't work when I separate the function call from each other like so:
app.controller('LoginController', function($scope, AccountService) {
$scope.login = function(user) {
//Do Login
AccountService.login(user, function(r){
result = r;
}); //At this point user.isAuth should be true
}
$scope.isAuth = function(){
//Check if user is logged in
console.log(AccountService.isLoggedIn()); //This STILL returns false
}
}
I still get a false when I call the login function and then the isAuth function on the LoginController
Your login function is asyncronous. So the console.log is actually running before the data comes back.
Try this instead,
$scope.login = function(user) {
//Do Login
AccountService.login(user, function(r){
result = r;
console.log(AccountService.isLoggedIn()); //This returns true
});
}

How to get the authenticated user info and use in all controllers and services?

I'm using angularFireAuth and I want to retrieve the logged in user's info and use in
all the controllers or services when the app is initial.
Currently, I used this in every controller but i having some problem.
$scope.$on("angularFireAuth:login", function(evt, user){
console.log(user);
});
The callback will not call if it is not a full page load or return null when app initial.
I need some tips for how can I return the authenticated user's info so I can use when app is initial and in all the controllers and services.
Example
When in controller or services
$scope.auth.user.id will return user's ID
$scope.auth.user.name will return user's name
etc
I would start with a userService for this:
angular.module('EventBaseApp').service('userService', function userService() {
return {
isLogged: false,
username: null
}
});
And write a LoginCtrl controller:
angular.module('EventBaseApp')
.controller('LoginCtrl', function ($scope, userService, angularFireAuth) {
var url = "https://example.firebaseio.com";
angularFireAuth.initialize(url, {scope: $scope, name: "user"});
$scope.login = function() {
angularFireAuth.login("github");
};
$scope.logout = function() {
angularFireAuth.logout();
};
$scope.$on("angularFireAuth:login", function(evt, user) {
userService.username = $scope.user;
userService.isLogged = true;
});
$scope.$on("angularFireAuth:logout", function(evt) {
userService.isLogged = false;
userService.username = null;
});
});
Inject the userService anywhere you want the user.
My app that am currently working on that uses this - https://github.com/manojlds/EventBase/blob/master/app/scripts/controllers/login.js
Based on ideas presented here - http://blog.brunoscopelliti.com/deal-with-users-authentication-in-an-angularjs-web-app
i'm not sure quite what your question is. but if you are looking to authorise once rather than in each controller, you can put the code into the module instead and put it into the $rootScope.
var myapp = angular.module('myapp').run(
function ($rootScope) {
$rootScope.user = null;
$rootScope.$on("angularFireAuth:login", function (evt, user) {
$rootScope.user = user;
});
});

Resources