AngularFire - Adding user info to the database after signing in with popup - firebase

I was wondering is there any way to store a user info only when he calls $scope.authObj.$signInWithPopup('google'); for the first time ( only once per user ).
Currently every time when "user" clicks on the button to get signed in, his data is stored to the database.
I've tried something like this:
$scope.googleSignIn = function(){
$scope.authObj.$signInWithPopup("google").then(function(data){
$scope.authObj.$onAuthStateChanged(function(user){
for(var prop in firebasedb.users){
if(firebasedb.users[prop].id === data.uid){
console.log('same')
break;
} else {
firebasedb.users.$add({
name: 'Default Name',
id: user.uid
});
}
}
}); // End auth state change
});// End sign in
};// End googleSignIn
This current code prevents adding new user because it is obvious that id's are the same.
I hope I explained it properly.
Thanks in advance

Related

React Native Firebase write user data after account creation

I'm currently working with Firebase and React Native I have come to a slight problem.
Reading the documentation for the: createUserWithEmailAndPassword:
On successful creation of the user account, this user will also be signed in to your application.
I was trying to work with the promise as follows:
firebase.auth().createUserWithEmailAndPassword(this.state.email, this.state.password)
.then(function(authData){
this.writeUserData(authData.uid);
}).catch(function(error){
console.warn(error)
});
And my writeUserData() method:
writeUserData(the_uid) {
console.warn("Writting data ")
var today = new Date();
firebase.database().ref('users/' + authData.uid + '/').set({
name: this.state.name,
birthday: this.state.date,
email: this.state.email,
registered: today
});
}
In the function however the console.warn is never fired hence the event isn't fired since the user is automatically logged in.
Problem: The .then isn't executed since user is logged in straight away.
Goal: Be able to add the user information (name, birthday, etc) after user creation before sign in.
Okay understood you, the problem seems to be how you define a function in your .then statement. You should be using an Arrow function, like so -
firebase.auth().createUserWithEmailAndPassword(this.state.email, this.state.password)
.then((authData) => {
this.writeUserData(authData.uid);
}).catch(function(error){
console.warn(error)
});
what i have understand is that you are trying to push (name ,birthday , etc ) to firebase , well with my little experience using firebase , you have to call {firebase.database().ref().child('your path')} to create a path first , then use the push() , exemple :
{ var ref = firebaseRef.database().ref().child('Clients');
var ref = ref.push(this.state.username); }
well i m newbie , but this worked for me , hope it help you

Meteor Routes & Sessions -> How to coordinate

Meteor Question: what is the best way to implement real sessions?
I have a normal login page with statistics. No issues there.
Now, I want people to be able to check-in with specific urls; assume they later
hit a url like http://localhost:3000/checkin/area1
How can I coordinate that with their login?
I have a route for the checkin:
Router.route('/checkin/:_id', function () {
var req = this.request;
var res = this.response;
this.userId = 'steve'; //TODO: Need way to pull userId
if (!this.userId) {
res.end('Please login first');
} else {
//Verify correct area
//Verify that haven't check before
var lastCheckin = checkins.find({ user: this.userId, visited: this.params._id });
if (lastCheckin.count() == 0) {
//we haven't checkedin yet
checkins.insert({ user: this.userId, visited: this.params._id, createdAt: new Date() })
res.end('Checkin '+this.userId+' for '+this.params._id);
} else {
console.log('already checked in '+lastCheckin.createdAt);
res.end(this.userId+' already visited '+this.params._id);
}
}
}, {where: 'server'});
Things I've tried:
Persistent session: But that doesn't work because request is not coming from the main page, so no session variable to pull.
Pull cookie in Request (since Persistent session seems to have 1). The original login doesn't appear to have the request object, so I don't know where to get that.
Others (diff situations) have shown a Meteor.user() inside a route, but the software complains that it can only be used inside a method. What can be used inside a route?
What else can I try?
Thanks for you help.
Ok, I am not sure if this will help exactly but.
If you are on /myPage, then you have an event that runs:
Router.go('/yourPage');
Session.set('yourVariable', "yourValue");
You will be able to access the session variable (yourVariable) in /yourPage (and yourPage's code).

Reactive subscription on user collection

I am trying to subscribe to profdle information of a different user than the logged in user, but I am facing issues as mentioned below
I am using angular-material and my code looks like below:
//publish user info upon following user
Meteor.publish("getUserInfo", function (userId) {
return (Meteor.users.find({_id: userId}, {fields: {profile: 1}}));
});
//subscribe
$scope.$meteorSubscribe("getUserInfo", askLikeController.$root.askLike[0].userId).then(function (subscriptionHandle) {
//Second element in the userProfile array will have the profile of required user
askLikeController.$root.usersProfile = $meteor.collection(Meteor.users, false);
});
Issues:
1. In the variable askLikeController.$root.usersProfile, I am getting both the loggedIn user and the desired userinfo having userId, I was expecting userinfo of only desired userId, why is this?
2. The subscription "getUserInfo" is not reactive, and even the subscription is lost after processing few blocks of code and then in the askLikeController.$root.usersProfile I am left with only user profile of logged in user, my guess is that my subscription is being replaced by inbuilt Meteor subscription for user.
How do I solve the issues?
Regards,
Chidan
First, make sure you have removed autopublish:
> meteor remove autopublish
To get reactivity in angular-meteor you need $meteor.autorun and $scope.getReactively. Here's an example:
// we need the requested id in a scope variable
// anytime the scope var changes, $scope.getReactively will
// ... react!
$scope.reqId = askLikeController.$root.askLike[0].userId;
$meteor.autorun($scope, function() {
$scope.$meteorSubscribe('getUserInfo', $scope.getReactively('reqId')));
}).then(function(){
askLikeController.$root.usersProfile = $meteor.collection(Meteor.users, false);
})
Getting only the user you selected: NOTICE- the logged in users is always published. So you need to specify which user you want to look at on the client side, just like you did on the publish method. So, in the subscribe method:
askLikeController.$root.usersProfile = $meteor.collection(function() {
return Meteor.Users.find({_id: $scope.getReactively('reqId')})
}, false);
At this point you might be better off changing it to an object rather than a collection:
askLikeController.$root.usersProfile = $scope.$meteorObject(Meteor.Users, {_id: $scope.getReactively('reqId')});

Publication of items where User is in group (Alanning Roles and Publications)

I am using Alanning Roles to maintain a set of groups/roles for the users of my application. When a user creates an "Application", I generate a new role for them as the app_name + UUID, then add that as a group with the roles of Admin to the user that created it. I can then use the combination of the generated group name plus either the Admin or Viewer roles to determine which Applications the user has rights to see and/or edit.
The issue that I am having is that I can't figure out a good way to get the publication to only publish the things the user should see. I know that, by default at least, publications are not "reactive" in the way the client is, and they they are only reactive for the cursors they return. But, in my code I create the group/role first, add it to the user, then save the "Application", which I thought would rerun my publication, but it did not:
Meteor.publish('myApplications', function(groups) {
if (this.userId) {
console.log('Running myApplications publication...');
console.log('Found roles for user ' + this.userId + ': ', Roles.getGroupsForUser(this.userId));
return Applications.find({group: {$in: Roles.getGroupsForUser(this.userId)}});
} else {
//console.log("Skipping null user");
return null;
}
});
But, contrary to what I thought would happen (the whole publication method would re-run), I am guessing what really happens is that only the Cursor is updates. So for my next attempt, I added the mrt:reactive-publications package and simply got a cursor to the Meteor.users collection for the user, thinking that would "trigger" the publication to re-run when the user gets updated with the new group/role, but that didn't work.
I have this finally working by simply passing in the groups for the user:
Meteor.publish('myApplications', function(groups) {
if (this.userId) {
if (!groups || groups.length === 0) {
groups = Roles.getGroupsForUser(this.userId);
}
console.log('Running myApplications publication...');
console.log('Found roles for user ' + this.userId + ': ', Roles.getGroupsForUser(this.userId));
return Applications.find({group: {$in: groups}});
} else {
//console.log("Skipping null user");
return null;
}
});
And then I just call the publication like Meteor.subscribe('myApplications', Roles.getGroupsForUser(Meteor.userId())) in my route's waitOn, but this would mean that any client could call the same publication and pass in any groups they like, and potentially see documents they were not intended to see. That seems like a pretty large security flaw.
Is there a better way to implement this such that the client would not be able to coax their way to seeing stuff not theirs? I think the only real way would be to gather the groups on the publication side, but then it breaks the reactivity.
After sifting through a bunch of docs and a few very helpful stack posts, this is the alternative I came up with. Works like a charm!
My objective was to publish 'guest' users' info to the group admins for approval/denial of enhanced permissions.
Meteor.publish('groupAdmin', function(groupId) {
// only publish guest users info to group admins
if(Roles.userIsInRole(this.userId, ['group-admin'], groupId)) {
// I can't explain it but it works!
var obj = {key: {$in: ['guest']}};
var query = {};
var key = ('roles.' + groupId);
query[key] = {$in: ['guest']};
return Meteor.users.find(query, {
fields: {
createdAt: 1,
profile: 1
}
});
} else {
this.stop();
return;
}
});
Reference: How to set mongo field from variable
& How do I use a variable as a field name in a Mongo query in Meteor?

Prevent items in scope from writing to a different user's records

I was having success with using AngularFire in a scenario where there is one user on my application.
Now that I have authentication up and running, I'm noticing that assigning items to $scope.items is catastrophic when switching users, mainly due to the $scope failing to update correctly.
Reading directly from the docs...
var ref = new Firebase('https://<my-firebase>.firebaseio.com/items');
angularFire(ref, $scope, 'items');
I need these to be only the items of the currently authorized user. So currently, I do this (if there's a better way, don't hesitate to tell me!)
var ref = new Firebase('https://<my-firebase>.firebaseio.com/items/userId');
angularFire(ref, $scope, 'items');
I generate userId using auth.provider and auth.id, btw. Now that my items are namespaced in (let's say) user1
var ref = new Firebase('https://<my-firebase>.firebaseio.com/items/[user1id]');
angularFire(ref, $scope, 'items');
I add items to $scope.items
$scope.create = function(item) {
$scope.items.push(item)
/* Pretend the user adds these from the interface.
[
{ name: 'eenie' },
{ name: 'meenie' },
{ name: 'miney' },
{ name: 'moe' }
]
*/
}
The problem
Now if I just log out and login as someone else, magically that user has eenie meenie miney and moe because $scope.items held the array between logout and login.
I tried to set $scope.items = [] on logout event, but that actually empties all the records. I'm pulling my hair out. This is 0.001% of what I need to do in my project and it's taking my whole weekend.
Update New method
$scope.create = function() {
$scope.selectedDevice = {
name: 'New Device',
userId: $scope.user.provider + $scope.user.id
};
return $scope.devices.push($scope.selectedDevice);
};
$scope.$on('angularFireAuth:login', function(evt, user) {
var promise, ref;
ref = new Firebase('https://mysite.firebaseio.com/users/' + (user.provider + user.id) + '/registry/');
promise = angularFire(ref, $scope, 'devices');
});
It now will accurately create items under the user's id. However, still, once you logout and log back in, those items do not get cleared from $scope.devices. Therefore, they just add themselves to data but under the newly logged in user.
Update
I did a lot of trial and error. I probably set $scope.devices to [] and moved around login events in every possible combination. What eventually worked was #hiattp's fiddle in the accepted answer.
This is a result of the implicit data binding remaining intact as you switch users. If the new user shows up and creates a new binding, it will consider the existing data to be local changes that it should assimilate (that's why you see the original user's items being added to the new user), but if you try to clear them first without releasing the binding then you are implicitly telling Firebase to delete that data from the original user's item list (also not what you want). So you need to release the data bindings when you detect the logout (or login) events as needed.
The callback in the angularFire promise provides an "unbind" method (see here and here):
var promise = angularFire(ref, $scope, 'items');
promise.then(function(unbind){
// Calling unbind() will disassociate $scope.items from Firebase
// and generally it's useful to add unbind to the $scope for future use.
});
You have a few idiosyncrasies in your code that are likely causing it not to work, and remember that unbind won't clear the local collection for you. But just so you have an idea of how it should work (and to prove it does work) here is a fiddle.
You need to unbind $scope.items on logout. The best way to do this will be to save the unbind function given to your promise in $scope:
var ref = new Firebase('https://<my-firebase>.firebaseio.com/items/[user1id]');
angularFire(ref, $scope, 'items').then(function(unbind) {
$scope.unbindItems = unbind;
});
$scope.$on('angularFireAuth:logout', function() {
$scope.unbindItems();
});

Resources