Firebase Simple Login $logout function does not set user property to null - angularjs

Per the documentation on the $logout method for $firebaseSimpleLogin, the API says clearly that
When logout is called, the $firebaseSimpleLogin:logout event will be fired, and the user >property on the object will be set to null.
However, it doesn't seem to work for me.
Here are snippets of my code:
var firebaseUrl = 'https://MYURL.firebaseio.com/';
var firebaseObject = new Firebase(firebaseUrl);
$rootScope.firebaseLoginObject = $firebaseSimpleLogin(firebaseObject, function(error, user) {
if (error) {
console.log(error);
} else if (user) {
console.log('User ID: ' + user.uid + ', Provider: ' + user.provider);
} else {
console.log('User is logged out');
// user is logged out
}
});
// Function for logging out.
$scope.logout = function() {
$rootScope.firebaseLoginObject.$logout();
console.log('i logged out successfully');
console.log($rootScope.firebaseLoginObject);
console.log($rootScope.firebaseLoginObject.user);
console.log($rootScope.user);
$location.path('/');
};
The output I get in the console after clicking "ng-click=logout()" shows that $rootScope.firebaseLoginObject is not set to null.

The problem is with $scope.logout. Your console.log statements are being executed before the $logout function has completed its work.
$scope.logout = function() {
$rootScope.firebaseLoginObject.$logout();
// This is not necessarily true. At this point, you may still be
// logged in since $logout can take an unknown amount of time to finish.
console.log('i logged out successfully');
console.log($rootScope.firebaseLoginObject);
console.log($rootScope.firebaseLoginObject.user);
console.log($rootScope.user);
$location.path('/');
};
To ensure you're logging the state of the firebaseLoginObject after $logout has finished, wait until the logout event has been broadcasted on $rootScope.
$rootScope.$on("$firebaseSimpleLogin:logout", function(event) {
// Log values here.
});

Related

How can I get protractor to wait for a login before proceeding?

I have a non-angular entry page to my app and I'm trying to first login:
describe('Authentication', function() {
it('should authenticate a user', function() {
browser.driver.get('https://example.com')
browser.driver.findElement(by.id('username')).sendKeys("user");
browser.driver.findElement(by.id('password')).sendKeys("mypass");
browser.driver.findElement(by.tagName('input')).click()
var url = browser.getLocationAbsUrl()
browser.driver.sleep(1)
browser.waitForAngular()
return
})
})
However, this gives an error:
Failed: Error while waiting for Protractor to sync with the page: "window.angular is undefined. This could be either because this is a non-angular page or bec
ause your test involves client-side navigation, which can interfere with Protractor's bootstrapping. See http://git.io/v4gXM for details"
What can I do to resolve this?
I wrote some helpers in the past to get this work in my e2e-tests:
waitForUrlToChangeTo: function (urlToMatch) {
var currentUrl;
return browser.getCurrentUrl().then(function storeCurrentUrl(url) {
currentUrl = url;
})
.then(function waitForUrlToChangeTo() {
browser.ignoreSynchronization = true;
return browser.wait(function waitForUrlToChangeTo() {
return browser.getCurrentUrl().then(function compareCurrentUrl(url) {
browser.ignoreSynchronization = false;
return url.indexOf(urlToMatch) !== -1;
});
});
}
);
},
login : function (username, password, url) {
browser.get('#/login');
element(by.model('username')).sendKeys(username);
element(by.model('password')).sendKeys(password);
element(by.buttonText('LOGIN')).click();
return this.waitForUrlToChangeTo(url);
}
And then in tests:
describe('when I login with valid credentials', function() {
it('should redirect to dashboard', function() {
helper.login('user', 'pass', '#/dashboard').then(function() {
expect(browser.getTitle()).toMatch('Dashboard');
});
});
});
I would say wait for logged in page until it displays properly and than do action. For e.g,
Target some element in logged-in page and wait for it.
Wait for url change, etc.
login -> browser.sleep(500)/wait for logged in page's element/URL change ->
other action
browser.driver.wait(function(){
expectedElement.isDisplayed().then(function (isVisible){
return isVisible === true;
},50000, 'Element not present ');
},50000);
if that element is not present within specified time, timeout error
would display & you would know unitl that time it's not logged in.

When I refresh my page Firebase authData becomes null

var user = null;
this.showLoginDialogGoogle = function(){
var ref = new Firebase("https://googlelogin1.firebaseio.com");
ref.authWithOAuthPopup("google", function(error, authData) {
if (error) {
console.log("Login Failed!", error);
} else {
console.log("Authenticated successfully with payload:", authData);
user = authData;
This.goToPage('/profile');
$rootScope.flag=true;
$rootScope.$digest();
}
});
};
I have authenticated the user with firebase google authentication. The problem is when I refresh my page, my session expires and authData becomes null. What can I do so that after refreshing the page my authData remains with the data it got at the time of authentication?
You'll want to monitor authentication state:
// Register the callback to be fired every time auth state changes
var ref = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com");
ref.onAuth(function authDataCallback(authData) {
if (authData) {
console.log("User " + authData.uid + " is logged in with " + authData.provider);
} else {
console.log("User is logged out");
}
});
Note that you're not using the AngularFire authentication wrappers. While your approach will authenticate the user, you will have to notify Angular of the updated scope yourself (see $timeout()). If you'd rather not do that yourself, look at AngularFire's auth wrappers, specifically $onAuth()

AngularJS does not update after value change

My intention is to update the navigation bar after the user login. Basically, it should change Login to Hi, {{usr.username}} right after the login.
However, it does not update after logging in and I have to click on Login again to trigger the change. However, in the console, the user info is logged right after the login.
In the index.html, the part of the code looks like:
<div class="item" ng-click="loginmodal()" ng-hide="loggedIn">Log in</div>
<div class="item" ng-show="loggedIn">Hi, {{usr.username}}</div>
where $scope.loggedIn is initialized as false and $scope.usr as null. I am using firebase for authentication:
FirebaseRef.authWithPassword({
"email" : email,
"password" : password
}, function(error, authData) {
if (error) {
console.log('Login Failed!', error);
} else {
console.log('Authenticated successfully with payload:', authData);
FirebaseRef.child("users").child(authData.uid).once('value', function(dataSnapshot) {
$scope.usr = dataSnapshot.val();
});
$scope.loggedIn = true;
console.log($scope.usr);
console.log($scope.loggedIn);
}
});
In console, I have $scope.loggedIn as true, but have $scope.usras null.
Is it wrong how I am using the authWithPassword() function or can I force the change to be updated?
AngularJS won't update the view if any changes to the $scope object are done outside of its $digest loop.
But worry not, Firebase is such a popular tool it has a AngularJS service. It's called AngularFire and you should be using it instead of global Firebase object.
So your code will be something similar to:
var auth = $firebaseAuth(FirebaseRef);
auth.$authWithPassword({
email: email,
password: password
}).then(function (authData) {
console.log('Authenticated successfully with payload:', authData);
var sync = $firebase(FirebaseRef.child("users").child(authData.uid));
var syncObject = sync.$asObject();
syncObject.$bindTo($scope, "usr");
}).catch(function (error) {
console.error('Login Failed!', error);
});
Read more in the documentation of AngularFire
Try calling $scope.$digest() right after changing $scope.usr value.
Anyway, notice that you call to Firebase to get the user is asynchronous, so I wouldn't put dependent code after this call, but inside the callback.
Call $scope.$apply();
FirebaseRef.child("users").child(authData.uid).once('value', function(dataSnapshot) {
$scope.$apply(function(){
$scope.usr = dataSnapshot.val();
});
});

Nested Service promise not resolved in then()

I got a service which first need to retrieve an accesstoken and then a User object. When this is done, further UI actions needs to be taken. The problem I have is that login().then() is invoked with a valid Session.accesstoken but a non-resolved promise for the Session.user. So I can't do any logic based on the user's info (e.g. role, etc.)
The following code I have:
In controllers.js (login() is invoked upon form credentials button click)
.controller('LoginController',
function($scope, SessionService) {
$scope.credentials = {
username : '',
password : ''
};
$scope.login =
function() {
SessionService.login($scope.credentials).then(function() {
console.info("succesfully logged in as user " + JSON.stringify(Session));
// further UI actions..
});
};
})
In services.js
.factory('SessionService',
function(User, Session, AuthorizationService) {
return {
login : function(credentials) {
return AuthorizationService.requestToken(credentials)
.then(function(token) {
console.info("Got token: " + token);
if (!!token) {
Session.create(token);
console.info("Fetching user...");
return User.get({
id : 'me'
});
} else {
throw (new Error("Could not log in"));
}
}).then(function(user) {
console.info("Got user: " + user);
if (!!user) {
Session.user = user;
} else {
throw (new Error("Could not fetch user"));
}
});
},
};
})
Here is the output of the console:
Got token: cf9c0eba82d872508f7dcc0b234f0d52 services.js:120
Fetching user...
services.js:124 Got user: [object Object]
services.js:132 succesfully logged in as user
{"token":"cf9c0eba82d772508f7dcc0b234f0d52","user":{"$promise":{},"$resolved":false}}
The User object is created by $resource and is not a promise; it is the actual object that will eventually be populated with the user info, but at this point of the code it contains nothing.
The solution is simple: from the first then do:
return User.get({...}).$promise;
The next then will be invoked with the user object as expected, but only when the data is actually fetched.

Angular rendering engine & Firebase profile retrieval

What I want:
firebase checks authentication of page load
firebase returns userID if logged in
my function returns the username associated with the user.Id
assign to the variable that represents the username
render!
All on page load
Current Behavior:
The following configuration retrieves the username but will only display the username once I click a login button I have made.For some reason even though I am currently logged in I must click the login button. I want a set up where if I am logged in the app will just know I am logged in from the start!
crossfitApp.controller('globalIdCtrl', ["$scope",'$q','defautProfileData','$timeout', function ($scope,$q,defautProfileData,$timeout) {
var dataRef = new Firebase("https://glowing-fire-5401.firebaseIO.com");
$scope.myFbvar =null;
$scope.authenticated={
currentUser: null,
avatarUrl: "",
emailAddress: "",
settings: "",
currentUserid: null,
};
function getProfile(userID,assignMe){
myprofile= new Firebase("https://glowing-fire-5401.firebaseio.com/profiles/"+userID+"/username");
myprofile.once('value', function(nameSnapshot) {
assignMe = nameSnapshot.val();
});
};
$scope.auth = new FirebaseSimpleLogin(dataRef, function(error, user) {
if (error) {
//Error
console.log ('error');
}
else if (user) {
//logged in
$timeout(function() {
getProfile(user.id,);
});
console.log('logged in');
$scope.authenticated.currentUserid = user.id ;
}
else {
// user is logged out
console.log('logged out');
$timeout(function() {
$scope.authenticated.currentUserid =null;
});
}
});
}]); //Global
In your else if( user ) logic, you forgot to put your scope var inside the $timeout, so it is being set properly, but Angular doesn't learn about it until the next time $apply is called (e.g. ng-click, ng-submit, etc).
Thus:
else if (user) {
//logged in
$timeout(function() {
getProfile(user.id,);
$scope.authenticated.currentUserid = user.id ; // moved into $timeout
});
console.log('logged in');
}
You can read more about why this matters here and here.

Resources