firebase add user information while creating user (react.js) - javascript

I researched before asking this question but I couldn't find an answer.
I made a form in react js. When I click the button it creates a user with createUserWithEmailAndPassword() method. There is no problem with creating the user. I want to add extra information like; username : "Usernameeeee" to my database. The database will look like this:
users {
userUid1: {name: "Name User 1"}
userUid2: {name: "Name User 2"}
....
}
When I use createUserWithEmailAndPassword(), it creates a user and logs them in. But as you can see the above, I need the current user's id. I know how to get it normally, firebase.auth().currenUser... But while creating a user I can't catch the user's id. It returns null;
export function signUpUser({email, password}) {
return function (dispatch){
firebase.auth().createUserWithEmailAndPassword(email, password)
.then(
() => dispatch({type: USER_SIGNUP_SUCCESS})
)
.then(console.log(firebase.auth().currentUser))
.catch(error => {
dispatch({type: USER_SIGNUP_FAIL, payload: error.message});
});
}
}
When I console the current user, the creating user is complete but not logged in yet. So how can I add information while creating a user ? (not after logged in some other way)

I found the answer after a day. I hope this will be helpful to others. As you understand, the main problem is getting the user id right after creating a user, before user sign in...
So the solution is:
export function signUpUser({email, password, username}) {
const db = firebase.database();
return function (dispatch){
firebase.auth().createUserWithEmailAndPassword(email, password)
// here we get user id if creating is successful.
.then(function(user){
dispatch({type: USER_SIGNUP_SUCCESS}); // I dispatched some message.
db.ref(`users/${user.uid}`).set({name: username}); // I added user
console.log('uid:',user.uid)
})

Related

Getting userID's through their username. Discord.JS

client.on("message", async message =>{
message.guild.members.fetch({query: "username here", limit: 1})
.then( members => console.log(members.user.id)) //results in undefined
.catch(console.error);
}
I would like to be able to get a users ID through their username. In this case I use the "fetch()" method and a query within it. However when i try to log (members.user.id) I get "undefined". The thing is whenever i fetch by a user ID(see code below) I am able to access the users ID,username, etc.. How would I able to fetch by their username and then be able to get a users ID. I was using this as a reference point https://discord.js.org/#/docs/main/master/class/GuildMemberManager?scrollTo=fetch
If theirs an easier way to get a users ID by their username please do tell.
client.on("message", async message =>{
message.guild.members.fetch("userID here")
.then( members => console.log(members.user.username))// a username is logged
.catch(console.error);
}
Looks like even if the limit is 1 it will still give back a collection, so you will need to get the first member of that collection
message.guild.members.fetch({query: "username here", limit: 1})
.then(members => {
const member = members.first();
//no need to convert it into a user
console.log(member.id);
});

Firebase Auth Login not showing logged in user name

I'm using firebase web authentication to log users into my website which works perfectly fine - but I want to be able to show the name of the user logged in. How do I do that?
When a user signs up, it will be written with some basic fields (email, uniqueID, etc) to the Firebase Authentication DB (which you have access to, but you don't have full control of, since it's controlled by the Firebase Auth system).
Once your brand new user is created, you'll get a UserCredential object with basic information of your user. Use the uniqueID field (UserCredential.user.uid) to write your user details with as many fields as you want (username, email, fullName, address, preferences, etc) to your Firebase database (realtime DB or cloud Firestore). I think it's a good practice to store under a document with the uniqueID created by the Auth system.
Once you've done that, and that users logins in at a later time, you'll get another userCredential object that contains the uniqueID for that user.
You'll use that uniqueID to access your database where you stored your users details and you will be able to retrieve all the data that you want.
See the code below (I'm using React): uppon a successful signUp, we get a UserCredential object, which we can use to read the uniqueId uid that it was just created, and use that to create a document in our database with that same id. We can add as many fields as we want. In this case, I added username (which I'm getting from my signup form) and email (which I'm getting directly from the UserCredential object).
UserCredential docs
Create User with Password and Email docs
handleSubmit(event){
event.preventDefault();
const { email, passwordOne} = this.state;
// SIGN UP FUNCTION
this.props.firebase.auth.createUserWithEmailAndPassword(email, passwordOne)
// SAVING USER DETAILS TO MY DATABASE
.then((UserCredential) => {
const username = this.state.username;
this.props.firebase.db.collection('users').doc(UserCredential.user.uid)
.set({
username: username,
email: UserCredential.user.email
})
.then( () => {
this.setState({ ...INITIAL_STATE });
this.props.history.push('/');
}
)
.catch(error => {
this.setState({ error });
}
);
}
)
.catch(error => {
this.setState({ error });
}
);
}

Firebase - keep user signed in while localStorage object is valid

I have an app I'm building with Ionic, Angular2, and Firebase.
What I'm currently doing is that when a user log's in or registers, their auth.currentUser information is being saved in localStorage. My code is currently assuming that if a user has the user variable in localStorage set that the user is also signed in. However, I just realized that that isn't actually the case.
My localStorage variable user looks like this:
and I'm getting it like this:
ngOnInit() {
Promise.all([ //data storage get all..
this.storage.get('user')
])
.then(([user]) => {
this.userData = user; //set the user storage to this.userData
console.log(this.userData)
})
.catch(error => {
console.log("Storage Error.");
});
}
So I have all of the users information, except the fact that they aren't actually signed in... So when I do:
this.afAuth.auth.onAuthStateChanged((user) => {
if (user) {
console.log(user)
} else {
console.log("Not logged in")
}
});
It shows the user isn't logged in. Is it possible for me to keep the user logged in while I have the localStorage information?
Or maybe just have the login state always be logged in unless the user signs out? With it never expiring?
What should I do at this point? Any advice would be great! Thank you.
Firebase manger user in a different way. You should not depend on localstorage to detect if user is logged it. You should depend on firebase to check if user is logged in. Since firebase is built on web sockets, it should be really quick. So let rewrite the ngOnInit as follows:
ngOnInit() {
const user = this.afAuth.auth.currentUser;
if (user) {
this.userData = user;
} else {
// force them to reauthenticate
user.reauthenticateWithCredential(credential)
.then(function() {
// User re-authenticated.
this.userData = this.afAuth.auth.currentUser;
}).catch(function(error) {
// An error happened.
});
}
}
Read more about managing users in Firebase here: https://firebase.google.com/docs/auth/web/manage-users. Hope this helps.

Check if Firebase Facebook user exists without creating a user starting from anonymous user

In Firebase I need to check if a Facebook user exists without creating the user. Initially the user is anonymous, and they try to login with Facebook. I want this to fail if the Facebook account is not already linked to a user in my system. It won't be linked to the current user because they are anonymous,
If I use Auth.signInAndRetrieveDataWithCredential I expected a "auth/user-not-found" error, but instead the user is simply created. Is this a bug or expected?
let credential = firebase.auth.FacebookAuthProvider.credential(
event.authResponse.accessToken)
firebase.auth().signInAndRetrieveDataWithCredential(credential).then( (userCredential) => {
let user = userCredential.user
app.debug("DEBUG: Existing user signed in:"+user.uid)
this.loginSuccess(user)
}).catch( (err) => {
app.error("ERROR re-signing in:"+err.code)
$("#login_status_msg").text(err)
})
If I use User.reauthenticateAndRetrieveDataWithCredential instead I get the error "auth/user-mismatch" which makes sense because user is currently anonymous. However, I was expecting "auth/user-not-found" may be thrown instead if the credential doesn't exist, but that doesn't happen.
I don't see a way to take my anonymous user, have them login with Facebook and then see if another user is already linked to that Facebook credential without creating the user if it doesn't exist.
If you're wondering why? My scenario is:
The system allows anonymous users
A user logs in, then converts to a logged in user by registering with Facebook.
App uninstall
App reinstall
User starts up the app and is initially anonymous.
They try and login with Facebook again. At this point I want to stop them from creating a user if they don't have one already. If they have a user ID already, the code works fine and changes their anonymous account ID to the original user ID which is good.
I found a solution! It wasn't too hard to implement, but it does seem hacky.
So we know that when using signInAndRetrieveDataWithCredential(cred) for facebook login, the account is created even if it does not exist yet. To solve this, we need to make sure that we handle the following three things:
Detect if the account is new
Delete the current account that was created by firebase
Throw an error to get out of the current flow and return to wherever you were before.
I just implemented and tested this solution, and it seems to work great:
// ... do your stuff to do fb login, get credential, etc:
const userInfo = await firebase.auth().signInAndRetrieveDataWithCredential(credential)
// userInfo includes a property to check if the account is new:
const isNewUser = _.get(userInfo, 'additionalUserInfo.isNewUser', true)
// FIRST, delete the account we just made.
// SECOND, throw an error (or otherwise escape the current context.
if (isNewUser) {
firebase.auth().currentUser.delete()
throw new Error('Couldn\'t find an existing account.')
}
// If the user already exists, just handle normal login
return userInfo.user
The reason I did this was to ensure that users had to go through the "create account flow" in my app. Your case would be really easy to implement as well, something like the following:
let credential = firebase.auth.FacebookAuthProvider.credential(event.authResponse.accessToken)
firebase.auth().signInAndRetrieveDataWithCredential(credential)
.then(userCredential => {
const isNewUser = userCredential.additionalUserInfo.isNewUser
if (isNewUser) {
firebase.auth().currentUser.delete()
// The following error will be handled in your catch statement
throw new Error("Couldn't find an existing account.")
}
// Otherwise, handle login normally:
const user = userCredential.user
app.debug("DEBUG: Existing user signed in:"+user.uid)
this.loginSuccess(user)
}).catch( (err) => {
app.error("ERROR re-signing in:"+err.code)
$("#login_status_msg").text(err)
})
You can use linkAndRetrieveDataWithCredential:
let credential = firebase.auth.FacebookAuthProvider.credential(
event.authResponse.accessToken);
anonymousUser.linkAndRetrieveDataWithCredential(credential).then( (userCredential) => {
// Firebase Auth only allows linking a credential if it is not
// already linked to another account.
// Now the anonymous account is upgraded to a permanent Facebook account.
}).catch( (err) => {
// Check for code: auth/credential-already-in-use
// When this error is returned, it means the credential is already
// used by another account.
})
You can use the method fetchSignInMethodsForEmail to check if an specific email is already associated to an specific provider or not. Doing this you will be able to check if one if the SighInMethods of the email associated to your user contains Facebook.com or not.
I show you below an example about how I manage this cases on my application. I'm using an RxJavaWrapper on my code, but you will understand the point of how to manage it:
RxFirebaseAuth.fetchSignInMethodsForEmail(authInstance, email)
.flatMap { providerResult ->
if (!providerResult.signInMethods!!.contains(credential.provider)) {
return#flatMap Maybe.error<AuthResult>(ProviderNotLinkedException(credential.provider))
} else {
return#flatMap RxFirebaseAuth.signInWithCredential(authInstance, credential)
}
}
.subscribe({ authResult ->
//Manage success
}, { error ->
//Manage error
})
First I check the providers associated to the email of the user(You can retrieve it from the provider)
If the list of SignInMethods contains my credential provider, I throw an error, if not, I call my signInWithCredential method to create the user.
Continue your workflow.
What I did to solve this problem without relying on the call to linkAndRetrieveDataWithCredential to fail and using the catch block to sign in the already existing user is to save the userID field that getCurrentAccessToken returns.
const { userID } = data;
this.props.setFacebookId(userID); // saves the userID on the server
I can later check if this userID already exists next time the user signs up with facebook.

Parse.com security: can I save an object and claim it's another user's?

I'm looking at this example of modeling a blog system using javascript, code snippet copied as below:
var user = Parse.User.current();
// Make a new post
var Post = Parse.Object.extend("Post");
var post = new Post();
post.set("title", "My New Post");
post.set("body", "This is some great content.");
post.set("user", user);
post.save(null, {
success: function(post) {
// Find all posts by the current user
var query = new Parse.Query(Post);
query.equalTo("user", user);
query.find({
success: function(usersPosts) {
// userPosts contains all of the posts by the current user.
}
});
}
});
It basically creates a post object and sets the current user object to its user field. To show all blog posts by the current user, it queries all blog posts with the user field set to the current user.
But since the User table by default is read only to all users, wouldn't this be problematic that a malicious user (X) can create random posts and "claim" that they are create by another user (Y), by setting the user field of those posts to Y as he queries from the User table? So the consequence would be that when the system shows posts for user Y, he would see all his true posts in addition to the post that was "forged" by X.
Is the mitigation that the User table needs to be ACL'd somehow? But if it is the solution, then why is the default behavior that an arbitrary user can see the entire User table?
Cloud Code is your friend here.
In this case you want a beforeSave handler that locks the user field to the currently authenticated user on new objects, and rejects the save if they're updating a post and trying to change the user field (or just using ACLs to prevent everyone except the post owner from modifying Post rows).
Something like this:
Parse.Cloud.beforeSave('Post', function(request, response) {
var post = request.object;
var user = request.user;
if (post.isNew()) {
post.set('user', user);
response.success();
} else {
// any special handling you want for updates, e.g.:
if (post.dirty('user')) {
response.error('Cannot change the owner of a Post!');
} else {
response.success();
}
}
});
My recommended approach to handling updates for something like a "Post" would be to prevent all updates. In the "Set permissions" for the class in the Data Browser I would change the following:
Update : Disabled
Delete : Disabled
To disable something just untick the "Any user can perform this action". Optionally you might want to assign a Role like "Administrator" or "Moderator" to allow those people to directly update/delete items.
These functions would then only be possible from Cloud Code when useMasterKey() is used, e.g.:
Parse.Cloud.define('deletePost', function(request, response) {
var postID = request.params.postID;
var query = new Parse.Query('post');
query.get(postID).then(function (post) {
if (post) {
// post found
var postOwner = post.get('user');
if (postOwner.id == request.user.id) {
// we let the owner delete their own posts
// NOTE: need to use the master key to modify/delete posts
Parse.Cloud.useMasterKey();
post.destroy().then(function () {
// TODO: delete all replies too?
response.success();
}, function (error) {
response.error(error);
});
} else {
response.error('Only the owner of a post can delete it!');
}
} else {
// post not found, might as well respond with success
response.success();
}
}, function (error) {
response.error(error);
}
});
But since the User table by default is read only to all users,
wouldn't this be problematic that a malicious user can create random
posts and "claim" that they are create by another user, by setting the
user field to the other user?
You can play around with curl to explore this.
IMO - you are right about world read on the _User class. So what. That is read.
When it comes to POST action, you are going to need an authenticated session as the user in question. You cant just spuuf things by claiming that u are a user that you read on the table.
try curl posts without an established session as the user. You will get a 403 or some 'illegal access' response.

Categories