I'm writing an app which has user profiles in it. The user is allowed to take a photo or choose from their photo library.
Once the photo is taken, I use this function:
Future<String> uploadFile(File image, String directory, String imageName) async {
assert(image.existsSync());
StorageReference storageReference = FirebaseStorage.instance.ref().child('$directory/$imageName');
StorageUploadTask uploadTask = storageReference.putFile(image);
await uploadTask.onComplete;
var dowurl = await storageReference.getDownloadURL();
return dowurl.toString();
}
to upload the image to firebase. The image appears in the Firebase storage area fine, and the URL is generated, and added to the user's profile as I've defined in the database.
The rules of firebase are set as follows:
Database
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if request.auth != null;
}
}
}
Storage
rules_version = '2';
service firebase.storage {
match /b/myappname.com/o {
match /{allPaths=**} {
allow read, write: if request.auth != null;
}
}
}
which I believe should allow access for any user as long as they're authenticated (they are!)
I'm uploading the URLs generated by uploadFile() to the users profile, but about 1/6 of them gives a URL which, when accessed gives the following error:
{
"error": {
"code": 403,
"message": "Permission denied. Could not perform this operation"
}
}
I'm at a loss as to what is going on here, as the rest of the code works fine, and the 'choice' of file which causes the problem seems completely random.
The URLs generated have different token IDs at the end of each, but I'm not generating them - they're coming from the getDownloadURL() function. I'm keen to stress that the process is the same for each file, but for some reason, randomly, one of the generated URLs doesn't work.
Any help would be much appreciated.
Related
I am struggling with Firestore rules to allow access to some resources in a subcollection.
I have some requests documents, that may present a sub-collection named status. My current rules are something like that:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// I use it to check if user is signed in
function userSignedIn() {
return request.auth != null;
}
match /requests/{requestId} {
// I use it to check if user is the owner of the request
function userCanAccess () {
return userSignedIn() && request.auth.uid == get(/databases/$(database)/documents/requests/$(requestId)).data.userId;
}
// allow read to logged user who own request
allow read, update: if userCanAccess();
// anyone can create a new request
allow create: if true;
// no one can delete the request
allow delete: if false;
match /status/{statusId} {
// allow read and update of status to logged user who own request
allow read, update: if userCanAccess();
// anyone can create the status
allow create: if true;
// no one can delete the status
allow delete: if false;
}
}
}
}
My very first way to check if user had access was by request.auth.uid == resource.data.userId, however it only works for requests documents, not for status documents since they do not have the userId field.
I'm using flutter to make the request with the code:
FirebaseFirestore.instance
.collection('requests')
.where('userId', isEqualTo: user.uid)
.orderBy('requestedOn', descending: true)
.snapshots()
.listen((snapshot) {
// Here I read requests and their status
});
However I'm getting Error: [cloud_firestore/permission-denied] Missing or insufficient permissions.. How can I solve (or debug) it? Using rules playground in firestore, with the exact same uid, I am able to complete the reading.
I'm wondering if the get() call may be causing problems. Try this:
function userCanAccess () {
return userSignedIn() && request.auth.uid == requestId.data.userId;
}
I use Firebase's Firestore. I have a collection called "root" which has an item called "db".
I want to allow read and write access to everybody on exactly that item.
This is my Rule:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if
request.path == 'root/db';
}
}
}
This rule was there by default, except this line request.path == 'root/db'; which is my custom. I tried to set a timestamp rule and it works. But this by path does not work.
This is my request from the browser:
const ref = doc(dbApi, "root", "db");
console.log(ref.path);
let db: Database;
try {
db = (await getDoc(ref)).data() as Database;
} catch (err) {
console.log(err);
throw err;
}
ref.path logs root/db and err logs FirebaseError: Missing or insufficient permissions.
(Using "firebase": "9.6.4" javascript package in the app)
I've also tried some variations on this https://firebase.google.com/docs/reference/security/storage#path
Rather than use the catch all (/{document=**}) and then filter, just filter at the match statement instead:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /root/db { <-- change it here
allow read, write: if true;
}
}
}
While this works, you should further restrict the rules to limit the shape of this document. If your database is all based on a single document, consider the RTDB instead as it provides finer grain controls over the data in the database.
I am trying to upload a profile photo as a logged in user with the following however I get error
E/flutter (32619): [ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: [firebase_storage/unauthorized] User is not authorized to perform the desired action.
My code
User? user = await authenticationService.getCurrentUser();
// Create a Reference to the file
Reference ref =
FirebaseStorage.instance.ref().child(user!.uid).child('/profile.jpg');
final metadata = SettableMetadata(
contentType: 'image/jpeg',
customMetadata: {'picked-file-path': s!.path});
UploadTask uploadTask = ref.putFile(File(s.path), metadata);
var result = await uploadTask;
print(result);
My rule
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read;
allow write: if request.auth.uid == userId
&& request.resource.contentType.matches('image/.+');
}
}
}
What am I missing here ?
I understand that you want to upload a file to the following reference
FirebaseStorage.instance.ref().child(user!.uid).child('/profile.jpg');
and check, in the security rules, that the id of the user executing the upload corresponds to the value of user!.uid.
In your security rules, you have a check as request.auth.uid == userId but nowhere in the rules you define what is userId. You need to use a wildcard, as follows:
service firebase.storage {
// The {bucket} wildcard indicates we match files in all Cloud Storage buckets
match /b/{bucket}/o {
match /{userId}/{imageId} {
allow read;
allow write: if request.auth.uid == userId
&& request.resource.contentType.matches('image/.+');
}
}
}
If you only upload images named profile.jpg in this "folder", you could add a check like imageId == 'profile.jpg' but you could also define the path as:
match /{userId}/profile.jpg {
allow read;
allow write: if request.auth.uid == userId
&& request.resource.contentType.matches('image/.+');
}
I am trying to get images from firebase firestore, while trying to get image download url there is this exception :
W/StorageUtil(10206): no auth token for request
(similar questions with this error not solve the problem)
Although my security rules allow read and write I getting this error still.
Any ideas what is the problem with this?
Code for getting image url :
static Future<dynamic> loadImage(BuildContext context, String image) async {
return await FirebaseStorage.instance.ref().child(image).getDownloadURL();
}
Calling this loadImage function :
Future<Widget> getImage(BuildContext context, String imgName) async {
Image image;
await FireStorageService.loadImage(context, imgName).then((value) {
print(value);
image = Image.network(value.toString(), fit: BoxFit.scaleDown);
return image;
});
}
Calling this getImage function :
child: FutureBuilder(
future: getImage(context, "/images/test1.jpg"),
...
)
My firebase storage rules :
rules_version = '2';
service firebase.storage {
match /images/{imageId} {
allow read,write;
}
}
Storage Rules ss:
My firebase store currently :
Navigate to Firebase console
In Sidebar under Develop open Storage
Open Rules tab replace the rule on your console with the rule below:
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write
}
}
}
And Publish
Done
(2022): Recently, I solved with following the Firestore basic security rules (for authenticated user):
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write: if request.auth != null;
}
}
}
You can see this in their website: https://firebase.google.com/docs/rules/basics?authuser=1#cloud-storage_1
I do not suggest that using allow read, write; directly. If your application has a authentication, then you need to check if auth is null or not.
I fixed it by changing the rules.
Go to Firebase project>Storage>Rules.
By default, you are not allowed to upload on Firebase so you have to change that.
Default rules
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write: if false;
}
}
}
Change allow read, write: if false; to allow read, write;
Go to Project Settings -> App Check -> Products
Change the Storage from Enforce -> Unenforced
This works for me by changing the rules in firebase.
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write
}
}
}
I get this error :
E/StorageUtil(21342): error getting token java.util.concurrent.ExecutionException: com.google.firebase.internal.api.FirebaseNoSignedInUserException: Please sign in before trying to get a token.
W/NetworkRequest(21342): no auth token for request
When I execute this code from https://stackoverflow.com/a/50877590/9272698 (to get the download url from an image with Firestore Storage) :
Future _getImageUrl() async {
final ref = FirebaseStorage.instance.ref().child('lake');
var url = await ref.getDownloadURL();
return url;
}
This is not due to my firestore rules since, I enabled it to be public like this :
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write;
}
}
}
Moreover I don't think it is due to internet connection since I'm able to load fields from Firebase Cloud Database.
Do you have a solution? Thanks a lot
I managed to get my url images.
Here's how :
Add SAH-1 certification to your project firebase and upload new google-service file to your project
Make your users sign-in anonymously (and enable it in your firebase project) before calling .getDownloadUrl()