Swift and Firestore security rules, firebase security - ios

I'm new developer working on my first Firestore app. I've changed the rules on Firestore to make the data more secure for user, but it's not allowing read/write.
This is the key line and I don't know how to configure it specific to my app -
match /some_collection/{userId}/{documents=**} {
I don't know if I change the "some_collection" to my collection name or if some_collection in that sense is an actual wildcard type of parameter itself.
Also, do I need to pass in the userID somehow from my swift application to Firestore? where is userID coming from in this line? I'd prefer to make the rule such that only the user who created the data can read/write. I believe this block is to allow any authenticated user, so I'm just trying to explore each step.
service cloud.firestore {
match /databases/{database}/documents {
// Allow only authenticated content owners access
match /some_collection/{userId}/{documents=**} {
allow read, write: if request.auth != null && request.auth.uid == userId
}
}
}

Addressing your questions:
This is the key line and I don't know how to configure it specific to my app.
match /some_collection/{userId}/{documents=**}
I don't know if I change the "some_collection" to my collection name or if some_collection in that sense is an actual wildcard type of parameter itself.
In the line above "some_collection" is not a firestore wildcard and you need to replace some_collection with the actual value of your collection.
Also, do I need to pass in the userID somehow from my swift application to Firestore?
Yes and it is expected that before reading or writing to/from firestore:
You had already created and configured the firebase object.
firebase.initializeApp({
apiKey: '### FIREBASE API KEY ###',
authDomain: '### FIREBASE AUTH DOMAIN ###',
projectId: '### CLOUD FIRESTORE PROJECT ID ###'
});
You had already authenticated your users with firebase auth.
firebase.auth().signInWithCustomToken(token)
.then((user) => {
// Signed in
// ...
})
.catch((error) => {
var errorCode = error.code;
var errorMessage = error.message;
// ...
});
Passing the userId is done by the firebase object when you call db. collection(“col123”).add or any other method. If you look at how firestore is initialized:
var db = firebase.firestore();
You will see its dependency with the firebase object.
where is userID coming from in this line?
The userID is coming from the firebase object.
I believe this block is to allow any authenticated user, so I'm just trying to explore each step.
Yes, the last rules allow any authenticated user to read and write from/to the subcollections/documents wildcard {userId}.
Lastly it is also expected that there is some naming consistency in the ids of your firestore documents or subcollections.
This means when you create firestore documents, use the firebase.auth.uid as the document id.
Otherwise, the rule from above will fail because the value behind {userId} is not equal to firebase.auth.uid of the logged user.
To achieve the latter, you can refer to this answer.
I highly recommend you have a look at this video(from the firebase channel) since it elaborates more on the core concepts of firestore security rules.
I hope you find this useful.

Related

Firebase: Maintain a Username Directory

I am creating a Social app and want to track if a username already exists or not. The username list is supposed to grow in future and the way I was doing it now was a key value pair of <string,bolean> like this:
name1: true,
name2: true
all the above data was to be stored in a single document and whenever I want to see if a user exists I would call this document and check accordingly. But here's the problem, firebase max document size is 1MBs and as the users grow this can be problematic, so wanted to know from firebase experts that what's the best way to solve this use case in firestore or realtime database but since I need to query exists maybe realtime db won't suit that well.
Note that I don't want any of firestore querying capabilities but only to check if an entry exists in the record or not and if not just add it.
The Realtime Database doesn't have a 1MB limit (since it has no concept of a document, and everything is just a tree of JSON), so I'd typically use that for the index of user names.
Checking whether a name exists is pretty simple there too, and in JavaScript would look something like:
const usernames = firebase.database().ref('usernames');
usernames.child('name1').once((snapshot) => {
if (snapshot.exists()) {
...
}
});

use variable names in firebase rules

I have an existing firebase project. Every user can save data to a 'folder' with the same name as the userid of the user.
I use the rules like this
"$uid":{
".read":"auth.uid == $uid",
".write":"auth.uid == $uid"
}
But now, the need of collaboration between multiple users has risen.
So, i can't use this rule anymore.
I made a new 'table' in firebase, called 'users' where i can save the users id and the according variable workingDir.
Of course, i would like to keep my database secure, but I don't have a clue to setup the firebase rules for that and use this workingDir variable.
Any help is appreciated

How to flag and report user in firebase firestore database?

I have application which have multiple users, one of the major thing left is to block and report users in firebase.
I am trying to look for the solution for the same by googling for it, but till now not any particular success.
I would like to know how I can achieve that. Please guide me for that,
and how the firestore security rules should be to achieve the same?
The typical approach is to have a collection that contains the blocked users, with one document for each blocked user and with the ID of that document being the UID of that user.
With that structure in place, your security rules can check for the existence of such a document and then block the user.
There's a great example of this in the blog post 7 tips on Firebase security rules and the Admin SDK (it's tip 7). The rules from there:
service cloud.firestore {
match /databases/{database}/documents {
function isBlackListed() {
return exists(/databases/$(database)/documents/blacklist/$(request.auth.uid))
}
// Collections are closed for reads and writes by default. This match block
// is included for clarity.
match /blacklist/{entry} {
allow read: if false;
allow write: if false;
}
match /posts/{postId} {
allow write: if !isBlackListed()
}
}
}

Firebase client side UID and Firebase function UID do not match

I am trying to use Stripe with Firebase and have followed along the linked Firestripe example project. When a user signs up for the app, the following function is triggered in Firebase which creates a Customer object in Stripe.
exports.createStripeCustomer = functions.auth.user().onCreate(event => {
const data = event.data;
return stripe.customers.create({
}).then(customer => {
return admin.database().ref(`/stripe_customers/${data.uid}/customer_id`).set(customer.id);
});
});
The last line in the function above is supposed to write the Stripe customer ID as a child of the Firebase UID in the database which it does successfully.
Now on the client side (iOS - Swift) when trying to write to the same authenticated user by obtaining their UID using the following:
let userID = Auth.auth().currentUser!.uid
Database.database().reference(fromURL: "https://REDACTED.firebaseio.com/").child("stripe_customers").child(uid).child("sources").setValue([
"token": token.tokenId,
])
it creates a whole different child under /stripe_customers/ meaning the UIDs don't match...
How do I go on obtaining the real UID of the user from the client side? Or am I doing something wrong? Any help or insight would be appreciated.
What you're doing in the first code sample is storing data under Stripe customer ID while in your second code sample, you're attempting to retrieve data based on your Firebase UID.
What you want to do is store user objects named as your Firebase UID, then store your Stripe ID in a child object. Finally, query for your Firebase UID object not its Stripe ID child.
Furthermore, I can't see you using your let userID = Auth.auth().currentUser!.uid
anywhere in your query. If you declare a constant that you're not using for your example, it's better to leave it out.
Let me know if you need more help.
Also, have a look at Firebase's new Cloud Firestore.
It's still in beta, but it's extremely powerful compared to Firebase Realtime Database.

Allow Firebase Storage Bucket to be accessed only by some users

I have a main "folder" in my storage directory called users-projects and then I create folders for every users' projects. I want to allow the users to access only their projects and the projects they are invited in, like a Dropbox or Google Drive folder with collaborators.
In the Documentation they says:
Include group information (such as a group ID or list of authorized
uids) in the file metadata
So here is my questions:
Can I do this directly with the folder?
How can I store the list of authorized uids?
I am programming an iOS app in Swift.
Here is my actual code for the Rules:
service firebase.storage {
match /b/on-team.appspot.com/o {
match /{allPaths=**} {
allow read, write: if request.auth != null;
}
match /users-projects {
match /{projectId} {
allow read, write: if ?????
}
}
}
}
Here is the official documentation: https://firebase.google.com/docs/storage/security/user-security .
I think the correct static rule would be :
allow read, write: if request.auth.uid == 'a-user-id' || request.auth.uid == 'another-user-id' || ...
But I guess you are looking for dynamic rules :)
For the owners it's quite simple to setup a dynamic rule with the folder name :
match /users-projects/{projectId}/{userId} {
allow read, write: if request.auth.uid == userId
For more complex cases like invited users, you can try using the custom metadata to store invited uids in the file, and match them against user id accessing that ressource, example rule:
allow read: if resource.metadata.invited.matches(request.auth.uid);
The custom metadata values can only be strings, so I suggest you store them as coma-separated value so you can edit them easily, and at the same time use a simple match in the access rule.
Note: this is only scalable while invitedUids.join(',') length is shorter than maximum length of custom metadata values. (I don't know that value). If your app is not built to accept hundreds of invited users, it should be ok, otherwise you might need to setup a server-side access mecanism which build a unique download link for each invited user, instead of relying on simple rules.
Also, I don't think you can use token groupId value to enforce access security in your case (as depicted in the docs), because you have a many-to-many relationship between users and folders/files. (users will not belong to only one group)
So, to answer your questions:
The resource object in the rules only apply to files, if using metadata to enforce access, they need to be updated on each file in the folder if they share the same access rules
The metadata is just a string->string key-value store, you just need to store user ids as a string in a arbitrary non-reserved key, as explained above.
Store individual project in a directory using their userid
/users-projects/user_id/project1
/users-projects/user_id/project2
service firebase.storage {
match /b/on-team.appspot.com/o {
match /{allPaths=**} {
allow read, write: if request.auth != null;
}
match /users-projects/{user_id} {
allow read, write: if request.auth.uid == user_id
}
}
}

Resources