Adding users to Firebase Database - firebase

I'm learning Firebase and creating my first project with it. I'm using FirebaseUI to simplify the authentication. I'm now working on the database and I need to start by adding my authenticated users to it. I've read all the documentation and I'm able to do this, but I'm wondering if I'm doing it the best way.
Since I'm using FirebaseUI and I'm not actually calling the signIn() or createUser() methods of Firebase Authentication myself, I thought the best way for me to add users would be to do it onAuthStateChanged()
usersRef = rootRef.child('users')
firebase.auth().onAuthStateChanged(user => {
if (user) {
let userRef = usersRef.child(user.uid)
userRef.set({
name: user.displayName,
email: user.email,
photoURL: user.photoURL,
emailVerified: user.emailVerified,
})
}
}
This works fine but I'm concerned about two things:
1) This sets the user data every time the user is authenticated, even if the user already exists and nothing has changed. A simple page reload will rewrite the user to the database.
2) In order for this to work, the userRef location needs to be writable by the user. This would mean that the emailVerified in the userRef location isn't reliable because the user could potentially modify it himself. I could just rely on the emailVerified returned from onAuthStateChanged which the user could never modify, but I'm wondering if I'm just doing this wrong or if there's a better way.
A possible solution is described in a video found at https://www.youtube.com/watch?v=QEd2lEoXpp40. He creates two sections in the database: users and loginQueue. A user in users is only readable by the authorized user and loginQueue is only writable by an authorized user. When a user is authenticated, his data gets written to the loginQueue. He then uses the on() method to check for a child added to the loginQueue that matches their user.uid and somehow uses the update method to write to the user data to users. This works but I don't understand how. How can is the client able to send an update() to users\uid if it's only readable? You can see his code at 7:00 in the video. This has me stumped. This works but why.
I just implemented the technique as shown in the video and although it worked for him, I encountered a PERMISSION_DENIED error when trying to update the only readable user data. This is what I thought SHOULD happen but in his video he clearly shows this was not happening. Unless I'm missing something which I don't think I am, his method doesn't work anymore. Maybe it was a bug that was later fixed?
UPDATE: Thanks to Doug Stevenson for pointing me to Firebase Cloud Functions. I was able to completely solve my problem by creating a cloud function that responds when new users are authenticated. Here is my code:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.addUserToDB = functions.auth.user().onCreate(event => {
admin.database().ref('/users/' + event.data.uid).set({
name: event.data.displayName,
email: event.data.email
});
});

This is a common strategy.
Authentication shouldn't happen too often. Also, if nothing changed in user record since the last write, nothing should actually happen. You don't pay for the bandwidth for client writes to the database.
Split your user data up into two sections, one that's writable by the current UID, and the other that's not. That will prevent problems with users modifying data that you'd not like them to.
Alternately to this, set aside another location in your database where your clients can push commands to update data elsewhere, and use a Cloud Functions for Firebase trigger to read these commands, act on them with elevated privilege, checking for correctness, and delete them.

Related

Loop user registration for firebase

Could someone please suggest how to properly set up a loop for registration? The idea is that I have an excel file with 500+ users ( emails and raw passwords ) which I wanted to register. As I understood the best thing is to read the excel file in js , assign for each email and password a variable and call the createusernamendpassword method in firebase upon each iteration ? Please don’t suggest using the auth:import as it suggest you to have already salted and hashed passwords ( as I don’t have them ). The whole point is to create in bulk absolutely new usernames
Using the Adkin SDK from your local machine or a trusted environment like a server or cloud functions, You can run the Create User from the auth package inside a loop. it is important to implement tracking if you are doing it in Firebase Cloud Functions as the 9-minute timeout (if configured) might not be enough to finalize processing.
async function ProcessUsers(){
for(const user in userList){
await admin
.auth()
.createUser({
email: 'user#example.com',
emailVerified: false,
password: 'secretPassword'
})
.then((userRecord) => {
// See the UserRecord reference doc for the contents of userRecord.
console.log('Successfully created new user:', userRecord.uid);
})
}
}
you can do several improvements from the base script such as setting up a queue to ensure the values are processed before batching more together. But this is sufficient to get started.
Source: https://firebase.google.com/docs/auth/admin/manage-users#create_a_user
If you do this with the regular client-side SDKs, you will quickly be rate-limited as quickly creating accounts from a single client is a common abuse scenario.
The Firebase Admin SDKs are designed to (only) run in trusted environments, such as your development machine, a server you control, or Cloud Functions. And these Admin SDKs have dedicated APIs to bulk import users. That is the best way to go about this.
You can also use the Firebase CLI to but import the users, which is quite similar.
Neither of these options takes a so-called cleartext password though, so you'll want to hash those first as shown on bojeil's answer here: can i import csv with real password to firebase authentication

Fake users in firebase with #gmail.com accounts

I have a firebase project.
The next sign-in methods auth are enabled:
Google
Facebook
Apple
Anonymous
A mobile app interacts with the firebase.
Each day I get some weird new users sign-ups with fake accounts with the pattern: [name][numbers]#gmail.com. They don't do anything except sign up via google oauth once.
Is it possible to prevent it? Maybe I missed something with the google oauth configuration?
Updated:
Also, I noticed that these sign-ups started to occur when I had sent out the mobile app to google/apple verification. May these two events are correlated?
New accounts created coz of Play market Pre Launch Report
You can change Pre Launch Report settings to change it's behaviour (e.g. specify test account to use in auth)
If you are sure those fake users have a specific pattern from their email address, I would make a trigger function on Cloud Functions for Firebase.
You can use functions.auth.user().onCreate() event handler like below.
exports.checkFakeUser = functions.auth.user().onCreate((user) => {
// You can check if the user has suspicious email patterns and delete them here.
});
Or you can also make a Schedule function on Cloud Functions for Firebase and daily check if there are fake users and automatically delete them.
Plus, it would be a good step if you figure out that fake users still joining even you didn't expose your mobile app anywhere if you want to find out the reason how they are joining.
Add the following Cloud Function will help you on check the email and delete the fake user
exports.checkFakeUser = functions.auth.user().onCreate((user) => {
const list = user.email.split(".")[1].split("#")
const isFake = list[0].length === 5 && list[1] === 'gmail'
if(isFake){
admin.auth().deleteUser(user.uid)
.catch(function(error) {
console.log('Error deleting user:', error);
});
}
});
You can't stop specific accounts from being created, as the underlying Google Auth APIs are accessible to anyone with an account. You could manually delete them, or write a program to delete them (bearing in mind that you could also be deleting actual user accounts).
Or if you suspect abusive behavior, you can contact Firebase support to report that.
Check, these e-mail addresses will be re-logged when they upload a new version to google play. The most likely reason for this is that google keeps your application to a number of tests with its automation infrastructure.

Flutter Firebase adding other user information such as Address to the database

I am currently working on Flutter Firebase to save the details of the users that are signing up for the app.
What I've done is that the user are able to sign up with their email and password. But what I would like to know is how do I add other user information such as Address to be saved so that the users will be able to see it when I am retrieving it from Firebase to the app.
What I've done :
AuthResult user = await
FirebaseAuth.instance.createUserWithEmailAndPassword(email: _email,password:_password);
This saves the user email and password into the database.
I've tried the solution on : Flutter user registration with Firebase: Add extra user information (age, username)
What I've tried to do :
Firestore.instance.collection('users').document().setData({ 'userid':
user.uid, 'username': _address });
And I seem to get errors on that Firestore is an undefined name along with the uid isn't defined for the class 'AuthResult' . Am I missing some import files?
I've only imported :
import 'package:firebase_auth/firebase_auth.dart';
Also, I know I have to create a table called user to allow the data to be saved in but which one do I use? The Cloud Firestore or the Realtime Database?
Thanks for all the help.
You have a lot of questions hidden in a single question. I'll try to address as many as I can below, but please try to stick to a single question in the future.
Recommending one database over the other is off-topic on Stack Overflow. But I'd recommend reading the Firebase documentation on it here.
Since you started with Cloud Firestore, let's focus on that.
You'll need to import the plugin for Firestore, just like you've done with the plugin with Authentication. So something like:
import 'package:cloud_firestore/cloud_firestore.dart';
As you've discovered, there is no AuthResult.uid property. In such cases, I recommend browsing the reference documentation for the plugin, which makes it fairly easy to see that AuthResult.user is probably the path to go.
So something like:
AuthResult result = await
FirebaseAuth.instance.createUserWithEmailAndPassword(email: _email,password:_password);
User user = result.user;
Not the solution to the question you're asking, but please use document(user.uid) instead of just document(). What you now have generates a document ID, while it's idiomatic (and much easier) to use the UID as the key in a users collection.

How to trigger a cloud function in Firebase on password change?

I am trying to trigger a Firebase cloud function when a user changes their password, be it by
changing the password (firebase.auth().currentUser. updatePassword(newPassword) ) or by reseting it (firebase.auth(). sendPasswordResetEmail(email) ). Since I am not storing password anywhere I can not use .onUpdate trigger (or any other of the triggers).
I found a similar question, but it only asked about a trigger on password change and there is no info about a workaround: Firebase cloud function listener for password change
Edit: with this trigger I want to send an email to user that their password has been changed.
Does anyone have any ideas? Is it possible at all?
What you want is totally possible, but you'll have to implement it yourself. As the link you found states, there is no built-in on-password-update account trigger... only onCreate and onDelete account triggers. Which means we have to handle it manually. I would handle it the same way you are heading - using a cloud function to send the user an email.
I would build a cloud function named something like notifyUserOfPasswordChange() and call that cloud function from your app immediately after the line of code where you call .updatePassword() or .confirmPasswordReset() (which is the finishing step after .sendPasswordResetEmail()). If I understand the point of your question - this is the real answer here. You will need to call a cloud function manually whenever you execute password update code. There's no automated trigger.
The email can be as simple or customized as you code it. If you're unsure, then start simple and have the cloud function get the target email address from the data parameter - and then use a generic message for the email body. If you feel more adventurous, consider passing the user's UID and using the Admin SDK to look up that user's registered email address & display name and then building a prettier & personalized HTML email.
As far as how to send an email from Firebase cloud functions, there are plenty of examples out there - a good sample is on Firebase's GitHub page.
Workaround
If you're using Firestore, you can use Cloud Firestore triggers as a workaround.
Step 1
Create a Cloud Function called sendEmail.
// This cloud function will get triggered when a new document is added to `emails` collection.
exports.sendEmail = functions.firestore
.document('emails/{documentId}')
.onCreate(async (snapshot, context) => {
const data = snapshot.data()
const user = data.user
if (data.changedPassword == true) {
// send email to the user saying password was changed
} else if (data.changedEmail == true) {
// send email to the user saying the email address was changed
}
})
Step 2
Write a document to your emails collection anytime the user updates their password.
// After you call .updatePassword() or .confirmPasswordReset()
// then you do this
const db = firebase.firestore();
db.collection("emails").add({
user: {INSERT USER ID HERE}",
changedPassword: true, //changedEmail: true,
})
.then((docRef) => {
// handle success
})
.catch((error) => {
// handle error
});
(Optional) Step 3
Write a Firebase Rule to protect the emails collection. The rule says: "Only allow documents to be written to the emails collection if the user field matches the authenticated user."
Summary
On the client, you'll write a new document to the emails collection anytime you want to send an email. The document will include fields for the user id and the type of email (e.g. changedPassword, changedEmail). Your cloud function will automatically send emails when documents are added to the emails collection. Firestore Rules will ensure emails are sent only to the intended user.
This system is reusable for any type of email you want to send the user. In my app, I send emails to the user when their password is changed and when their email is changed.

Query firestore on the server with auth info in a nuxt app

I plan to query some data from Firestore within the fetch() method that Nuxt provides and then send it to the store. The problem is that the query requires the Firebase Auth info (the Firestore rules more precise), and afaik will the Firebase Auth not yet be connected or have loaded the active user since that happens client side. So that would mean that the query wouldn't have the auth info? How would I go about solving this? I have looked around and found that you could use cookies to store the Firebase Auth id and then within nuxtServerInit() in the store, load the user and get the correct uid and so forth, but Firebase will still not know about the user and I haven't found a way to load the user into Firebase Auth on the server side using the id from the cookie.
Any help would be really appreciated! (I released this got quite messy but hopefully you guys get the main point)
Worth nothing that I'm using Google as the sign in provider.
UPDATE:
I've done some digging and found that you can get the idToken and accessToken from the AuthCredential you get back after successful sign in. I think you could store those instead in a cookie and then sign in the user on the server using signInWithCredential() and pass a new GoogleAuthProvider that you could create with GoogleAuthProvider.credential(idToken, accessToken) using the tokens from the cookie.
Again, I'm not sure if this is safe to do or the correct way but I think it should work at least because now Firebase would now about the user already on the server side and therefore should the call to Firestore be no problem regarding the auth info.
I'm not able to test this right now but some thoughts on this would be great, thanks!
Still not able to test it but would like someone thoughts on this.

Resources