CreateUser Function Not Working in AngularFire - Firebase Simple Login? - firebase

I have the following controller that uses AngularFire
app.controller("authController", function($scope, $firebaseSimpleLogin){
var ref = new Firebase("https://myapp.firebaseIO.com/");
$scope.auth = $firebaseSimpleLogin(ref, function(error, user){
if(error){
console.log(error);
}
else if(user){
console.log(user);
}
else{
console.log("user logged out");
}
});
// This shows a valid object
console.log($scope.auth);
$scope.createAccount = function(){
console.log("found me");
$scope.auth.$createUser($scope.email, $scope.password, function(error, user){
console.log("something");
console.log(user);
if(!error){
console.log(user);
}
else{
console.log(error);
}
});
};
});
When I bind the $scope.createAccount function to an ng-click event and click on the bound button, console.log("found me") runs in the browser, but none of the other console.log commands in $scope.createAccount is shown.
The console.log($scope.auth) command I have before setting the $scope.createAccount function shows a valid object with the $createUser function defined.
I am not receiving any console errors when I run $scope.createAccount so I am assuming the call has been "successfully" made.
Why am I able to see the auth object, but not receiving a callback after calling $createUser?

This was happening because I was doing callbacks based on JavaScript notation instead of Angular notation. Since I was using the AngularFire methods (they have the same names as the vanilla JavaScript SDK methods, but with a $ sign in front of them), I needed to handle callbacks using Angular's $promise methodology.
I changed
$scope.auth.$createUser($scope.email, $scope.password, function(error, user){
// do things;
});
to
$scope.auth.$createUser($scope.email, $scope.password)
.then(function(user){
// do things if success
}, function(error){
// do things if failure
});
and the callback worked as expected.
There is an exception to the above with the vanilla JS firebaseSimpleLogin constructor vs Angular $firebaseSimpleLogin constructor. On the vanilla JS constructor, there are callbacks on the constructor that allows you to specify what your script should do when a user logs in / logs out. It follows the following format:
var auth = new firebaseSimpleLogin(ref, function(error, user){
if(error){
// do things if login failure
}
else if(user){
// do things when login succeeds
}
else{
// do things when user logs out
}
});
If you try to do the same with the Angular constructor like so:
$scope.auth = $firebaseSimpleLogin(ref)
.then(function(user){
// do things when login succeeds
}, function(error){
// do things when login fails
});
you'll receive an error. There are no callback methods with the Angular constructor. I am guessing this was done on purpose since with Angular, you have data binding and you can simply $watch $scope.auth.user for changes and perform operations in your app depending on the variable's state. When $scope.auth.user is set to null, the user is logged out. If the value is set to anything else than null, then the user is logged in.

Related

onAuthStateChanged is invoked twice while the function in which it resides is called only once

function signInWithGoogle(){
//console.log("clicked signInWithGoogle");
firebase.auth().onAuthStateChanged(function(user){
if(user){
alert("Already logged in");
}
else{
// var info=document.getElementById("info");
var provider=new firebase.auth.GoogleAuthProvider();
firebase.auth().signInWithPopup(provider).then(function(result){
if(result.credential){
var token=result.credential.accessToken;
console.log("token="+token);
}
var user=result.user;
console.log("user_provider="+user.displayName+" user_email="+user.email+" user_dp="+user.photoURL+" user_verification="+user.emailVerified+" uid="+user.uid);
}).catch(function(error){
var errorCode=error.code;
var errorMessage=error.message;
var email=error.email;
var credential=error.credential;
alert(errorMessage);
});
}
});
}
When a user signs in for the first time, the sign-in process goes well but after that an alert message("Already logged in") is shown, that indicates onAuthStateChanged is called once again.How to make it not call again?
I am sure that that signInWithGoogle() is invoked once.
P.S. I have seen many questions related to this but they don't have any concrete solution.
onAuthStateChanged is called first time when the user is not signed in, you signInWithPopup. This causes the user to sign in, triggering this same observer again and hence the alert message.
I agree with #camden_kid's assessment. But if you insist on adding this observer in a sign in function, you can unsubscribe when it is first called:
var unsubscribe = firebase.auth().onAuthStateChanged(function(user){
unsubscribe();
...
});
This would guarantee it is called once each time you call this function.

Meteor / Jasmine / Velocity : how to test a server method requiring logged in user?

Using velocity/jasmine, I'm a bit stuck on how I should test a server-side method requiring that there be a currently logged-in user. Is there a way to make Meteor think a user is logged in via stub/fake ?
myServerSideModel.doThisServerSideThing = function(){
var user = Meteor.user();
if(!user) throw new Meteor.Error('403', 'not-autorized');
}
Jasmine.onTest(function () {
describe("doThisServerSideThing", function(){
it('should only work if user is logged in', function(){
// this only works on the client :(
Meteor.loginWithPassword('user','pwd', function(err){
expect(err).toBeUndefined();
});
});
});
});
What you could do is add users just to your test suite. You could do this by populating these users in a the server-side test script:
Something like:
Jasmine.onTest(function () {
Meteor.startup(function() {
if (!Meteor.users.findOne({username:'test-user'})) {
Accounts.createUser
username: 'test-user'
... etc
Then, a good strategy could be to use the beforeAll in your test to login (this is client side):
Jasmine.onTest(function() {
beforeAll(function(done) {
Meteor.loginWithPassword('test-user','pwd', done);
}
}
This is assuming your test isn't logged in yet. You can make this more fancy by checking for Meteor.user() and properly logging out in an afterAll, etc. Note how you can handily pass the done callback to many of the Accounts functions.
Essentially, you don't have to mock a user. Just make sure you have the right users, with the correct roles, available in the Velocity/Jasmine DB.
Lets say you have a server side method like this:
Meteor.methods({
serverMethod: function(){
// check if user logged in
if(!this.userId) throw new Meteor.Error('not-authenticated', 'You must be logged in to do this!')
// more stuff if user is logged in...
// ....
return 'some result';
}
});
You do not need to make a Meteor.loginWithPassword before executing the method. All you got to do is stub the this.userId by changing the this context of the method function call.
All defined meteor methods are available on the Meteor.methodMap object. So just call the function with a different this context
describe('Method: serverMethod', function(){
it('should error if not authenticated', function(){
var thisContext = {userId: null};
expect(Meteor.methodMap.serverMethod.call(thisContext).toThrow();
});
it('should return a result if authenticated', function(){
var thisContext = {userId: 1};
var result = Meteor.methodMap.serverMethod.call(thisContext);
expect(result).toEqual('some result');
});
});
EDIT: This solution was only tested on Meteor <= 1.0.x
What are you testing and why does it require a user to be logged in? Most of the methods I have that need a user object I pass the user object into. This allows me to call from a test without actually being logged in. So in the actual running of the code I would pass...
var r = myMethod(Meteor.user());
but when running from the test I would call like...
it('should be truthy', function () {
var r = myMethod({_id: '1', username: 'testUser', ...});
expect(r).toBeTruthy();
});
I think that Meteor.server.method_handlers["nameOfMyMethod"] allows you to call/apply a Meteor method and supply this as the first parameter at least in the current version (1.3.3)
this.userId = userId;
Meteor.server.method_handlers["cart/addToCart"].apply(this, arguments);

How to validate data in onCreateUser without losing the form data?

I'm writing an Appliction using Meteor. In this App I want to implement a server-side validation of the user data using Accounts.onCreateUser. There is some data passed which can only be verified on the server side.
At client side I call:
Template.register.events({
'submit form': function (e) {
e.preventDefault();
var attributes = {
username: $("#inputUsername").val(),
password: $("#inputPassword").val(),
confirmation: $("inputConfirmation").val(),
email: $("#inputEmail").val(),
...
};
Accounts.createUser(attributes, function(err){
if (err) {
throwError(err);
} else {
}
});
}
});
And on the server side:
Accounts.onCreateUser(function(options, user) {
if(!verifyData(options))
throw new Meteor.Error(403, "Wrong input");
return user;
});
After the server side verification fails, all form data is lost. What is the best way to keep the data?
I went ahead and reproduced your code on a Meteorpad and from what I can tell, the form data does still persist. You just need to access it via the attributes variable in the client-side.
There may be something I am missing, but i took what you posted above and put it in there.

Extending the Meteor loginWithPassword method

I may be totally off line here, but I'm trying to extend the loginWithPassword method in Meteor to handle only returning users with a few parameters set in their profile.
I'm creating the users fine and once created they login as that user type and all is good, but, when I try and login again I hit a wall.
I've tried implementing my own login handler as follows...
Accounts.registerLoginHandler(function(loginRequest) {
console.log("Got to Accounts.registerLoginHandler");
console.log(loginRequest);
var userId = null;
var user = Meteor.loginWithPassword(loginRequest.email, loginRequest.password, function(error){
if(error !== undefined){
setAlert('error', 'Error in processing login. ' + error.reason + '.');
}
});
var userWithType;
if(user){ // we have the right username and password
console.log("Found a user and logged them in");
userWithType = Meteor.users.findOne({'id': user._id, 'profile.type': loginRequest.type});
}
if(userWithType){
console.log("Found User of that type")
userId = user._id;
}
console.log("UserId", userId);
return {
id: userId
}
});
But am getting an error when I get to this code that says
Got to Accounts.registerLoginHandler
{ email: 'blah2#blah', password: 'blha', type: 'user' }
Exception while invoking method 'login' TypeError: Object #<Object> has no method 'loginWithPassword'
at app/server/login.js:8:23
at tryAllLoginHandlers (app/packages/accounts-base/accounts_server.js:53:18)
at Meteor.methods.login (app/packages/accounts-base/accounts_server.js:73:18)
at maybeAuditArgumentChecks (app/packages/livedata/livedata_server.js:1367:12)
at _.extend.protocol_handlers.method.exception (app/packages/livedata/livedata_server.js:596:20)
at _.extend.withValue (app/packages/meteor/dynamics_nodejs.js:31:17)
at app/packages/livedata/livedata_server.js:595:44
at _.extend.withValue (app/packages/meteor/dynamics_nodejs.js:31:17)
at _.extend.protocol_handlers.method (app/packages/livedata/livedata_server.js:594:48)
at _.extend.processMessage.processNext (app/packages/livedata/livedata_server.js:488:43)
I'm obviously missing a this pointer or something like that, but don't know enough about this framework to know if I'm totally off track here even trying to get this to work.
Ta
P.
I am not too familiar with it but from http://docs.meteor.com, Meteor.loginWithPassword () can only be called on the client. You have written it into the server side code from the tutorial.
That is throwing the error you see. If you move it to the client you will also see that it only returns to the callback function so your variable user will remain undefined.
Meteor.user().profile is available on the client so you can just check the type there in the callback of loginWithPassword to check the information at login.

Meteor accounts framework signed/logged in event

I'm using the accounts-ui package and would like to process some javascript as soon as the user is logged in/and or registered.
Is there an event that gets called as soon as the user signs in?
The raw login API (eg loginWithFacebook, loginWithPassword, etc) has a callback that fires when login is complete, but this is not currently exposed through accounts-ui. This may change.
A potential workaround, inspired by Werner's suggestion, but also taking page load into account:
var oldUserId = undefined;
Meteor.autorun(function() {
var newUserId = Meteor.userId();
if (oldUserId === null && newUserId) {
console.log('The user logged in');
} else if (newUserId === null && oldUserId) {
console.log('The user logged out');
}
oldUserId = Meteor.userId();
});
You could check the result of the Meteor.userId() function inside Meteor.autorun():
Meteor.autorun(function() {
if (Meteor.userId()) {
console.log('The user logged in');
}
});
Just to give an alternative; I monkey patched the callback function. It looks a bit more complex because the credentialRequestCompleteHandler requires a function that returns a function but besides that its a plain monkey patch. Stick this in main.js or something that gets processed late and only once. I hope it helps for future reference.
var orgCallback = Accounts.oauth.credentialRequestCompleteHandler;
Accounts.oauth.credentialRequestCompleteHandler = function(callback){
return function (credentialTokenOrError) {
var tmpFunc = orgCallback(callback);
tmpFunc(credentialTokenOrError);
alert("do your own thing here");
}
}

Resources