This question already has answers here:
Cloud Firestore: Enforcing Unique User Names
(8 answers)
I want to make unique usernames in firebase/firestore
(1 answer)
Closed 7 days ago.
My data of usercollection looks like this:
username: "johndoe"
email: "john.doe#test.com"
firstName: "John"
lastName: "Doe"
loginUid: "...."
and this is my rule:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if request.auth != null;
}
}
}
I just want to read usernames from firestore, if not authenticated to check before registration, if username is already taken.
Is this possible. If not, is there another way, like creating a view of all usernames, which i could read from?
I understand that you want to check if a user document already exists before creating a new one.
A straightforward approach is to maintain a collection of documents with the user name as Document ID and set a security rule like the following one:
match /usersList/{userName} {
allow create: if !exists(/databases/$(database)/documents/usersList/$(userName));
allow update, delete: if false;
}
This collection (usersList) can be the collection where you store the users. But it could also be a collection just dedicated to the verification and the users are contained in another collection.
In this last case you would use a batched write to create the two docs, since batched writes are atomic: If the doc in the usersListRef collection already exists, the new user doc will not be created.
const userName = '....';
// Get a new write batch
const batch = writeBatch(db);
const usersListRef = doc(db, 'usersList', userName);
batch.set(usersListRef, { ... });
const userRef = doc(db, 'users', userName);
batch.set(userRef, { ... });
// Commit the batch
await batch.commit();
Related
I have users and profiles collection in the cloud firestore as shown in the image below. I am storing user details in user collection with document ID as a firebase UID of the user so that only a particular user can update and read its own data and not other users. I have security rules for users collection something like in this answer Disable querying collection in Firebase Cloud Firestore with rules
Now my profile collection should be a public collection of documents such that these IDs can be referenced to read details of profiles but whenever user update their data in users collection it should also be reflected in the profile document if the common fields are updated. How can I achieve this?
P.S: In user document fields are like name, age, payment details and in profile it's like name and age only.
Your Functional Requirement:
Whenever user update their data in users collection it should also be
reflected in the profile document if the common fields are updated.
You can use a batched write, as follows:
// Get a new write batch
var batch = db.batch();
var userId = '......';
var commonData = {
name: '...',
age: ...
}
var userRef = db.collection('users').doc(userId);
batch.update(userRef, {paymentDetails : "...", ...commonData});
// We use the spread syntax, see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals
var profileRef = db.collection('profiles').doc(userId);
batch.update(profileRef, commonData);
// Commit the batch
batch.commit().then(function () {
// ...
});
In terms of write security rules, you would define them as follows:
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
allow write: if request.auth.uid == userId;
allow ....
}
match /profiles/{userId} {
allow write: if request.auth.uid == userId;
allow ....
}
}
}
I am wondering if it is safe to store sensitive user data in collections of documents (see structure below) and using the Firestore.rules (see rules below) to gate specific users from accessing a collection inside a document? If I run a get instruction on a user document, will the private collection be accessible? Or is it only accessible if I call a get instruction on the address document (for example)?
Sorry if this is up somewhere on the web, but I couldn't find a definitive answer and this is somewhat important to the integrity of my app. I wanted to get a good idea before I restructure my backend.
Firestore Structure:
(collection) users: {
(document) ID1234567890: {
(collection) private: {
(document) address: {
line1: "",
line2: "",
city: "",
state: "",
zip: "",
country: "",
},
(document) billing: {
ssnLast4: "",
externalAccount: {},
},
(document) signatures: {
...
}
}
id: "",
userName: "",
firstName: "",
lastName: "",
avatarUrl: "",
...
phone: "",
email: "",
}
}
Firestore.rules:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
function isSignedIn() {
return request.auth != null;
}
// Users collection //
// TODO: does this match give access to all sub collections?
match /users/{userId} {
function isTrueUser() {
return resource.data.id == request.auth.uid;
}
// Anyone can create a user
allow create: if true;
// Only signed in users can read or list user profile data
allow read, list: if isSignedIn();
// Only that user can update their profile data
allow update: if isSignedIn() && isTrueUser();
// No one can delete a user
allow delete: if false;
}
match /users/{userId}/private/{documentName} {
function isTrueUser() {
return userId == request.auth.uid;
}
// Only that signed in user can create, update, read, or list their private documents
allow create, update, read, list: if isSignedIn() && isUserOwner();
// No one can delete a private document
allow delete: if false;
}
}
}
Read operations in Firestore are shallow. So reads on a parent document don't automatically read the data from subcollections. You will need to do a separate read operation for each subcollection.
Having read access to a parent document does also not automatically mean that you can read the subcollections of that document. You can secure access to the subcollections in security rules.
Your current rules only matches documents in the /users collection:
match /users/{userId}
To make it also match the subcollections (which you don't want to do), it'd have to be:
match /users/{userId=**}
Also see the Firebase documentation on securing hierarchical data, specifically the section on recursive wildcards.
I'm having a bit of trouble trying to configure my Cloud Firestore rules.
I'm trying to access a field inside a document, inside a collection... Like this
Future<void> fetchAndSetProducts([bool filterByUser = false]) async {
final filterString = filterByUser
? Firestore.instance.collection('products').getDocuments()
: Firestore.instance
.collection('products')
.where('creatorId', isEqualTo: userId)
.getDocuments();
try {
QuerySnapshot prodSnap = await filterString;
if (prodSnap == null) {
return;
}
'creatorId' is a field within database/products/{productId}
I want to distinguish between users and only allow them to update, and delete files they've created within database/products/... , but I also want them to be able to view all the documents inside of /products/...
the bool I have set up for fetchAndSetProducts is what I'm hoping to use to filter some of the information app side, e.g. only allowing using to view certain products (ones containing their userId). I'm not sure if I also needs to set up indexing on "products", but I have done already just in case..
So, I want to lock down all files that weren't created by a user.
I thought it would be something like this:
service cloud.firestore {
match /databases/{database}/documents {
match /products/{productId}/documents{
allow read: if resource.data.creatorId == request.auth.uid;
}
}
}
buuut that doesn't work, and nor does my app-side code for filtering by user..
If you want to match all documents in the products collection, it would look like this:
service cloud.firestore {
match /databases/{database}/documents {
match /products/{productId} {
allow read: if resource.data.creatorId == request.auth.uid;
}
}
}
Notice that I removed the "documents" from the end of the second match.
These are the rules for saying only a logged in user can access a document named with their user id in realtime db. I'm asking how to do this in Firestore, except the name of the documents isn't the userid, but the username (which is the first part of the email)
I have seen this done before when the document names are the uid, but my documents names are the username of the user, and the username is stored as the part before the # in the authentication email.
For example, I have these users:
john#gmail.com
jane#gmail.com
and my firestore is:
collection: users
john
jane
Only a user logged in as john#gmail.com should be able to write to the john document in users. I couldn't figure out how to get the email out of the auth variable.
Is there a way to do this without renaming my documents to user id?
I found out you can get the email of the user with request.auth.token['email']. I'm able to just use this for allowing anyone to read, only people who aren't authenticated to create, and for updating and deleting user can only update/delete their own document (with emails):
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{email} {
allow read;
}
match /users/{email} {
allow update, delete: if request.auth.token['email'] == email;
allow create: if request.auth.uid != null;
}
}
}
I want to implement role based restrictions in my application. I have the user role information in one collection documents. Now I want to write rules to restrict different Write, Update operation on other collections of the database.
Since I am using Firestore database with React-Native I only pass the respective collection's document info while inserting/updating. So how can I pass the user's role information along with this so that my rule gets authenticated and that data doesn't go into other collection.
One example depicting above scenario:
/collection1/document1
{
prop1: value1,
prop2: value2,
role: "WRITE"
}
/collection1/document2
{
prop1: value1,
prop2: value2,
role: "READ"
}
Now consider that current logged in user is document2.
I have another collection:
/collection2/doc1
{
userRef: document1, //this is id of document1 from collection1
...
}
I want to configure firestore rule for collection2 that if request has come from user with role="WRITE" then only allow it to isert/update documents.
Have read many articles and ways but any of them does not satisfy this use-case.
Any help would be appreciated.
To make the rules easier to read, you can create a function that gets the user role, then use in the conditions. This is how the firestore.rules file would look like :
service cloud.firestore {
match /databases/{database}/documents {
match /collection2/{doc} {
allow read: if getUserRole() === 'READ' || getUserRole() === 'WRITE';
allow create: if getUserRole() === 'WRITE';
allow update: if getUserRole() === 'WRITE';
}
function getUserRole() {
return get(/databases/$(database)/documents/collection1/$(request.auth.uid)).data.role;
}
}
}