Can't write user data to firebase - javascript

I'm new at this so bear with me. I'm trying to use the fire base simple login with the email and password. I have that working with this:
var authClient = new FirebaseAuthClient(fireRef, function(error, user) {
if (error) {
// an error occurred while attempting login
if(error.code === "INVALID_EMAIL"){
$('#log_email_error').hide();
$('#log_pass_error').hide();
$('#log_email_error').html("Invalid email specified.").fadeIn();
$('.login_button').removeClass('login_button_success').attr('value','Log in');
}
if (error.code === "INVALID_PASSWORD") {
$('#log_email_error').hide();
$('#log_pass_error').hide();
$('#log_pass_error').html("The specified password is incorrect..").fadeIn();
$('.login_button').removeClass('login_button_success').attr('value','Log in');
}
console.log(error);
} else if (user) {
// user authenticated with Firebase
hideLogin();
$('.userInfo_cont').show();
$('.userInfo').html('<div> '+ user.email + ' <span class="grey">| </span> </div>');
$('.logout').on('click',function(){
authClient.logout();
});
console.log('User ID: ' + user.id + ', Provider: ' + user.provider);
} else {
// user is logged out
$('.userInfo_cont').hide();
showLogin();
}
});
But when the user registers I want to store some additional info in a firebase/users area
which I can do with this in the registration:
$('#submit_reg').on('click',function(){
var firstName = $('#regFirstname').val();
var lastName = $('#regLastname').val();
var email = $('#regUsername').val();
var password = $('#regPassword').val();
authClient.createUser(email, password, function(error, user) {
if (!error) {
console.log('User Id: ' + user.id + ', Email: ' + user.email);
authClient.login('password', {
email: email,
password: password,
rememberMe: false
});
hideLogin();
userInfo = {
userId : user.id,
firstName : firstName,
lastName : lastName,
email : user.email
}
var url = USERS_LOCATION + "/" + user.id;
var userRef = new Firebase(url);
console.log(userInfo);
userRef.set(userInfo);
}else{
//display error
alert(error);
}
});
});
My Problem is when I implement the read write rules like the documentation has:
{
"rules": {
"users":{
"$userid": {
".read": "auth.id == $userid",
".write": "auth.id == $userid"
}
}
}
}
I get a permission denied when the user registers. So it registers the user just fine but won't write the additional data to the /users/id area.
Any help would be much appreciated.
Craig

In the snippet above, you're calling login(), and then immediately calling set() afterwards. This is problematic because the login() method is asynchronous, and you are almost always guaranteed to have called set() method prior to the return of the login attempt method, since login() is non-blocking yet makes a network call to the Firebase servers.
This means that even though you're calling login() with the correct email and password, you're trying to set the data before the authentication process has completed.
I would recommend moving your set() logic into a block that will only be executed when you are certain that the user has already authenticated, such as in the callback you passed when calling new FirebaseAuthClient() and detected a logged in user.

Related

How do I store the user uid to firebase database after I register an account?

Here's my code in .js
$("#loginBtn").click(
function(){
var email = $("#loginEmail").val();
var password = $("#loginPassword").val();
if(email != "" && password != ""){
$("#loginProgress").show();
$("#loginBtn").hide();
$("#registerBtn").hide();
firebase.auth().signInWithEmailAndPassword(email,
password).catch(function(error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
$("#loginError").show().text(errorMessage);
$("#loginProgress").hide();
$("#loginBtn").show();
$("#register_account_Btn").hide();
$("#back_btn").hide();
});
}
}
);
/* REGISTER PROCESS */
$("#register_account_Btn").click(
function () {
var email = $("#regEmail").val();
var password = $("#regPassword").val();
if(email != "" && password != ""){
$("#loginProgress").show();
$("#loginBtn").hide();
firebase.auth().createUserWithEmailAndPassword(email,
password).catch(function(error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
$("#loginError").show().text(errorMessage);
$("#loginProgress").hide();
$("#loginBtn").show();
$("#register_account_Btn").hide();
$("#back_btn").hide();
});
}
I found this on the internet but I don't know where to insert it.
Is this correct?
var uid = firebase.auth().currentUser.uid;
firebase.database().ref().child('accounts').child(uid).set({
email: user.email,
userId: uid
})
Should I create another function?
and these are the rules in my firebase database. Is it advisable to use this?
Additional Question:
I want to create a user roles: teacher and student. Teacher can create classroom, add students, view the list of students. While the student can view his/her classmates and teacher. How should I do this?
{
"rules": {
"users": {
"$uid": {
".read": false,
".write": "$uid === auth.uid"
}
}
}
}
Sincerest apology but I'm new to this ;(
The firebase.auth().createUserWithEmailAndPassword function returns a Promise: firebase.User.
You're only catching exceptions with your code, if you chain a .then onto the function you can get the user information and save that user to the data.
firebase.auth().createUserWithEmailAndPassword(email, password)
.then(user => {
// Save user here.
return firebase.database().ref('/users/' + user.uid).set({
email: user.email,
uid: user.uid
});
})
.catch(function(error) {
// Handle Errors here.
});
Your other question around the teacher / student is very broad - try to narrow it down to a solution and if you get stuck with some specific create a new question.
Since version 5.0.0 of the Firebase JavaScript SDK, createUserWithEmailAndPassword() returns a Promise containing the new user's credentials as a UserCredentials object instead of a User object.
However, the UserCredentials object contains the User object as it's user property, which you can get the new email and user ID from as in #sketchthat's answer.
firebase.auth().createUserWithEmailAndPassword(email, password)
.then(credential => {
// Save user here.
return firebase.database().ref('/users/' + credential.user.uid).set({
email: credential.user.email,
uid: credential.user.uid
});
})
.catch(function(error) {
// Handle Errors here.
});

react native TypeError: Cannot read property 'navigation' of undefined

I am using the FacebookAuthProvider by firebase to login my users from my platform.
I'm using react native in expo with firestore and it was working fine till I tried to add in some checks to redirect users to the correct screens after login. There are two different roles (administrators and users) which have to be separate right after the login.
if (/* user is administrator */) {
this.props.navigation.navigate('Admin');
} else {
this.props.navigation.navigate('Main');
}
After adding this method to separate users by there roles, I got this error:
react native TypeError: Cannot read property 'navigation' of undefined
Later I will add some more details (log files etc. as soon as I've learned how to grep them from my locale machine).
For better understanding I put my whole code here (sorry for the bad indentations which lesses the readability):
const auth = firebase.auth();
const firebaseUser = '';
const usersRef = firebase.firestore().collection('users');
async handleFacebookButton() {
const { type, token, } = await Facebook.logInWithReadPermissionsAsync(FACEBOOK_APP_ID, {
permissions: ['public_profile', 'email']
});
if (type === 'success') {
//Firebase credential is created with the Facebook access token.
const credential = firebase.auth.FacebookAuthProvider.credential(token);
auth.signInAndRetrieveDataWithCredential(credential)
.then(function(userCredential) {
newUserCheck = userCredential.additionalUserInfo.isNewUser;
console.log('newUserCheck = ', newUserCheck)
});
this.setState({loggedIn: "You are signed in"})
this.setState({signedIn: true})
console.log('you are signed in');
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
firebaseUser = {name: user.displayName, uid: user.uid, email: user.email}
console.log(firebaseUser.name, ' and ', firebaseUser.uid);
var existingRef = usersRef.doc(firebaseUser.uid);
existingRef.get().then(function(documentSnapshot) {
// check if user is registered
if(documentSnapshot) {
data = documentSnapshot.data();
console.log('existing user exists!!');
// check if user is an administrator
if (data.administrator == true) {
console.log('existing administrator exists!!');
this.props.navigation.navigate('Admin');
} else { this.props.navigation.navigate('Main');
}
}
});
(error => {
console.log('user not accessed: ', error);
});
//User is not yet in firebase database and needs to be saved
// double check that user is a new user
if (newUserCheck == true) {
this.ref
.doc(uid)
.set({
id: firebaseUser.uid,
username: firebaseUser.name,
email: firebaseUser.email,
})
this.props.navigation.navigate('ChooseRoute')
}
}
})
}
// If login type is not success:
(error => {
this.setState({loggedIn: "Login failed: log in again"})
this.setState({ errorMessage: error.message });
});
}
I fixed it!! 3 days later - it was a binding issue - after several unsuccessful attempts to work out which were the right parts of the functions to bind I converted both 'auth().onAuthStateChanged' and 'documentSnapshot' into fat arrow functions and the errors are gone!! Thank goodness for ES6...! Hope this helps someone else down the line...

How to add username with email and password in Firebase?

I'm using Firebase and I'm trying to add a username to the database with the email and password.
Is there a way to do it or is createUserWithEmailAndPassword() function only for email and password?
signUp.addEventListener("click", function(user)
{
var username = usernameTxt.value;
var email = emailTxt.value;
var password = passwordTxt.value;
firebase.auth().createUserWithEmailAndPassword(email, password).catch(function(error)
{
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
if (errorCode == 'auth/email-already-in-use')
{
alert('email-already-in-use.');
}
else
{
alert(errorMessage);
}
console.log(error);
});
});
The solution that i found to how to add username with the createUserWithEmailAndPassword() function:
firebase.auth().onAuthStateChanged(function(user) {
var username = usernameTxt.value;
if (user) {
firebaseDataBase.ref('users/' + user.uid).set({
email: user.email,
uid : user.uid,
username: username
});
console.log("User is signed in.");
} else {
console.log("No user is signed in.");
}
});
You need to create that database users child node yourself ;-)
The createUserWithEmailAndPassword function only creates a new user in Firebase authentication service. The database itself isn't changed at all as a result.
To add this new user to the database as well, try:
firebase.database().ref("users").child(user.uid).set(...)
This cannot be done through createUserWithEmailAndPassword() but there is a firebase method for this . You will need to listen for when authentication state is changed , get the user , then update the profile info . See Below
This code would come after createUserWithEmailAndPassword()
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
// Updates the user attributes:
user.updateProfile({ // <-- Update Method here
displayName: "NEW USER NAME",
photoURL: "https://example.com/jane-q-user/profile.jpg"
}).then(function() {
// Profile updated successfully!
// "NEW USER NAME"
var displayName = user.displayName;
// "https://example.com/jane-q-user/profile.jpg"
var photoURL = user.photoURL;
}, function(error) {
// An error happened.
});
}
});
As stated in firebase User Api here : https://firebase.google.com/docs/reference/js/firebase.User#updateProfile
Hope this helps
You can create a users endpoint and store custom user data in there.
function writeUserData(userId, name, email, imageUrl) {
firebase.database().ref('users/' + userId).set({
username: name,
email: email,
profile_picture : imageUrl,
// Add more stuff here
});
}
Have a look to https://firebase.google.com/docs/database/web/read-and-write

Getting Correct Info in Console, But Angular 2 Login Not Working as Expected

I have a login in my Angular 2 app, and I have been converting it from using a fake backend (which works) to connect to our mongoDB-based API instead.
This is the login function I am using in the authentication service:
login(username: string, password: string) {
const u = encodeURIComponent(username);
const p = encodeURIComponent(password);
this._url = `https://api.somesite.com/v0/staff/login/${u}/${p}?apikey=somekey`;
console.log(this._url);
return this.http.post(this._url, JSON.stringify({ username: username, password: password }))
.map((response: Response) => {
// login successful if there's a jwt token in the response
const user = response.json();
if (user && user.token) {
// store user details and jwt token in local storage to keep user logged in between page refreshes
localStorage.setItem('currentUser', JSON.stringify(user));
}
});
}
In my login component I am subscribing like this:
login() {
this.loading = true;
this.authenticationService.login(this.model.username, this.model.password)
.subscribe(
data => {
this.router.navigate(['/']);
console.log('User logged in as: ' + this.model.username);
},
error => {
this.alertService.error(error);
this.loading = false;
});
this.authenticationService.username = this.model.username;
}
When I try this, and log to the console "this_url", I get what I would expect. For instance, if the user typed in "billsmith" for username, and "parisnow" for password, I see this in the console for "this_url":
https://api.somesite.com/v0/staff/login/billsmith/parisnow?apikey=somekey
Furthermore, I can type that url directly into the browser address window and see data (when the username and password correctly correspond to actual records in our database). So it's accessing the correct info in that sense.
But in the console I get a "404" error for that generated url. It also doesn't "do anything". In other words, it doesn't correctly redirect to the main component as it did with the fakeBackend-enabled login. And the only thing that's different now is the url that I am calling (because I'm connecting to our actual API now, as opposed to a fake backend provider).
FYI, the url when using the fake backend looked like this:
return this.http.post('/api/authenticate', JSON.stringify({ username: username, password: password}))
What am I missing here?
By the way, this is how things look on the server side re: our mongoDB:
exports.byLogin = function(req, res, next) {
let ioOnly = false, username, password;
if (_.isUndefined(req.params)){
ioOnly=true;
username = req.username;
password = req.password;
}
else {
username = req.params.username;
password = req.params.password;
}
staff.findOne({username: username, password: password}, function(err, doc) {
if (err) { if (!ioOnly) { return next(err) } else { return res(err)}}
else if(doc) ((!ioOnly) ? res.send(doc) : res(doc));
else ((!ioOnly) ? res.sendStatus(204) : res(doc));
});
};

Allow actions based on Meteor.user() porperties?

I want to allow adding and deleting from my Meteor collections based on a user property.
This is how I set up my admin user:
if (Meteor.isServer) {
if (Meteor.users.find().count() === 0) {
Accounts.createUser({
username:'Greg',
password:'default',
isAdmin: 1
});
}
}
I now want to allow every user with isAdmin = true to create another user via Accounts.createUser:
Meteor.methods({
makeUser: function(attributes) {
var user = Meteor.user();
if (user.isAdmin)
Accounts.createUser(attributes)
else
console.log('User ' + user.username + ' created a player.')
}
})
The user is never created as if user.isAdmin never equals true. What am I doing wrong? Is this related to publishing and subscribing? At the moment I still have autopublish switched on.
Add flag isAdmin to profile object:
Accounts.createUser({
username:'Greg',
password:'default',
profile:{
isAdmin: 1
}
});
See docs
Accounts.createUser methods allows to add ONLY fields username, password, email and profile to user object.
Meteor.methods({
makeUser: function(attributes) {
var user = Meteor.user();
if (user.profile && user.profile.isAdmin)
Accounts.createUser(attributes)
else
console.log('User ' + user.username + ' created a player.')
}
})
Update
Consider using package roles.
In that case normal user can still call Accounts.createUser and completely bypass your makeUser to create a user, which I don't think it's what the behaviour you want to see.
I would suggest wrapping Accounts.onCreateUser with the isAdmin logic from #Kuba Wyrobek:
// server side
Accounts.onCreateUser(function(options, user) {
user.profile = options.profile ? options.profile : {};
if (user.profile && user.profile.isAdmin) {
return user;
} else {
throw new Meteor.Error(403, "Forbbiden");
}
});

Categories

Resources