Angularfire 2.1 - How to access auto-generated ID for users (or how to make my UID the first node for each user) - firebase

Disclaimer, I am trying to self-teach myself development. I am building a hybrid mobile app using Ionic 1 and now Firebase 3 for my database and authentication.
For my scenario, in short, I'm trying to display a list of 'friends' for the user that is currently logged in. Here is the current data structure I have (the relevant part anyway):
Data Structure
I have a line of code that does return me what I want:
var friends = $firebaseArray(ref.child('users').child('-KXcxMXkKs46Xv4-JUgW').child('friends'));
Of course, that can't work because there is a nice little hard coded value in there.
So, I looked into how to retrieve the current UID so I could replace the hard coded value. But after running the following bit of code through, the first node under user is not the UID (it is some other auto generated value that I don't really know how it got there). The UID is actually within the id field.
var ref = firebase.database().ref();
authObj = $firebaseAuth();
var firebaseUser = authObj.$getAuth();
console.log(firebaseUser.uid);
So, ultimately what I would love is to be able to change the data structure so that the UID is the first node under Users, but I can't seem to find documentation to do that. I looked at this other stack thread, but it is for an outdated version and I can't seem to connect the dots. Other thread
Though, if I can't change the structure, I still need to figure out how to access that friends node for the current user, one way or another.
Thank you in advance. This is my first stackoverflow post, so be gentle.
Update:
Per Frank's comment, this is the code that I execute to create users - $add is what is creating the push id (-KXcxM...).
createProfile: function(uid, user) {
var profile = {
id: uid,
email: user.email,
registered_in: Date()
// a number of other things
};
var messagesRef = $firebaseArray(firebase.database().ref().child("users"));
messagesRef.$add(profile);
},
register: function(user) {
return auth.$createUserWithEmailAndPassword(user.email, user.password)
.then(function(firebaseUser) {
console.log("User created with uid: " + firebaseUser.uid);
Auth.createProfile(firebaseUser.uid, user);
Utils.alertshow("Success!","Your user has been registered.");
})
.catch(function(error) {
Utils.alertshow("Error.","Some helpful error message.");
console.log("Error: " + error);
});
}

Instead of creating a $firebaseArray and calling $add on it, you can just store the user using the regular Firebase JavaScript SDK:
createProfile: function(uid, user) {
var profile = {
id: uid,
email: user.email
};
firebase.database().ref().child("users").child(uid).set(profile);
}
Since AngularFire is built on top of the Firebase JavaScript SDK, the two interact nicely with each other. So if you have any existing $firebaseArray on users it will pick up the new profile too.

Related

What is the correct way to use next-auth with Google OAuth and users stored in the database?

I'm new to next-auth, and I'm looking for some help.
I have added the Google OAuth provider, and now when I run signIn("google") function on the frontend, it automatically takes me to the google's login page, and logs me in, somehow, without ever touching my database.
When the google authentication is complete, I need to be able to create a new user in my database, or retrieve the existing one if they have already signed up before (because I need to store all kinds of custom information about the user, not just their email).
And I want to make user's information available on the session object from useSession()hook. Right now I'm seeing some kind of default user info (with name, email, and image field which I didn't define).
When I was using a regular Express server and Passport, the code looked kinda like this:
const googleAuth = new GoogleStrategy(
{
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: "/api/v1/profiles/google/callback",
},
async (req, accessToken, refreshToken, profile, done) => {
const existingProfile = await Profile.findOne({ email: profile.emails[0].value })
/* Found user, return him and move along. */
if (existingProfile) return done(null, existingProfile)
/* Haven't found profile with this googleId, create a new one. */
const newProfile = await new Profile({
googleId: profile.id,
email: profile.emails[0].value,
})
newProfile.save()
done(null, newProfile)
}
)
So I would still be creating the users in my database, and retrieving their information on log in, so that I could send it to the client.
Where does this kind of code supposed to go when I'm using the serverless next-auth?
And a second, but kind of related question - what's that default user object that gets provided to me in the session object? The one with the name, email, and image fields that next-auth seems to create for me? How can I make it use the user object I'm returning from my database instead?
(I've done my best to look through the tutorials and examples, but couldn't find one that explains this clearly.)
I don't know if you still need this, but I hope it helps someone:
Oauth kinda mixes up Sign In and Sign Up, so if you want to have Google authentication what you probably want to do is create a callback of the Sign In function in /api/auth/[...nextauth].js, then get the account and profile as parameters and access to its provider.
async signIn({account, profile}) {
if(account.provider === 'google') {
//check if user is in your database
if(user NOT in DB) {
//add your user in DB here with profile data (profile.email, profile.name)
}
return true
}
You always want to return true since you always want to log in independently if it is in your DB or not.
Regarding the session object, you can also add a callback and access to the default session (that you can modify), token and user. Here you can retrieve all information you want from your database, add it to the session object and return it.
async session({ session, token, user }) {
const newData = DB.find(...).data
session.newfield = newInfo
return session
}

How to determine whether it's a SignUp or SignIn in Passwordless Auth from Firebase? [duplicate]

My use case is that I want to ask newly signed up users to enrich basic info like their names.
So I was hoping to do it like:
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
// User is signed in.
if (some indicator tells me it is newly signed up user)
{redirect to a form to fill in more info}
} else {
// No user is signed in.
}
});
I checked the doc, and could not find anything related to this...
Thanks for the help in advance.
Since version 4.6.0: https://firebase.google.com/support/release-notes/js#4.6.0
You can get if a user is new or existing in 2 ways:
If you are getting back a UserCredential result, check result.additionalUserInfo.isNewUser
Check firebase.auth().currentUser.metadata.creationTime === firebase.auth().currentUser.metadata.lastSignInTime
Previously you had to do that on your own and keep track of the user using Firebase Realtime Database. When a user signs in, you check if a user with the specified uid exists in the database or not. If the user was not found, it is a new user, you can then add the user to the database. If the user is already in the database then this is a returning existing user. Here is an example in iOS.
Handing Firebase + Facebook login process
Example for using result.additionalUserInfo.isNewUser:
firebase.auth().signInWithPopup(provider).then((result) => {
console.log(result.additionalUserInfo.isNewUser);
});
One thing you can do is do things in the callback function of the signup function, the signup function do return a promise. You can do something like this:
firebase.auth().createUserWithEmailAndPassword(email, password)
.then(function(user) {
//I believe the user variable here is the same as firebase.auth().currentUser
//take the user to some form you want them to fill
})
.catch(function(error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
// ...
});
However, I don't really recommend doing it this way because the client side code can be unreliable. Think about what if a user suddenly disconnect before they can fill the form. Their data will be incomplete in your database. So if you do it this way, do set a flag in your user's profile when they submit the form so that you know who filled detailed information and who didn't.
Another better way to do this is using firebase cloud functions. You can have code like this in your cloud functions. Cloud functions are written in node.js so you don't need to spend time on another language.
exports.someoneSignedUp = functions.auth.user().onCreate(event => {
// you can send them a cloud function to lead them to the detail information form
//or you can send them an welcome email which will also lead them to where you want them to fill detailed information
});
This way is much better because you can safely assume that your cloud functions server will never be down or compromised. For more information about cloud functions you can refer to their doc: https://firebase.google.com/docs/functions/auth-events
You can check the sign-in methods the user has (if any). If there are none, it is a new user.
// Fetch sign in methods (if any)
Auth.auth().fetchSignInMethods(forEmail: userEmail!) { [self] signInMethodsArray, error in
// Check for error and alert user accordingly
if let error = error {
// handle errors
}
// Email accepted.
// Check if new or returning user.
else {
if (signInMethodsArray == nil) {
// New User
}
else {
// Returning User
}
}
}
This is Swift (iOS) code, but the concept is the same across languages.

Flutter - How to add Firebase-Auth user credentials to new records (FireStore documents)?

I'm trying to create a simple Crud app with Flutter and Firebase which the records (documents created in FireStore) are related to the user who has been Authenticated. Therefore the Crud functions will only be performed by the user who created the record. IE a user will only be able able to edit/update/delete the records they added in the first place.
I have the firebase_auth and crud functions working nicely with firestore. the issues i'm have is with relating the two. I have chosen to use the users email and the unique identifier (i'm not sure if it's better to use the auto generated user id or not). I have created a separate function for simply returning the current user's email as it's being added to the firestore document. The problem is the first time i add a record the user email returns null, If i submit the form again it starts working fine.
String _userEmail;
_getUserAuthEmail() {
FirebaseAuth.instance.currentUser().then((user){
setState((){this._userEmail = user.email;});
});
return this._userEmail;
}
Which is being called from the onPressed event
onPressed: () {
crudObj.addData({
'itemName': this.itemName,
'userEmail': _getUserAuthEmail(),
}).then((result) {
dialogTrigger(context);
}).catchError((e) {
print(e);
});
},
As i'm just starting out please let me know if there is a better approach. Cheers.
You are getting null because you are not waiting for the currentUser method to settle. Change the _getUserEmail method like this:
String _userEmail;
_getUserAuthEmail() async {
FirebaseUser user = await FirebaseAuth.instance.currentUser();
setState(() {
_userEmail = user.email;
});
return this._userEmail;
}
Also, about this
"I have chosen to use the users email and the unique identifier (i'm not sure if it's better to use the auto generated user id or not)."
I suggest you using the user's uid for saving user related stuff.

Firebase Auth, how to know new user signed up, rather than existing user sign in?

My use case is that I want to ask newly signed up users to enrich basic info like their names.
So I was hoping to do it like:
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
// User is signed in.
if (some indicator tells me it is newly signed up user)
{redirect to a form to fill in more info}
} else {
// No user is signed in.
}
});
I checked the doc, and could not find anything related to this...
Thanks for the help in advance.
Since version 4.6.0: https://firebase.google.com/support/release-notes/js#4.6.0
You can get if a user is new or existing in 2 ways:
If you are getting back a UserCredential result, check result.additionalUserInfo.isNewUser
Check firebase.auth().currentUser.metadata.creationTime === firebase.auth().currentUser.metadata.lastSignInTime
Previously you had to do that on your own and keep track of the user using Firebase Realtime Database. When a user signs in, you check if a user with the specified uid exists in the database or not. If the user was not found, it is a new user, you can then add the user to the database. If the user is already in the database then this is a returning existing user. Here is an example in iOS.
Handing Firebase + Facebook login process
Example for using result.additionalUserInfo.isNewUser:
firebase.auth().signInWithPopup(provider).then((result) => {
console.log(result.additionalUserInfo.isNewUser);
});
One thing you can do is do things in the callback function of the signup function, the signup function do return a promise. You can do something like this:
firebase.auth().createUserWithEmailAndPassword(email, password)
.then(function(user) {
//I believe the user variable here is the same as firebase.auth().currentUser
//take the user to some form you want them to fill
})
.catch(function(error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
// ...
});
However, I don't really recommend doing it this way because the client side code can be unreliable. Think about what if a user suddenly disconnect before they can fill the form. Their data will be incomplete in your database. So if you do it this way, do set a flag in your user's profile when they submit the form so that you know who filled detailed information and who didn't.
Another better way to do this is using firebase cloud functions. You can have code like this in your cloud functions. Cloud functions are written in node.js so you don't need to spend time on another language.
exports.someoneSignedUp = functions.auth.user().onCreate(event => {
// you can send them a cloud function to lead them to the detail information form
//or you can send them an welcome email which will also lead them to where you want them to fill detailed information
});
This way is much better because you can safely assume that your cloud functions server will never be down or compromised. For more information about cloud functions you can refer to their doc: https://firebase.google.com/docs/functions/auth-events
You can check the sign-in methods the user has (if any). If there are none, it is a new user.
// Fetch sign in methods (if any)
Auth.auth().fetchSignInMethods(forEmail: userEmail!) { [self] signInMethodsArray, error in
// Check for error and alert user accordingly
if let error = error {
// handle errors
}
// Email accepted.
// Check if new or returning user.
else {
if (signInMethodsArray == nil) {
// New User
}
else {
// Returning User
}
}
}
This is Swift (iOS) code, but the concept is the same across languages.

Meteor user name based on userId

I am saving logged in userId with each record saved in my Meteor app collection as shown in the example below, yet I was wondering if there was any way in Meteor where I can retrieve user name based on the user saved id without have to make another query on the users collection? In Node.js / mongoose there was this Populate function, but I can't seem to find similar package / function in Meteor. So I was wondering if someone can help me by suggesting a resolution to this problem (if any). thanks
var newInvoice = {
customerid: $(e.target).find('[name=customer]').val(),
userid: Meteor.userId(),
//....more fields here
}
Meteor.call('saveInvoice', newInvoice, function(error, id){
if(error)
return alert(error.reason);
});

Resources