node.jsgoogle-cloud-platformgoogle-cloud-functionsgoogle-cloud-storagefirebase-admin

Uploading images to Firebase storage returns "Access denied" after some time


I have been using the following code to upload images on Firebase Storage:

const file = admin
    .storage()
    .bucket()
    .file("my path to save the image")
const mainPromise = file
    .save(buffer, { resumable: false, contentType: "auto" })
    .then(() => file.getSignedUrl({ action: "read", expires: "03-17-2100" }))
    .then(([url]) => url)

const url = await Promise.resolve(mainPromise) 

Recently, I migrate the function containing the above snipped code to the 2nd gen (2nd gen upgrade). The updated function runs with a different service account with the following roles:

With the current setup, I am able to:

However, with some images uploaded after the 2nd gen migration, I received the following error when I'm trying to open an image (after some time from the upload time):

<Error>
    <Code>SignatureDoesNotMatch</Code>
    <Message>Access denied.</Message>
    <Details>The request signature we calculated does not match the signature you provided. Check your Google secret key and signing method.</Details>
    <StringToSign>GET 4108924800 "the image path I'm trying to get..."</StringToSign>
</Error>

Do I need to specify a specific role for the service account in a 2nd gen function to upload file to the storage and been able to retrieve them after some time (i.e. months)? Or is there something else that I'm missing here?


Solution

  • After a discussion with the Firebase Support, we have found the reason why the signed url returns a SignatureDoesNotMatch error.

    When we initialized the admin, we did NOT specify the credentials:

    const admin = require("firebase-admin")
    const dotenv = require("dotenv")
    dotenv.config()
    
    admin.initializeApp({
        storageBucket: process.env.ADMINCONFIG_STORAGEBUCKET,
    })
    module.exports = admin
    

    Thus, Google Cloud will create a SYSTEM_MANAGED key with 16 days expiration date and use the key for signing the URLs. Hence, after the key used for signing the URL is automatically revoked by Google Cloud, the url is no longer valid and will returns the SignatureDoesNotMatch error.

    If we don’t specify a private key for the service account, the Firebase Admin SDK will using the default credentials (in your case "SYSTEM_MANAGED" type key). This usually works in environments like Google Cloud, Firebase Hosting, or local emulators authenticated via gcloud CLI.