Unable to write to firebase realtime database even though authentication is successful - javascript

I'm creating a sign up form using firebase auth and realtime database.
Here is the basic layout of the form (without CSS).
<script type="module">
// Import the functions you need from the SDKs you need
import { initializeApp } from "https://www.gstatic.com/firebasejs/9.4.0/firebase-app.js";
import { getDatabase, set, ref, update , onValue } from "https://www.gstatic.com/firebasejs/9.4.0/firebase-database.js";
import { getAuth, createUserWithEmailAndPassword, signInWithEmailAndPassword, onAuthStateChanged, signOut , GoogleAuthProvider, signInWithRedirect, getRedirectResult, TwitterAuthProvider } from "https://www.gstatic.com/firebasejs/9.4.0/firebase-auth.js";
// Your web app's Firebase configuration
const firebaseConfig = {
//CONFIDENTIAL
};
// Initialize Firebase
const app = initializeApp(firebaseConfig);
const database = getDatabase(app);
const auth = getAuth();
signUp.addEventListener('click',(e) => {
var email = document.getElementById('signUpEmail').value;
var password = document.getElementById('signUpPass').value;
// var username = document.getElementById('username').value;
createUserWithEmailAndPassword(auth, email, password)
.then((userCredential) => {
// Signed in
const user = userCredential.user;
let dt = new Date();
set(ref(database, 'users/' + user.uid),{
// username: username,
email: email,
prem: false,
pday: dt.getDate(),
pmon: dt.getMonth(),
pyear: dt.getFullYear()
})
alert('Signup Successful!');
window.close();
// window.location.replace('index.html');
})
.catch((error) => {
const errorCode = error.code;
const errorMessage = error.message;
alert(errorMessage);
// ..
});
});
<h1 class='status'>signup</h1>
<input type="text" class="email" name="email" id="signUpEmail" placeholder="E-mail" />
<input type="password" class="password" name="password" id="signUpPass" placeholder="Password" />
<input type="submit" id='signUp' name="signUp" value="signUp" />
The Auth is happening successfully but the user details are not written to the realtime database.
I'm storing the following in the database:
User email
If they have premium subscription for my product (T/F)
Day when their premium ends
Month when their premium ends
Year when their premium ends
Where am I going wrong?

Writing data to the database (and reading data from it) is an asynchronous operation. Asynchronous here means that your main code continues to execute, while the operations runs in the background. But this also means that your window.close() in the main code executes before the database write completes, and in fact it seems to cancel that operation.
The solution is to wait for the database operation to complete before closing the window, similar to what you already do for createUserWithEmailAndPassword :
set(ref(database, 'users/' + user.uid),{
// username: username,
email: email,
prem: false,
pday: dt.getDate(),
pmon: dt.getMonth(),
pyear: dt.getFullYear()
}).then(() => { // šŸ‘ˆ
alert('Signup Successful!');
window.close();
})

Related

Firebase web realtime database stiring user id as undefined

I have a create user and login authentication setup with firebase realtime database. I have a user field in my database where I will store user data. However, I would like to create a child element under the user field that is the user id then the user information can be displayed under that child.
So far I have tried to create such a field with this:
import { initializeApp } from "https://www.gstatic.com/firebasejs/9.13.0/firebase-app.js";
import { getDatabase, set, ref, update } from "https://www.gstatic.com/firebasejs/9.13.0/firebase-database.js";
import { getAuth, createUserWithEmailAndPassword, signInWithEmailAndPassword, onAuthStateChanged, signOut} from "https://www.gstatic.com/firebasejs/9.13.0/firebase-auth.js";
//Signup section
signup.addEventListener('click',(e) => {
var username = document.getElementById('signup-user').value;
var email = document.getElementById('signup-email').value;
var schoolID = document.getElementById('signup-id').value;
var password = document.getElementById('signup-pwd').value;
createUserWithEmailAndPassword(auth, email, password, schoolID)
.then((userCredential) => {
// Signed in
const user = userCredential.user;
update(ref(database, "user/" +user.id),{
username: username,
schoolID: schoolID,
email: email
})
alert('User Created!');
// ...
})
.catch((error) => {
const errorCode = error.code;
const errorMessage = error.message;
alert('Error please try again');
// ..
});
});
But it only gives me an undefined field in my realtime database:
Not only that but it overwrites the previous entry every time a make an new user.
userCredential.user is a User type object, which is a subclass of UserInfo. As you can see from the linked API documentation, User doesn't have an property called id. It will always be undefined.
What you want instead is uid.
update(ref(database, "user/" + user.uid), {...})

How to verify that the email is authentic in Firebase?

I need to add to my current code, the necessary functionality and the exact code so that the user must verify the email before logging in.
Now, the user registers and automatically accesses all the functions of the application and its user panel. I want to add the necessary function so that when a user registers, a message is shown telling him that: You must verify your email In this way we ensure that it is a valid email and avoid the registration of SPA users.
I need the user to verify her email to be able to log in, until she does, she can continue using the App as she did, without logging in.
You can see that I did several tests, and other users tried to help me, but we have not achieved what is necessary, since I need to add the functionality to the code that I have now, since it is the only way I know to continue building my application.
The app has registration with Firebase, registered by email and password and I'm using Formik to control the state of the form and Yup to validate.
I have read Firebase documentation about "Send a verification message to a user",
This is the Firebase function:
```
const auth = getAuth();
sendEmailVerification(auth.currentUser)
.then(() => {
// Email verification sent!
// ...
})
```
The registration system I use now is Mail and Password. The user enters an email, a password, verifies the password and is automatically registered in the application.
I did several tests trying to add sendEmailVerification to my registration system, and for now what I have achieved is that the confirmation email arrives to the user (SPA folder) but the confirmation email arrives after the user already registered and use the app.
It would be necessary that the user could not register until receiving and confirming the "Confirmation Email"
I need a code example that fits my current app, I don't have the knowledge to change all my code, this is the base of my app.
What do I have to do so that this works correctly and the verification email arrives before the user can register?
What am I doing wrong in my code?
I show the application on GitHub, so they can see all the files
You can test the project as it is built with Expo:
exp://exp.host/#miguelitolaparra/restaurantes-5-estrellas?release-channel=default
This is the method I'm using to register users:
const formik = useFormik({
initialValues: initialValues(),
validationSchema: validationSchema(), // validate the form data
validateOnChange: false,
onSubmit: async(formValue) => {
try { // send the data to Firebase
const auth = getAuth()
// sendEmailVerification(auth.currentUser)
await createUserWithEmailAndPassword(
auth,
formValue.email,
formValue.password
)
sendEmailVerification(auth.currentUser)
navigation.navigate(screen.account.account)
} catch (error) {
// We use Toast to display errors to the user
Toast.show({
type: "error",
position: "bottom",
text1: "Failed to register, please try again later",
})
}
},
})
And I also show you the complete file:
import { useFormik } from 'formik'
import { getAuth, createUserWithEmailAndPassword, sendEmailVerification } from 'firebase/auth'
export function RegisterForm() {
const [showPassword, setShowPassword] = useState(false)
const [showRepeatPassword, setShowRepeatPassword] = useState(false)
const navigation = useNavigation()
const formik = useFormik({
initialValues: initialValues(),
validationSchema: validationSchema(), // validate the form data
validateOnChange: false,
onSubmit: async (formValue) => {
try { // send the data to Firebase
const auth = getAuth()
//sendEmailVerification(auth.currentUser)
await createUserWithEmailAndPassword(
auth,
formValue.email,
formValue.password
)
sendEmailVerification(auth.currentUser)
navigation.navigate(screen.account.account)
} catch (error) {
// We use Toast to display errors to the user
Toast.show({
type: "error",
position: "bottom",
text1: "Error al registrarse, intentelo mas tarde",
})
}
},
})
// function to hide or show the password
const showHidenPassword = () => setShowPassword((prevState) => !prevState)
const showHidenRepeatPassword = () => setShowRepeatPassword((prevState) => !prevState)
return (
// Registration form interface
<View>
<Input
placeholder="Correo electronico"
keyboardType="email-address"
containerStyle={AuthStyles.input}
rightIcon={
<Icon type="material-community" name="at" iconStyle={AuthStyles.icon} />
}
onChangeText={(text) => formik.setFieldValue("email", text)}
errorMessage={formik.errors.email}
/>
<Input
placeholder="ContraseƱa"
containerStyle={AuthStyles.input}
secureTextEntry={showPassword ? false : true}
rightIcon={
<Icon
type="material-community"
name={showPassword ? "eye-off-outline" : "eye-outline"}
iconStyle={AuthStyles.icon}
onPress={showHidenPassword}
/>
}
onChangeText={(text) => formik.setFieldValue("password", text)}
errorMessage={formik.errors.password}
/>
<Input
placeholder="Repetir contraseƱa"
containerStyle={AuthStyles.input}
secureTextEntry={showRepeatPassword ? false : true}
rightIcon={
<Icon
type="material-community"
name={showRepeatPassword ? "eye-off-outline" : "eye-outline"}
iconStyle={AuthStyles.icon}
onPress={showHidenRepeatPassword}
/>
}
onChangeText={(text) => formik.setFieldValue("repeatPassword", text)}
errorMessage={formik.errors.repeatPassword}
/>
<Button
title="REGISTRATE"
containerStyle={AuthStyles.btnContainer}
buttonStyle={AuthStyles.btn}
onPress={formik.handleSubmit} // send the form
loading={formik.isSubmitting}// show loading while doing user registration
/>
</View>
)
}
And this is the file to validate the form with Yup RegistreFormValidar.js
import * as Yup from "yup"
// object that has the elements of the form
export function initialValues() {
return {
email: "",
password: "",
repeatPassword: "",
}
}
// validate the form data whit Yup
export function validationSchema() {
return Yup.object({
email: Yup.string()
.email("El email no es correcto")
.required("El email es obligatorio"),
password: Yup.string().required("La contraseƱa es obligatoria"),
repeatPassword: Yup.string() // validate that the passwords are the same
.required("La contraseƱa es obligatoria")
.oneOf([Yup.ref("password")], "Las contraseƱas tienen que ser iguales"),
})
}
You have several options to achieve your purpose.
First, to fix the SPA issue, you can use a custom domain, as shown on Firebase
To get what you are looking for, you can follow these steps:
1 - The user registers with an email address.
2 - The new record is created, but with status "To be verified" and an activation string is assigned.
3 - You send user data and activation string, along with a link to verify registration.
4 - The user clicks on the link, enters their data and, if they are valid, you change the status to "Active".
You can try to do it.
You also have the option to do it with "Authenticate with Firebase via email link"
For users to sign in via an email link, you must first enable the Email Provider and Email Link sign-in method for your Firebase project.
-Then send an authentication link to the user's email address.
To start the authentication process, show the user an interface that prompts them to enter their email address, then call sendSignInLinkToEmail to ask Firebase to send the authentication link to the user's email.
You can see all the details in the official Firebase documentation
1 - Build the ActionCodeSettings object, which provides Firebase with instructions to build the email link
const actionCodeSettings = {
// URL you want to redirect back to. The domain (www.example.com) for this
// URL must be in the authorized domains list in the Firebase Console.
url: 'https://www.example.com/finishSignUp?cartId=1234',
// This must be true.
handleCodeInApp: true,
iOS: {
bundleId: 'com.example.ios'
},
android: {
packageName: 'com.example.android',
installApp: true,
minimumVersion: '12'
},
dynamicLinkDomain: 'example.page.link'
};
2 - Ask the user for the email.
3 - Send the authentication link to the user's email and save their email in case the user completes the login with email on the same device
import { getAuth, sendSignInLinkToEmail } from "firebase/auth";
const auth = getAuth();
sendSignInLinkToEmail(auth, email, actionCodeSettings)
.then(() => {
// The link was successfully sent. Inform the user.
// Save the email locally so you don't need to ask the user for it again
// if they open the link on the same device.
window.localStorage.setItem('emailForSignIn', email);
// ...
})
.catch((error) => {
const errorCode = error.code;
const errorMessage = error.message;
// ...
});
Finally complete the access with the email link.
This is not exactly what you are looking for, but it may help.
Put this listener in your route You can tweak it according to your usage
useEffect(() => {
const unsubscribe = auth().onAuthStateChanged(
async (user) => {
if (user) {
if (user.emailVerified) {
store.setUser(user);
} else {
await user.sendEmailVerification();
auth()
.signOut()
.then(() => {
store.resetStore();
store.setAlertModal('Please Verify Your Email');
})
.catch((error) => log.error('Signout Error', error));
}
}
}
);
return () => {
// Unsubscribe
unsubscribe();
};
}, [store]);
As far as I understood, you need to verify email address of the user first, then create the user. Blocking functions maybe what you need.
exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
const locale = context.locale;
if (user.email && !user.emailVerified) {
// Send custom email verification on sign-up.
return admin.auth().generateEmailVerificationLink(user.email).then((link) => {
return sendCustomVerificationEmail(user.email, link, locale);
});
}
});
This Firebase function will trigger before a new user is saved to the Firebase Authentication database, and before a token is returned to your client app. However, I think after this function executes, user is created. To prevent user creation you may have to implement a more complex flow.
One naive approach I can think of is as follows: After sending email to user, do not terminate the function and inside the function periodically check if user's email address is verified. Also set a timeout option and reject user creation after timeout. As expected, this approach increases the function execution time and can be costly.
If you are fine with the user being created in the Firebase Authentication database, I suggest implementing the solution stated in the documentation.
exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
const locale = context.locale;
if (user.email && !user.emailVerified) {
// Send custom email verification on sign-up.
return admin.auth().generateEmailVerificationLink(user.email).then((link) => {
return sendCustomVerificationEmail(user.email, link, locale);
});
}
});
exports.beforeSignIn = functions.auth.user().beforeSignIn((user, context) => {
if (user.email && !user.emailVerified) {
throw new functions.auth.HttpsError(
'invalid-argument', `"${user.email}" needs to be verified before access is granted.`);
}
});
This will block users with unverified emails from logging into your app.
Check this documentation for other possible options: https://firebase.google.com/docs/auth/extend-with-blocking-functions#requiring_email_verification_on_registration

I can create firebase user using this code, but how can I add display Name, phone Number and other information in default firebase user table? Thanks

This is my funcation code.
const signUpFun = () => {
if (isValidForm()) {
createUserWithEmailAndPassword(auth, email, password)
.then((userCredential) => {
// Signed in
const user = userCredential.user;
setbitdata({});
})
.catch((error) => {
const errorCode = error.code;
const errorMessage = error.message;
setEmailAndPasswordCheck(true)
setTimeout(() => {
setEmailAndPasswordCheck(false)
}, 3500)
});
}
}
import { getAuth, updateProfile } from "firebase/auth";
const auth = getAuth();
updateProfile(auth.currentUser, {
displayName: "My Name", photoURL: "https://example.com/jane-q-user/profile.jpg"
}).then(() => {
// Profile updated!
}).catch((error) => {
// An error occurred
});
If you are using firebase version 8, then you should use like these:
import firebase from "firebase";
const user = firebase.auth().currentUser;
user.updateProfile({
displayName: "My Name",
photoURL: "https://example.com/jane-q-user/profile.jpg"
}).then(() => {
// Update successful
}).catch((error) => {
// An error occurred
});
Firebase have very great documentation, you should probably read that, as like #Alexandros Giotas mentioned the link for official Documentation for your question
You're probably looking for the updateProfile method.
Using modular imports:
import { getAuth, updateProfile } from "firebase/auth";
const auth = getAuth();
updateProfile(auth.currentUser, {
displayName: "Your display name here",
// more updating..
}).then(() => {
// Success
}).catch((e) => {
// Handle errors.
});
Remember that updateProfile returns a Promise which you should handle, with either then() & catch() as shown above or await if you're updating within an async function.
I encourage you to read the Firebase docs on updating a user's profile.
There are more code snippets there, that answer your question probably better than I did.

Users duplicate on Firestore when I use googleSignIn

I have two ways to register a user in Firebase: through email and through Google Sign In.
I perform the user registration by email as follows:
signUp() {
const auth = getAuth();
const db = getFirestore();
createUserWithEmailAndPassword(
auth,
this.createEmail,
this.createPassword
).then(
(userCredential) => {
const user = userCredential.user;
this.$router.push("/");
addDoc(collection(db, "users"), {
email: this.createEmail,
name: this.createName,
});
},
);
},
In other words, in addition to saving the user in Firebase Authentication, I also send their name and email to Firestore. And this is my first question:
Is it the most effective way to save the username and future data that will still be added to it?
Finally, login by Google is done as follows:
googleSignIn() {
const auth = getAuth();
const provider = new GoogleAuthProvider();
signInWithPopup(auth, provider)
.then((result) => {
this.$router.push("/");
addDoc(collection(db, "users"), {
email: result.user.email,
name: result.user.displayName,
});
})
},
Here a problem arises because if a user logs in more than once in Firebase Authentication everything is ok, but in Firebase Firestore a user is created for each new login with Google.
How do I handle this issue of storing users in Firestore, especially users coming from Google Login?
First, I'd move the router.push() statement below addDoc() so I can confirm that the document has been added and then user is redirected to other pages. In case of Google SignIn, you can check if the user is new by accessing the isNewUser property by fetching additional information. If true, then add document to Firestore else redirect to dashboard:
signInWithPopup(auth, provider)
.then(async (result) => {
// Check if user is new
const {isNewUser} = getAdditionalUserInfo(result)
if (isNewUser) {
await addDoc(collection(db, "users"), {
email: result.user.email,
name: result.user.displayName,
});
}
this.$router.push("/");
})
It might be a good idea to set the document ID as user's Firebase Auth UID instead of using addDoc() which generated another random ID so it's easier to write security rules. Try refactoring the code to this:
signInWithPopup(auth, provider)
.then(async (result) => {
// Check if user is new
const {isNewUser} = getAdditionalUserInfo(result)
const userId = result.user.uid
if (isNewUser) {
await setDoc(doc(db, "users", userId), {
email: result.user.email,
name: result.user.displayName,
});
}
this.$router.push("/");
})

Can't add user to firestore database after signing up with firebase

I want to add a user to a firestore database after signing up through firebase with email and password. I get no errors, and the user is created and is visible in firebase auth. However the user is not added to the firestore database.
Does anyone know how to make this work? My code snippet is pasted below.
firebase.initializeApp(config);
const auth = firebase.auth();
const db = firebase.firestore();
auth.createUserWithEmailAndPassword(email, pass).then(cred => {
const userId = cred.user.uid;
const userData = {
firstName: firstName,
lastName: lastName,
email: email
};
db.collection("users").doc(userId).set(userData).then(() => {
console.log("User successfully added to the DB!");
})
.catch((e) => {
console.log("Error adding user to the DB: ", e);
});
}).then(() => {
console.log('User created! - ' + email);
}).catch(e => {
console.log(e.message);
txtErrMsg.classList.remove('hide');
txtErrMsg.innerHTML = e.message;
});
FYI: I have console logged the parameters (userId and userData), and they appear just fine.
Thanks!
I figured out what was wrong with my code.
I wrote another method auth.onAuthStateChanged() which was interfering with my action to write to the database. Since I have two auth files (signup.js and login.js), I decided to only keep this method in login.js and to remove it from signup.js.
Now I can successfully add the user to the DB upon signup (signup.js), and I don't need to add anything to the DB when a user is simply logging in (login.js).
I hope this helps anyone out there experiencing the same problem.
I experienced that problem myself. The only way I managed to get this working was to await auth first, and then add the user to firestore:
I have a firebaseConfig.js where I set up the usersCollection:
import firebase from 'firebase';
import 'firebase/firestore';
const firebaseConfig = {
apiKey: ......
};
firebase.initializeApp(firebaseConfig);
const auth = firebase.auth();
const db = firebase.firestore();
const usersCollection = db.collection('users');
export { auth, usersCollection };
Then, I use it like this.
const fb = require('../firebaseConfig.js');
....
const cred = await fb.auth
.createUserWithEmailAndPassword(
'someemail#test.com',
'password123'
)
.catch(e => {
console.log(e.message);
});
if (cred) {
console.log({ cred });
const userId = cred.user.uid;
const userData = {
firstName: 'firstName',
lastName: 'lastName',
email: 'email',
};
fb.usersCollection
.doc(userId)
.set(userData)
.then(() => {
console.log('User successfully added to the DB!');
})
.then(() => {
console.log('User created!);
})
.catch(e => {
console.log('Error adding user to the DB: ', e);
});
}
When I ran this code I got the following in my log:
And it appears in my database like this:

Categories