Get a permanent URL from Google Cloud Storage? - firebase

I have a scenario where a user can upload images to Firebase Storage, however I do not want them to be able to get a URL for these images (Copy Image Address). Instead I want to provide them with a blob.
When a user uploads the image client side, I get the download URL and store that in Firestore. When the user wants to see the image, I have a cloud function that downloads that image, and sends it to the user as a blob.
This works great for images the user uploads, however I also have a cloud function that is triggered automatically when an image is uploaded and generates a thumbnail.
How do I go about getting a permanent download URL from a Cloud Function/Node server for this generated image? I can get a signed one, but it's not what I need

You can't directly with Google Cloud Storage. The signed URL can't live more than 7 days
The longest expiration value is 604800 seconds (7days).
You can keep the link of the generated thumbnail image, but you have to either download it and serve it each time, or generate a signed url for using and displaying it, each time also. This second solution reduces the processing time and thus the cost.

If you want a permanent URL to access your data, you could make them public according to this documentation[1].
Just keep in mind that when accessing public data through the Google Cloud Platform Console, you must authenticate with Google. It can be accessed with any Google account, the account does not have to be associated with the project that contains the public data, nor must it be registered in the Cloud Storage service [2].
[1] https://cloud.google.com/storage/docs/access-control/making-data-public
[2] https://cloud.google.com/storage/docs/access-public-data

If you are using Django and django-storages[google] lib
Make the bucket public(not advisable for production)
Disable ACL and URL signing
Add these to the settings.py
GS_DEFAULT_ACL = None
GS_QUERYSTRING_AUTH = False

Related

Is it safe to store only "folder-name/filename.jpg" information for files uploaded to Firebase storage?

Right now, everytime I upload an image file to firebase storage, I get an url that looks like this:
https://firebasestorage.googleapis.com/v0/b/my-project.appspot.com/o/some-folder%2Ffile_name.jpg?alt=media
And I'm storing the full URL in my Firestore docs for the object that uses that image.
If I know that it will never change, I could save just the last part: /some-folder%2Ffile_name.jpg?alt=media
But I don't know if the first part could change for future uploads.
Since I'll be rebuilding the full URL on client, if the first part do change for future downloads, it will break my code.
Should I stick to storing the full URL? Or is it safe to assume that the first part of the Firebase Storage API will always be the same?
Firebase gives no written guarantee that nothing will change for that URL. The only "safe" thing to do is store the entire URL. Or, store the path and call getDownloadUrl every time you want to manufacture a new URL.

How to hide Google Storage bucket path and image name from URL

I'm using Google Storage to store profile pictures of my users. I have couple of thousands pictures.
Now the pictures are being saved in a bucket like so:
data/images/profiles/USER_ID.jpg
So the URL to an image:
https://storage.cloud.google.com/data/images/profiles/USER_ID.jpg
I don't want users being able to see someone else picture by just knowing their USER_ID, and still, It has to be the USER_ID for easier search from a developer's side.
I can't use Signed URL as my users do not have a google account, and the pictures from the storage are fetched from a Mobile Application.
Is there a way to keep the file names as they are in the storage, but simple hide the path+filename from the URL?
https://storage.cloud.google.com/fc720d5c05411b03e5e2a6692f8d7d61.jpg -> points to https://storage.cloud.google.com/data/images/profiles/USER_ID.jpg
Thank You
You have several options. Here are a few:
Have users request the URL for another user from the server, then have the server decide whether or not the user is allowed to see the image. If so, have the server (which does have a service account) generate a signed URL and pass it back to the user (or redirect to it). This way, although the user may know the user ID of another user and the URL of their image, they still can't see the image unless the server agrees that this is okay.
Use Firebase Storage to manage the images, which will still store them in GCS but will give you Firebase's auth support.
Proxy the images through your app, either an app engine app or something running in GCE or GKE. This lets you hide everything about the source of the image, including the user ID, but has the downside of requiring all of the data to pass through your service.
Reexamine your requirements. "Easier search on the developer's side" may not be as important as you think, and you need to way the benefit of that vs the cost of working around it.
Another option is Google Images API available on AppEngine. You can link your Cloud Storage objects with Google Images API and use benefits of this API - secure URLs, transform and resize images using URL parameters.
You only need to prepare servingURL for every image stored in GCS and persist this serving URL (for example in Google Datastore)
ImagesService imagesService = ImagesServiceFactory.getImagesService();
ServingUrlOptions suo = ServingUrlOptions.Builder
.withGoogleStorageFileName(gcsImageObjectPath)
.secureUrl(true);
String servingUrl = imagesService.getServingUrl(suo);

Generate thumbnails with predictable URLs

I'm building a Flutter app for which I use the same image in a different size. For performance reason, I would like to fetch this same image in different sizes depending on the display size on the client.
To do that, I would build a system allowing me to generate thumbnails of every uploaded images on firebase storage using cloud function and then use those thumbnails from the client.
On upload, the user would receive the public URL of the source image and could just append the desired size to the URL to fetch a specific thumbnail version (ex.https://google.api.com/mybucket/myprofilepicture.png -> https://google.api.com/mybucket/myprofilepicture#2x.png)
However, firebase cloud storage uses a token protection mechanism (ex. https://google.api.com/mybucket/myprofilepicture.png?alt=media&token=09cb2927-4706-4e36-95ae-2515c68b0d6e) more info here making the public URLs of newly uploaded files unpredictable from the client.
The official thumbnail generation cloud function example solve this by saving the generated URLs into cloud firestore and therefore, requiring another call to be made from the client to fetch the available thumbnails. https://github.com/firebase/functions-samples/tree/master/generate-thumbnail
Is there a way to generate predictable URLs for thumbnails to avoid the overhead of storing them in a separate DB?

How to hide the Firebase Storage download URL from the network tab of browsers?

I'm leveraging Firebase Authentication for downloading images from firebase storage. I'm also leveraging google API HTTP referrers for blockage by domain so that my image from firebase storage is only accessed from my website. But when I go to the network tab of my browser I can see the download URL of the image. By this, anyone can download my image and use it. What should I do so that my images are secured?
P.S: I'm using the firebase storage SDK and by following the documentation when I execute this code below
storageRef.child('images/stars.jpg').getDownloadURL().then(function(url) {
// `url` is the download URL for 'images/stars.jpg'
var img = document.getElementById('myimg');
img.src = url;
}).catch(function(error) {
// Handle any errors
});
I can see the download URL in the network tab of my browser.
You can't. When you give up access to a Cloud Storage download URL to any one, in any way, you are implicitly trusting that user to its access. They are free to share it with anyone they want. If you don't trust that user, then don't give them the URL.
If you don't like the way this works, then don't use download URLs, and allow only secure downloads via the Firebase SDK. At that point, you are trusting the user they will not take the content and upload it elsewhere and generate a URL to it.
You seem to have two options as far as I can tell. Unfortunately, they are basically one in the same effectively as you will probably have to implement both.
The first option is to revoke the access token on individual files you don't want to be allowed to download. Unfortunately, this also means that you can't display them anywhere you currently do via the URL as it breaks that link. See this answer for why that is a pain to do.
The second option is to use storage references to download them client side, but this only works if you are using Firebase SDK's in a web app and not a simple static website. I think this shouldn't expose the URL on the network tab of the browser if the app is set up correctly.
You can implement the second option without the first and the URL shouldn't be exposed, but you can't use the url anymore and have to use both options if you implement the first one... :/ meh... firebase is great, but nothing is perfect
This seems to work, I'll update if it doesn't
Edit: "However, the CORS configuration applies only to XML API requests," which one can just go to the file still.. https://cloud.google.com/storage/docs/cross-origin
GCP console >_
pencil icon > create cors.json [{"origin":["https://yourorigin1.com"],"method":["GET"],"maxAgeSeconds":3600}]
go back to shell and enter gsutil cors set cors.json gs://yourproject1.appspot.com
https://stackoverflow.com/a/58613527/11711280
Workaround:
I will make all rules resource.data.authorId, resource.data.members, etc. I need to match the request.auth.uid (or control calls in client code to non-anonymous uid's), and sign-in every user anonymously, at first. Then, uid will not be null when using a firebase initialized from our domain

Firebase Storage – getting static link based on filename

I have a situation where in Firebase Storage users store their avatar to /users/{uid}.jpg
If I then use the Storage API to get the download URL and display the image, it ends up being very slow to make the first request because the download URL is not cached anywhere.
So my solution is to get the DownloadURL when the user uploads the image and store that in Firebase allowing the client image provider to automatically cache the image which speeds up loads considerably.
However, there is one problem with this solution.
If a user replaces their avatar, the old link becomes broken instead of updated. I believe this is because a new token is generated each time something is uploaded for security reasons but these are of no benefit to me.
So my question is twofold:
1) How can I allow a user to upload an avatar to a path that is dedicated to them such as /users/{uid}.jpg, get a bare download URL that can be cached by the client, and have that URL remain the same even when the file changes at /users/{uid}.jpg
2) If this is not possible, what is the normal way to solve this issue?
Download URLs are opaque. The contents of the actual URL itself is an implementation detail of the system, and it's not supported to dig around in its contents. The URLs can't be dissected or composed.
You can use a storage trigger with Cloud Functions to automatically generate a signed URL whenever something changes in your storage bucket.
So instead of serving from a hard-coded URL, simply retrieve the URL from an updated value in the datastore (or any data storage system). Every time the user updates the avatar, simply store the new URL in the datastore and you can query for it when you need it.

Resources