-
Notifications
You must be signed in to change notification settings - Fork 359
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Firebase Admin SDK: getDownloadUrl - Permission denied. No READ permission #2344
Comments
I couldn't figure out how to label this issue, so I've labeled it for a human to triage. Hang tight. |
This is the emulator code that's returning this particular error message: https://github.com/firebase/firebase-tools/blob/b7eea76c22816a0caf4e45e6bd0f072c066c5d44/src/emulator/storage/apis/firebase.ts#L111 Unfortunately, our admin credential vetting in the emulator is in a pretty poor state. We don't have full support of OAuth access tokens, which is what the admin SDK is sending along with its requests. In short the validation flow for getDownloadUrl in the emulator is this:
Since there's no way to get the admin SDK to pass along the header "Authorization: Bearer owner", this will likely remain broken until we have a full OAuth validation, which we haven't needed until the introduction of the admin See this comment at https://github.com/firebase/firebase-tools/blob/master/src/emulator/storage/rules/utils.ts#L81. This is a true bug and we will get around to fixing this eventually but it's hard to say when the team will have the time to tackle this. |
@tonyjhuang thanks for taking the time to review it! But can I rely on that it only persists for the emulator and not in the "real" / production environment when deploying the function? Would the simplest Appreciate the support on this! Or is there any other workaround to get the download url using the firebase-admin sdk in case the above is not working? |
So does it actually work in production?? @maneesht @tonyjhuang |
No one here? 😅 |
Same issue with the Firebase emulator, even with custom rules. Only working after deploying to Firebase
|
I have the same issue here and am bypassing the getDownloadUrl in the emulator for now. @tonyjhuang Please notify us if this is fixed 👍 |
Here's my workaround: import fetch from 'cross-fetch';
import { FullMetadata } from '@firebase/storage-types';
/**
* Asynchronously generates and returns the download URL for a file in a specified Firebase Storage emulator bucket.
* The generated URL can be used to download the file.
*
* @param {string} bucket - The name of the Firebase Storage emulator bucket.
* @param {string} filePath - The path to the file inside the bucket.
* @returns {Promise<string>} - A promise that resolves to the download URL as a string.
*/
export const getEmulatorDownloadURL = async (bucket: string, filePath: string) => {
// fetch a new download token
const tokenGenerationFetch = await fetch(
`http://${process.env.FIREBASE_STORAGE_EMULATOR_HOST}/v0/b/${bucket}/o/${encodeURIComponent(
filePath,
)}?create_token=true`,
{
method: 'POST',
headers: {
Authorization: 'Bearer owner',
},
},
);
const tokenGenerationResponse: FullMetadata & { downloadTokens: string } = await tokenGenerationFetch.json();
const downloadToken = tokenGenerationResponse.downloadTokens.split(',')[0];
// manually construct the emulator download url
return `http://${process.env.FIREBASE_STORAGE_EMULATOR_HOST}/v0/b/${bucket}/o/${encodeURIComponent(
filePath,
)}?alt=media&token=${downloadToken}`;
};
|
Is there any debug info we can provide to help make a fix? This is very unfortunate DX. |
You can use this function to conditionally run exports.getFileDownloadUrl = async (filePath) => {
// Use 'process.env.FUNCTIONS_EMULATOR === "true"' to check your environment.
// Make sure that "true" is surrounded by quotes because it is a string, not a boolean.
if (process.env.FUNCTIONS_EMULATOR === "true") {
// Running using emulators.
// You can find the bucket in the storage emulator suite.
// Your bucket name should look something like this: <gs://your-app-name.appspot.com/>.
return await getEmulatorDownloadURL(bucket, filePath);
} else {
// Running in production.
const fileRef = getStorage().bucket().file(filePath);
const fileUri = await getDownloadURL(fileRef);
return fileUri;
}
};
|
Is there a way to set a custom {
...
"emulators": {
"storage": {
"port": 9199,
"rules": "storage.rules.emulator"
},
}
} This could be an easy workaround to use open rules for local and then use the regular rules for deployment as a workaround for now. |
@kdawgwilk I haven't tried myself, but you should be able to use a different Firebase config file (ala |
This solution actually works, is the only way I found out there thanks! |
Btw (for my use case), I found that the download URL was accessible via const uploadRef = storage.bucket().file('assets/' + filename)
await uploadRef.save(buffer, {
metadata: { cacheControl: 'public,max-age=86400' },
public: true,
})
return uploadRef.publicUrl() or via const uploadRef = storage.bucket().file('assets/' + filename)
await uploadRef.save(file.buffer, {
metadata: { cacheControl: 'public,max-age=86400' },
public: true,
})
const [metadata] = await uploadRef.getMetadata()
return metadata.mediaLink |
Describe your environment
Describe the problem:
I have now tried for a very long time to follow these docs in order to get
getDownloadURL
to work. https://firebase.google.com/docs/storage/admin/start#use_a_default_buckethttps://firebase.google.com/docs/storage/admin/start#shareable_urls
Regardless of how I initialize my app, when trying to use
getDownloadURL
I getError: Permission denied. No READ permission.
Here is how different ways I tried initializing:
Furthermore, I have tried adding IAM roles to the service account:
![Screenshot 2023-10-19 at 16 54 45](https://private-user-images.githubusercontent.com/16811871/276657897-8da9e338-4d60-4744-adf7-8ae933bc2f58.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjExMzc4MTgsIm5iZiI6MTcyMTEzNzUxOCwicGF0aCI6Ii8xNjgxMTg3MS8yNzY2NTc4OTctOGRhOWUzMzgtNGQ2MC00NzQ0LWFkZjctOGFlOTMzYmMyZjU4LnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDA3MTYlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwNzE2VDEzNDUxOFomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWUzNWIxZThmMTYzMjRlODY4OGRhNTQ1ZTUyYTFkZjdhZmQyOGJmOGVhZTRkMDY0YzM3YjlkM2UzZjk1MTkxOTcmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.CCSfInDMLozJ8D-nJf3dAqo92mVpP-7JOU4Dc0pxINM)
What I am trying to accomplish is simply what is done in the before-mentioned docs:
What is going wrong here, as the docs states clearly I firebase admin sdk should have access by default?
Stacktrace: (from emulator)
My service account file:
The text was updated successfully, but these errors were encountered: