'Getting 403 Forbidden error when trying to load image from Firebase Storage

I'm using firebase storage to store and load images for users on my android app. All users must be authenticated before using it. Occasionally, some user profile images are not showing and throwing "403 Forbidden" error. Those images were displaying before, I'm not sure why they stop working. i'm using the following rules on my firebase storage:

 service firebase.storage {
   match /b/<storage_address>.appspot.com/o {
    match /{allPaths=**} {
     allow read: if request.auth != null;
     allow write: if request.auth != null;
    }
  }
}

if i change the read rule to allow read; all the images are working properly. This is my display method:

Picasso.Builder builder = new Picasso.Builder(this);
                builder.listener(new Picasso.Listener()
                {
                    @Override
                    public void onImageLoadFailed(Picasso picasso, Uri uri, Exception exception)
                    {
                        exception.printStackTrace();
                    }
                });
                builder.build().load(URL).into(imgView);

The image URL looks something like this:

https://firebasestorage.googleapis.com/v0/b/<storage_address>.appspot.com/o/images%2Fprofile%2Fphoto%2F36805?alt=media&token=e62ec151-3aaf-4e5c-aefb-1b3c93828684

Could it be something to do with the token?



Solution 1:[1]

Probably comes too late, but maybe helpful for someone else. I had the same issue, my problem is that filenames in firebase storage are unique. If you upload a new file with the same filename it will override the old one and generate a new token for it, making the old url obsolete. You should generate a unique filename for your uploaded file and then it should be fixed.

Solution 2:[2]

If you are just testing Storage you might not have implemented Authentication. By default Storage expects uploading user to be authenticated. To by-pass that, in the Storage->Rules write this :

service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write: if request.auth == null;
    }
  }
}

Worked like a charm for me. Have fun using Firebase

Solution 3:[3]

You get the following error if the extension of your file is either incorrect or missing:

 {
    "error": {
    "code": 403,
    "message": "Permission denied. Could not perform this operation"
    }
 }

In the image URL you provided there is no extension for the image after the image name.

I have tested with one of my urls, this works (you can test): https://firebasestorage.googleapis.com/v0/b/training-a0a3e.appspot.com/o/penguin-56101_640.jpg?alt=media&token=8dfb913d-e2f3-4956-bd3a-2c3746d0d6d3

However when the .jpg is removed: https://firebasestorage.googleapis.com/v0/b/training-a0a3e.appspot.com/o/penguin-56101_640?alt=media&token=8dfb913d-e2f3-4956-bd3a-2c3746d0d6d3

The following response is received:

 {
    "error": {
    "code": 403,
    "message": "Permission denied. Could not perform this operation"
    }
 }

Solution 4:[4]

Granting read access to everyone got my app displaying images correctly again:

rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read: if true; 
      allow write: if request.auth!=null;
    }
  }
}

Probably this is not recommended, but solved the problem for the time being

Solution 5:[5]

I had the same problem as you. This is a late answer but maybe this can help someone.

The problem for me was exactly as user2555964 described.

I can see that you only provide the url and it is referencing a token. I assume that you don't download the url from a storage path like this and just keep the reference in the database.

My problem in detail was that upon uploading a picture I saved the url and kept it in the database and then I had a storage trigger that optimized the picture on firebase cloud functions (copy, optimize, and replace original) which changed the token of the picture, making the token on the url worthless when checking for auth on storage rules.

The solution for me was to store the storage path instead and download the current url with the correct token.

Solution 6:[6]

To add to these existing responses, there is a use case I unfortunately ran into that isn't covered here. Say you store two urls separated by a special character of your choice, this is what you would do:

var url = 'https://myUrl.jpg + https://anootherUrl.jpg' //This is a representation of the response you get from a query

String myUrl = url.toString().replaceAll("+", "").replaceAll("https://anotherUrl", "")//In Dart this is how to edit string objects. Removing unwanted parts by replacing with a blank

If the special character you chose is part of the standard url format, you will get an error. Say you used an underscore(_), some url paths contain underscores so by replacing all underscores with a blank, the path becomes invalid thus throwing a 403 Forbidden error. To resolve it, just inspect the format of your urls and pick a separator that is not within it (IF necessary).

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 user2555964
Solution 2 Arizona2014
Solution 3 Chris Herbst
Solution 4 Felipe Chernicharo
Solution 5 Marceh
Solution 6 O'neya