I'm currently working with Firebase Authentication and absolutely loving it. The only problem right now is that I'm trying to validate the user's display name using my own rules. What I have tried is to use Cloud Firestore to store the display name and other related info such as address, etc. On registration, the user has to declare his/her display name as well as email and password. Somewhere in my code I'm doing this:
try {
await firebase.auth().createUserWithEmailAndPassword(email.value, password.value);
await firebase
.firestore()
.collection('/users')
.doc(firebase.auth().currentUser.uid)
.set({
displayName: displayName.value,
});
} catch (err) {
setIsLoading(false);
setErrorText(err.message);
}
The problem is that the app also redirects the user when he/she finishes his/her registration to the private area like so:
useEffect(() => {
if (authUser) history.push('/dashboard');
}, [authUser, history]);
(authUser is stored in Redux and is updated inside onAuthStateChanged()) The display name won't be validated because as soon as firebase.auth().createUserWithEmailAndPassword() resolves, authUser has a value and immediately redirects the user. It's kind of annoying and would love to know whether there's a better way of organising this bit of code.
Any help is greatly appreciated!
Edit: Firestore rules:
service cloud.firestore {
match /databases/{database}/documents {
match /users/{documentId} {
allow read: if request.auth != null
allow write: if request.auth != null &&
request.resource.data.displayName is string &&
request.resource.data.displayName.size() >= 6 &&
request.resource.data.displayName.size() <= 20
}
}
}
Related
I'm really new to Firebase and Firestore database.
I have an angular project and I use the angular/fire package to communicate with firebase.
I have created a user with user/password method and the call works perfectly.
try {
const user = await signInWithEmailAndPassword(this.auth, email, password);
return user;
} catch (e) {
return null;
}
After that I would like to store some data in the firestore db and all works fine
addVideoId(storageVideoId: StorageVideoIdModel) {
const notesRef = collection(this.firestore, 'videoIds');
return setDoc( doc(notesRef, '' + storageVideoId.date), storageVideoId );
}
After that comes the problem.
I applied a rule in the firestore console:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /videoIds/{id} {
allow read, write: if request.auth != null;
}
}
}
well... if I run the addVideoId function.... IT WORKS!
The playgroud tells me that the rule is valid and no operations are permitted, but If I run the addVideoId from angular code without log in, it works anyway....
I really don't understand how to pass that auth object in the rule...
I need some help because are 2 days that I'm stuck on it and I cannot find any example or tutorial that merge authentication with database rules.
Please help
Thanks
I think you want to define where the user has permission to write to, otherwise, your users can overwrite each other's data.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{userId}/videoIds/{id} {
allow read;
allow write: if request.auth != null && request.auth.uid == userId;
}
}
}
this will allow all users to read, but only write to their own collection.
How do you keep a user logged in with Sign in with Google between reloads?
Here is my login function (Firebase):
const loginWithGoogle = async () => {
try {
const provider = new firebase.auth.GoogleAuthProvider();
const res = await firebase.auth().signInWithPopup(provider);
$user = res.user;
$isLoggedIn = true;
}
catch (error) {
console.log(error);
}
}
Although $isLoggedIn and the $user object save to LocalStorage (I'm using SvelteJS), the user does not actually stay logged in after reloading.
After reloading, admin users are no longer able to write to the database and I get the error firestore: PERMISSION_DENIED: Missing or insufficient permissions.
(Here are my firestore rules)
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read;
allow write: if (request.auth != null && (request.auth.uid == "someAdminID" || request.auth.uid == "otherAdminID"));
}
}
How would I stay logged in after reloading? Or is there some way to automatically log in again if the user had not previously logged out?
On most browser environments Firebase automatically persists the user credentials in local storage, and restores them when the app/page reloads. This requires it to make a call to the server however, a.o. to check if the account was disabled, and thus it isn't completed right away when the page loads.
To react to when the authentication state is restored (or otherwise changes), you'll want to use an auth state listener as shown in the first code snippet in the documentation on getting the currently signed in user:
firebase.auth().onAuthStateChanged((user) => {
if (user) {
// User is signed in, see docs for a list of available properties
// https://firebase.google.com/docs/reference/js/firebase.User
var uid = user.uid;
// ...
// 👈 This is where you can also query the database as the user for the first time
} else {
// User is signed out
// ...
}
});
I am currently building a vuejs web app that uses firestore from firebase. The basic structure is a user fills out an application with data and then an admin can look at that data to see if they are eligible for funding. The problem that I am encountering is that when the admin requests the information from firestore, it only ever returns an empty array.
The admin tag is assigned using this firebase cloud function:
const admin = require('firebase-admin');
admin.initializeApp();
exports.addAdminRole = functions.https.onCall((data, context) => {
//get user and add custom claim (admin)
return admin.auth().getUserByEmail(data.email).then(user => {
return admin.auth().setCustomUserClaims(user.uid, {
admin: true
});
}).then(() => {
return {
message: `Success! ${data.email} has been made an admin`
}
}).catch(err => {
return err;
});
});
My firestore structure is as follows:
users
|_user1_uid
|_info
|_user2_uid
|_info
The call I am making to get the information is this:
db.collection('users')
.get()
.then(snapshot => {
console.log(snapshot.docs)
})
As you can see, the call is supposed to get a snapshot of all of the documents I have. (example: the console.log should show that I have three users > (3) [n, n, n]). However, what is returned instead is an empty array.
My firestore rules are this:
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
allow read, write: if request.auth.token.admin == true;
}
match /users/{userId}/{document=**} {
allow create: if request.auth != null;
allow read, write: if request.auth.token.admin == true;
allow read, write: if request.auth.uid == userId;
}
}
}
Any suggestions or pointers on why my admin is unable to get the users' information would be appreciated.
Thanks!
Ok, I figured out what was happening. When I start making my documents I was using this code:
db.collection('users').doc(this.uid)
.collection('info').doc('county').set({
county: this.county
)}
What I didn't realize is that this makes the document that holds this.uid to be virtual. If I had looked a little harder I would have seen this notification on firebase:
I found a relatively simple workaround which was first creating the UID document with dummy data before adding actual info to it like so:
db.collection('users').doc(this.uid).set({
dummy: 'dummy'
})
This fixed my problem. Thanks everyone for your help! I appreciate it!
I have been looking for a way to use Authentication with Firebase's new Cloud Firestore. I know it's possible, but there is no good guide both in the Documentation and other places.
Could someone please explain how to provide an Authentication UID when get()ing data from Cloud Firestore?
Here are my security rules:
service cloud.firestore {
match /users/{userID} {
allow read, write: if request.auth.uid == userID;
}
}
And here is my current code:
var db = firebase.firestore();
var docRef = db.collection("users").doc(uid); //lets say "uid" var is already defined
docRef.get().then(function(doc) {
if (doc.exists) {
console.log("Document data:", doc.data());
} else {
console.log("No such document!");
}
}).catch(function(error) {
console.log("Error getting document:", error);
});
The structure of my database is just a "users" collection at root, then a document for each user (named after the UID). I want to get the document with the user's UID.
Of course, this gives the error "Missing or insufficient permissions," which is expected, because of the security rules.
This question may seem simple, but if anyone can find some good documentation on this, that would be great!
I was just confused about how UIDs work. I was under the impression that you have to supply a UID and an auth token to database operation.
You don't actually pass in a UID to database operations. The firebase object stores authentication state. You can simply sign in (check the Firebase docs to read how to do this), and after the action is completed (use a .then promise statement) you can simply do your operation.
Additionally, to have users not sign in every time they visit your app, you can have the `firebase object's authentication state persistent.
Your rule should be inside match /databases/{database}/documents
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userID} {
allow read, write: if request.auth.uid == userID;
}
}
}
I am building a vuejs application and I am trying to add firebase auth to the app. I used cloud firestore for a news system.
Now on the "add news" page I call
firebase.auth().signInWithEmailAndPassword("john#doe.com", "mypassword").catch(function (error) {
console.log(error.code);
console.log(error.message);
if (errorCode === 'auth/wrong-password') {
alert('Wrong password.');
} else {
alert(errorMessage);
}
});
to log the user in, or give him feedback if something went wrong.
Later I am writing data to cloud firestore like this
irestore.collection("news").doc().set({
date: today,
title: "Hello",
text: "A think I am a news!"
});
In the firestore rules I set
allow read, write: if request.auth != null;
to the news collection - so it should only grant write access to logged in users, right?
Now the thing:
If i log in with a wrong password, firebase gives back, that the password was incorrect (so we are not logged in are we?) but the data is written to firestore anyways. What did I do wrong?
Your rule request.auth != null will check if the user is signed in via any method. You can check on the client side for sign in two ways:
Sync:
// Synchronously check for current user
var user = firebase.auth().currentUser;
if (user) {
// User is signed in.
} else {
// No user is signed in.
}
Async:
// Listen for current user status
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
// User is signed in.
} else {
// No user is signed in.
}
});
If you get a user then you can expect request.auth to not be null.