Firebase Storage security rule error- User does not have permission - firebase

I am authenticating to firebase using custom tokens. I want to restrict only the current user to have access to write and delete the image and grant read access to everyone.
below code is part of my swift code related to storage
let filePath = REF_STORAGE.child("/"+(FIRAuth.auth()?.currentUser?.uid)!+"/profilepic.jpeg")
let metaData = FIRStorageMetadata()
metaData.contentType = "image/jpeg"
And my current firebase storage rule is as shown below
service firebase.storage {
match /b/test-123456789.appspot.com/o {
match /{uid}/{allPaths=**} {
allow read: if request.auth != null;
allow write: if request.auth.uid == uid;
}
}
}
When i try to upload image i get the following error
User does not have permission to access gs://test-123456789.appspot.com/MYAUTHUID/profilepic.jpeg.
But if i change the storage rule to below storage rule, i am able to upload the image and the profilepic is stored under /gs://test-123456789.appspot.com/123456789/profilepic.jpeg
service firebase.storage {
match /b/test-123456789.appspot.com/o {
match /{allPaths=**} {
allow read: if request.auth != null;
allow write: if request.auth != null;
}
}
}
Kindly let me know how to fix this issue.

Does your {uid} contain any special characters? (e.g '+' sign)
Note: Firebase Support replied to me that this issue is under investigation, following a bug report I sent them 3 days ago: I discovered that calling {userid} doesn't work for me in Firebase Storage Rules if it contains the '+' sign.
I use phone numbers in E.164 format (e.g: "+97234567899").
I tried several run tests, and they clearly showed there's an issue with the value returned from {uid}:
request.auth != null; - Worked
request.auth.uid != null; - Worked
request.auth.uid == "+97234567899"; - Worked
request.auth.uid == uid; - Didn't work
However, I found a way to bypass this issue. This is what I came up with:
Solution
resource.name.split('/')[position]
Since {uid} represents a folder name, you can extract its value by manipulating the full path string of the file using resource.name, splitting it by ('/') and then selecting the relevant hierarchy.
For example: if this is your path: /Images/Profiles/{uid}/profile.jpg, then extract the uid like this: resource.name.split('/')[2]
In your code sample, I would do this:
service firebase.storage {
match /b/test-123456789.appspot.com/o {
match /{uid}/{allPaths=**} {
allow read: if request.auth != null;
allow write: if request.auth.uid == resource.name.split('/')[0];
}
}
}
Edit (July 28th, 2017)
Firebase Support has replied to my report a month ago, and the issue has been identified & now being taken care of.
However, in the meantime, if you wished to authenticate by phone number (like I did) - we shouldn't be using Custom Tokens anymore, because Firebase now supports such authentication.

For anyone still having this problem: Updating your firebase security rules to the following did the trick for me.
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write: if request.auth.uid != null;
}
}
}

Related

Firebase, insecure rules

I recently received an email from firebase telling me that the rules of my database (Firestore) are insecure so I changed them to the following:
rules_version = '2';
service cloud.firestore {
match / databases / {database} / documents {
match / {document = **} {
allow read: if true;
allow write: if request.auth.uid != null;
}
}
}
Before I had these rules:
allow read, write: if true;
After making the change, the emails keep coming back and I don't know what else to do.
I already tried several of the following options given in this link but none of them works for what I need.
https://firebase.google.com/docs/rules/insecure-rules#firestore
I need authenified users to be able to read and create content. So use the rules that I put above.
I saw that in the email they send me it says that people can modify my database, is this referring to from the app, or does it mean that they can hack me or something?
Because the end of my app is that users can create content.
But I don't want someone to hack into my database and delete everything, is that possible?
Thanks
The emails are because the rules aren't really stringent. You should probably be using the following rule, that:
Allows unauthenticate users to read data
Allows authenticated users to create entries
Allows to update & delete entries that are only owned by themselves and not of others.
service cloud.firestore {
match /databases/{database}/documents {
// Allow public read access, but only content owners can write
match /some_collection/{document} {
allow read: if true
allow create: if request.auth.uid == request.resource.data.author_uid;
allow update, delete: if request.auth.uid == resource.data.author_uid;
}
}
}
Read this article for better understanding. You can also check when firestore flags rules as insecure over here. More importantly, this is the point to be emphasized.
Remember that Firebase allows clients direct access to your data, and
Firebase Security Rules are the only safeguard blocking access for
malicious users. Defining rules separately from product logic has a
number of advantages: clients aren't responsible for enforcing
security, buggy implementations will not compromise your data, and
most importantly, you're not relying on an intermediary server to
protect data from the world.
Sample rules:
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userDoc} {
allow read: if true
allow create: if request.auth.uid == request.resource.data.id;
allow update, delete: if request.auth.uid == resource.data.id;
}
match /posts/{postDoc} {
allow read: if true
allow create: if request.auth.uid != null;
allow update, delete: if request.auth.uid == resource.data.user_id;
}
match /comments/{commentDoc} {
allow read: if true
allow create: if request.auth.uid != null;
allow update, delete: if request.auth.uid == resource.data.user_id;
}
}
}
This case is mentioned in the documentation. Any authenticated user can write to your database and that also involves deleting data. You are using a recursive wildcard which gives them access to complete database.
Instead try rules that allow users to write their own documents only or something similar.
rules_version = '2';
service cloud.firestore {
match / databases / {database} / documents {
match /collectionName/{docId} {
allow read: if true;
allow write: if request.auth != null && request.auth.uid == docId;
}
}
}
Above example will allow users to edit documents where document ID is equal to their UID only.
If you wish to allow selected users to write (such as admin) then you can add a field namely admin and set it to true in user's document in users collection. Then you can read the document data as shown:
match /collection/{document} {
allow write: if get(/databases/$(database)/documents/users/$(request.auth.uid)).data.admin == true;
allow read: true;
}

Access database reference in firestore rules

I am struggling to access document references in the firestore rules. My database looks like this. (Simplified for brevity):
curriculum
session1
roles
admin
--- canEditContent
user
--- canEditContent
users
userid
--- role
roles/admin <document reference>
I want to access the admin permissions based on the document reference.
I have tried several ways however can't seem to get anywhere. This is my code so far
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
function isSignedIn() {
return request.auth != null;
}
function getUser() {
return get(/databases/$(database)/documents/users/$(request.auth.uid));
}
function getUserRole() {
let role = get(/databases/$(database)/documents/users/$(request.auth.uid)).data.role;
return get(role);
}
match /curriculum/{curriculum} {
allow write: if isSignedIn() && getUserRole().data.canEditContent;
}
match /users/{userId} {
allow read, update, delete, write: if request.auth != null && request.auth.uid == userId;
allow create: if request.auth != null;
}
}
}
I have tried many ways and can't seem to solve it.
Thanks in advance
EDIT
Added screenshots below of collections
Users collection showing role as a document ref to a role document in the roles collection.
Roles collection
I can see two issues in your rules:
get needs the full document path, so your function getUserRole wont work. Try this instead:
function getUserRole() {
let role = getUser().data.role;
return get(path("/databases/" + database + "/documents/" + role));
}
Your rule uses the role canEditContent but the data you show uses editContent, is that on purpose?
As already mentioned please provide the complete set of data & query & rules, here we cant see the query you are using. Also note that you can use the Firestore emulator to get information on what rule is failing and where.

Firebase database rules: cant read an another collection

can you help me please?
I have these rules
service cloud.firestore {
match /databases/{database}/documents {
match /locations/{document=**} {
allow read, write;
}
match /Users/{userId} {
allow read: if request.auth.uid != null && request.auth.uid == userId;
allow write: if request.auth.uid != null && request.auth.uid == userId;
}
}
}
So when I am trying to access the 'locations' collection via Firebase Simulator I get an error:
Simulated data access denied
Here is a
screenshot
The path in the Location field of the Simulator is
/databases/(default)/documents/locations
I CAN access and write a data to the Users collection from my app (when authorized of course) but the problem is that I need to allow the access to the Locations collection without any permissions. I can't read a 'locations' collection
Any advice highly welcome
The path you are specifying is incorrect.
It should be only locations/documentID instead of databases/(default)/documents/locations/documentID.
/databases/(default)/documents is basically a tooltip of where you are.

Firestore security rules, get() and the allow create operation

I've made an app where users can post records to the firestore database. I'm now at the point where I'm implementing security rules but I'm struggling to find a solution.
My code looks like this
docRef.get().then(function(doc){
if(doc.exists){
docRef.set(//data to set here)
} else {
docRef.update(//data to update here)
}
.catch((error) => {
alert('Error' + error);
})
My rules are currently set to allow the user to create an object if they are authorised, and only allow an update if the user id on the record matches themselves.
service cloud.firestore {
match /databases/{database}/documents {
match /users/{user}{
allow create: if request.auth.uid != null;
allow read, write: if request.auth.uid == resource.id;
}
match /equipment/{document} {
allow create: if request.auth.uid != null;
allow read, update: if request.auth.uid == resource.data.user;
}
I think the problem lies in that I'm trying to get the object prior to creating it. However I need to perform these checks prior to writing to the document.
Am I right in thinking this the problem? and if so is there a solution I can implement.
Thanks
I'm still working on my rules, but I hope this helps.
If you're trying to make sure the user is editing something they own and their uid is the same as the document id....
function isOwner(userId) {
return request.auth.uid == userId
}
match /users/{userId} { //this is the document
allow write: if isOwner(userId);
}
If you're trying to make sure they are the creator of a document:
match /equipment/{documentId} {
allow create: if request.auth.uid != null;
allow read, update: if get(/databases/$(database)/documents/equipment/documentId).data.userId == request.auth.uid;
}
https://firebase.google.com/docs/firestore/security/rules-conditions?authuser=0
You need to use the get function to retrieve a document you're interested in. That function returns data that has the related fields you can compare to, in this case, whatever field name you stored the user ID on so you can compare it to their auth.uid.
I think you should change the 'create' rule as following:
allow create: if request.auth.uid != null &&
request.auth.uid == request.resource.data.id;
This is what documentation says: "if your ruleset allows the pending write, the request.resource variable contains the future state of the document."

Firestore security rules allow user access to their data

I want to write a rule like this:
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId}/{document=**} {
allow read, write: if request.auth.uid == userId;
}
}
}
That is, I want to allow all read and write operations to all of a user's data if that user is the authenticated user.
Unfortunately that does not work. I get an error specifying that I don't have permission to access the data.
This code solved the problem:
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
allow read, write: if request.auth.uid == userId;
match /{document=**} {
allow read, write: if request.auth.uid == userId;
}
}
}
}
I think it's because you need to grant access to /users/{userId}, as well as /users/{userId}/{anyDoc=**}.
From the official documentation:
Another common pattern is to make sure users can only read and write
their own data:
service cloud.firestore {
match /databases/{database}/documents {
// Make sure the uid of the requesting user matches name of the user
// document. The wildcard expression {userId} makes the userId variable
// available in rules.
match /users/{userId} {
allow read, update, delete: if request.auth.uid == userId;
allow create: if request.auth.uid != null;
}
}
}
If your app uses Firebase Authentication, the request.auth variable
contains the authentication information for the client requesting
data.
Please note that this only works if you have made a 'users' table in your database and populated it with users that are known to your application (possibly copied from FireBase's users section Authentication/users in the webconsole).
AfaIcs you cannot refer to the Firestore authenticated users table this way. I found this lack of information very confusing since all examples and Firestore documentation make you believe that you can access the users created through the webconsole this way, invariably resulting in an 'access denied' messages when trying to read from a users table...

Resources