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
});
}
Related
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.
I'm using Satellizer for authentication in my Angular app and have pretty much everything working... except that I can't seem to figure out how to display the username (or email) after successful login in the navbar.
My login controller looks like this
$scope.login = function() {
$auth.login($scope.user).then(function(response) {
$scope.user = JSON.stringify(response.data.user);
localStorage.setItem('user', user);
$scope.user = response.data;
$rootScope.authenticated = true;
$rootScope.currentUser = response.data.user;
// redirect user here after successful login
$state.go('home');
}
}
I have this in my $states (using UI Router) for global access
.run(function ($rootScope, $auth, $state) {
$rootScope.$on('$stateChangeStart', function (event, toState, fromState) {
if (toState.loginRequired && !$auth.isAuthenticated()) {
//if ($auth.isAuthenticated()) {
$rootScope.currentUser = JSON.parse($state, localStorage.currentUser);
//$rootScope.currentUser = JSON.stringify($state, localStorage.currentUser);
$state.go('/login');
event.preventDefault();
};
});
});
And then this in my navbar controller
.controller('NavbarCtrl', function($scope, $rootScope, $window, $auth) {
$scope.isAuthenticated = function() {
return $auth.isAuthenticated();
$scope.user.email = $localStorage.currentUser.email;
}
}
I'm not getting any errors in the console, so I'm not sure exactly where I'm going wrong...?
currentUser is undefined in the localStorage, I thought I was setting that in my login controller code above...?
First, you store the user object after login in localStorage.user, then you read currentUser in NavbarCtrl with
$localStorage.currentUser.email;
You should use the same property user, i.e
$scope.user.email = localStorage.user.email;
But then, why do you need this in local storage?
since you put the currentUser in the $rootScope, you should be able to directly use it in your navbar, e.g.
<span>{{currentUser.email}}</span>
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.
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;
});
});
Been playing around with firebase and angularjs and just trying to put some little things together. I have the auth working now in a controller with this function called on the sign in button click:
$scope.signin = function(){
var user1 = $scope.cred.user;
var pass1 = $scope.cred.password;
var ref = new Firebase("https://kingpinapp.firebaseio.com");
var auth = new FirebaseAuthClient(ref, function(error, user) {
if (user) {
// user authenticated with Firebase
console.log(user);
} else if (error) {
// an error occurred authenticating the user
console.log(error);
} else {
// user is logged out
}
});
auth.login('password', {
email: user1,
password: pass1,
rememberMe: false
});
console.log("tracer");
}
Now this is great and works fine. But it seems to work in a async manner for example my console.log("tracer") returns before the user object of the auth.login. I know I probably need to work with promises to get this done and tried doing the following:
var defer = $q.defer();
defer.auth
.then(function() {
auth.login('password', {
email: user1,
password: pass1,
rememberMe: false
});
})
.then(function() {
console.log("tracer");
})
But i'm receiving a $q is not defined after declaring it in the controller module. So what I'm trying to do is
check if the user is logged in.
wait till I receive a yes/no
if not logged in. use auth.login
else user user logged in do some other things
I thought of putting the auth.login function in the else of the variable auth but that doesn't seem like it would work. Just trying to figure out the proper logic in understanding how to get this to work.
You don't want to have a FirebaseAuthClient per controller, but you do want to alert all of your controllers when a user's auth state changes.
FirebaseAuthClient will take care of session storage for you. You just need to hide your sign in screen/button once a user is successfully signed in.
Try something like this:
.service('myAuthService', ["$rootScope", function($rootScope) {
var ref = new Firebase("https://kingpinapp.firebaseio.com");
this.auth = new FirebaseAuthClient(ref, function(error, user) {
if (user) {
$rootScope.$emit("login", user);
}
else if (error) {
$rootScope.$emit("loginError", error);
}
else {
$rootScope.$emit("logout");
}
});
}])
.controller('myCtrl', ["$scope", "$rootScope", "myAuthService", function($scope, $rootScope, myAuthService) {
$scope.signin = function() {
var user1 = $scope.cred.user;
var pass1 = $scope.cred.password;
myAuthService.auth.login('password', {
email: user1,
password: pass1,
rememberMe: false
});
}
// listen for user auth events
$rootScope.$on("login", function(event, user) {
// do login things
$scope.user = user;
})
$rootScope.$on("loginError", function(event, error) {
// tell the user about the error
})
$rootScope.$on("logout", function(event) {
// do logout things
})
}])
<button ng-show="user" ng-click="signin()">Sign in</button>
Make sure you are including $q as a dependency in your controller. In the simple case:
function MyController($scope, $q, angularFire) {
$scope.signin = ...
}
Or if you're using the "proper" module syntax:
angular.module("MyModule", ["firebase"]).
controller("MyController", ["$scope", "$q", "angularFire", function($scope, $q, aF) {
$scope.signin = ...
}]);
Learn more about angular dependency injection here: http://docs.angularjs.org/guide/di, and take a look at https://gist.github.com/sbrekken/5151751 for an example of Firebase auth using deferred.
I've posted the answer to this question already here FACTORY: get current user.id for Firebase Simple Login (Email / Password)
It's a pretty solid solution to this problem and may be just what you're looking for.