TLDR: This is a question about the URL returned from the task.snapshot.ref.getDownloadURL()
and its respective downloadToken
. It asks about what is the main role and functionality of the token
and if it's necessary for files that will be publicly available by security rules.
I've just finished the tutorial guides about uploading and downloading files to Firebase Storage found on https://firebase.google.com/docs/storage/web/upload-files and also on this Youtube tutorial from the Firebase official channel.
I'm building a Content Management System for a blog section in one of my Firebase web apps (React + Firebase).
I have a component that lets the admin pick an image and upload it to Firebase Storage Bucket to be displayed in a specific blog post. All images to a specific blogPost
should be inside a folder for the specific blog-post-slug
.
Example:
//bucket/some-blog-post-slug/image1.jpg
Code that runs whenever the admin selected a new file on the <input type='file'/>
:
function onFileSelect(e) {
const file = e.target.files[0];
const storageRef = firebase.storage().ref('some-slug/' + file.name);
const task = storageRef.put(file);
task.on('state_changed',
function progress(snapshot) {
setPercent((snapshot.bytesTransferred / snapshot.totalBytes) * 100);
},
function error(err) {
console.log(err);
},
function complete() {
console.log('Upload complete!');
task.snapshot.ref.getDownloadURL().then(function(downloadURL) {
console.log('File available at', downloadURL);
props.changeImageSrc(downloadURL);
});
}
);
}
The code above returns the downloadURL
that will be saved to the Firestore inside the blogPost
document.
The downloadURL
has the following format:
https://firebasestorage.googleapis.com/v0/b/MYFIREBASEAPP.appspot.com/o/some-slug%2FILE_NAME.jpg?alt=media&token=TOKEN_VALUE
You can see that it comes with a "basic URL": https://firebasestorage.googleapis.com/v0/b/MYFIREBASEAPP.appspot.com/o/some-slug%2FILE_NAME.jpg
And the basicURL is appended with the following GET parameters:
alt=media
and token=TOKEN_VALUE
I was not aware that I would be getting a token, so I'm now testing its behavior to know more about it.
BEHAVIOR WITH STORAGE READS ALLOWED:
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write;
}}}
- When I access the basicURL:
- I get an object in response with the uploaded file details:
{
"name": "some-slug/FILE_NAME.jpg",
"bucket": "MYBUCKET",
"generation": "GENERATION_NUMBER",
"metageneration": "1",
"contentType": "image/jpeg",
"timeCreated": "2019-06-05T13:53:57.070Z",
"updated": "2019-06-05T13:53:57.070Z",
"storageClass": "STANDARD",
"size": "815155",
"md5Hash": "Mj4aCPs21NUNxXpKg1bHirFIO0A==",
"contentEncoding": "identity",
"contentDisposition": "inline; filename*=utf-8''FILE_NAME.jpg",
"crc32c": "zhkQMQ==",
"etag": "CKu4a1+u2+0ucI412CEAE=",
"downloadTokens": "TOKEN_VALUE"
}
- When I access the basicURL?alt=media
The image is displayed.
When I access the basicURL?alt=media&token=TOKEN_VALUE
- The image is displayed.
BEHAVIOR WITH STORAGE READS RESTRICTED:
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write: if request.auth != null;
}}}
- When I access the basicURL:
- I get the following error object:
{
"error": {
"code": 403,
"message": "Permission denied. Could not perform this operation"
}
}
- When I access the basicURL?alt=media
- I get the same error object:
{
"error": {
"code": 403,
"message": "Permission denied. Could not perform this operation"
}
}
- When I access the basicURL?alt=media&token=TOKEN_VALUE
- The image is displayed.
CONCLUSION AND QUESTIONS
It seems to me that the security rule allow read: if request.auth != null;
should have blocked any reads from unauthorized users, but with the TOKEN
parameter, the file is accessible even for requests without an auth
object (Note: I wasn't logged in when I ran the tests above).
I know it's not the best practice to ask more than 1 question, but in this case I think it's necessary:
QUESTION 1:
What is this TOKEN mainly used for and why does it overried the auth
rule?
QUESTION 2:
I want these images to be publicly availabe, as the blog section will be for all users. What URL should I save to Firestore?
Option 1: allow the reads for everyone and just save the basicURL.
Option 2: keep the reads restricted and save the basicURL + token.
QUESTION 3:
To display the images in <image src="imgSrc">
tags do I need the alt=media
parameter? Or the basicURL ending in the FILE_NAME is good enough?
EDIT: QUESTION 3 ANSWER: Just tested it and found out that the alt=media
GET parameter IS necessary to display the image inside an <img>
tag.
NOTE: if you upload the same file and replace the old one, you'll get a different token
each time and the older token
becomes invalid.