0

I'm attempting to use a Firebase Cloud Function to create signed download URLs for files stored in a Storage Bucket. Using the snippet below on my local machine, I'm able to access cloud storage and generate these URLs.

/* eslint-disable indent */
import * as functions from "firebase-functions";
import * as admin from "firebase-admin";
// eslint-disable-next-line @typescript-eslint/no-var-requires
const serviceAccount = require("./test-serviceAccount.json");

admin.initializeApp();
const storage = admin.storage();
const bucket = storage.bucket();

admin.initializeApp({
  credential: admin.credential.cert(serviceAccount),
}, "firestore");

export const getFile = functions.https.onRequest(async (request, response) => {
  const [files] = await bucket.getFiles();
  const fileNames: string[] = [];
  files.forEach(async (file) => {
    console.log(file.name);
    const url = await file.getSignedUrl(
      {
        version: "v2",
        action: "read",
        expires: Date.now() + 1000 * 60 * 60 * 24,
      }
    );
    fileNames.push(String(url));
    if (files.indexOf(file) === files.length - 1) {
      response.send(JSON.stringify(fileNames));
    }
  });
});

However after deploying to Cloud Functions I get an error when I call the function saying:

Error: could not handle the request

and the following message is logged in the functions console:

Error: The caller does not have permission
    at Gaxios._request (/workspace/node_modules/gaxios/build/src/gaxios.js:129:23)
    at processTicksAndRejections (internal/process/task_queues.js:95:5)
    at async Compute.requestAsync (/workspace/node_modules/google-auth-library/build/src/auth/oauth2client.js:368:18)
    at async GoogleAuth.signBlob (/workspace/node_modules/google-auth-library/build/src/auth/googleauth.js:655:21)
    at async sign (/workspace/node_modules/@google-cloud/storage/build/src/signer.js:97:35) 

I've tried using and not using a .json service account key and made sure that the service account has permissions (it has Service Account Token Creator, Storage Admin, and Editor roles at the moment).

I also read this issue relating to the python SDK for storage, but it seems to have been resolved. The workaround mentioned in that issue (using a .json service account token) also didn't resolve the permissions errors.

Todd Rylaarsdam
  • 438
  • 6
  • 19
  • It has been discussed [here](https://stackoverflow.com/questions/50136353/) that the signed URL methods in Firebase might not be the best option, as it is recommended to use other [methods](https://firebase.google.com/docs/storage/web/download-files) in a combo with a setup of Firebase Security Rules. For some alternatives, check this [previous thread](https://stackoverflow.com/questions/42956250/). Also, the V2 Signin method is not currently recommended, and you should consider using V4. Can you please confirm if you are able to use the recommended methods? – Alex Apr 14 '22 at 23:18

2 Answers2

0

After working with Firebase support - here's what worked for me:

import { initializeApp, applicationDefault } from 'firebase-admin/app';

initializeApp({
 credential: applicationDefault(),
 projectId: '<FIREBASE_PROJECT_ID>',
});

Specifying the projectId in the init call seems to have resolved the issue.

Todd Rylaarsdam
  • 438
  • 6
  • 19
0

Signed url means it is signed for (or, accessible to) any particular user for particular span of time, maximum 7 days. If you trying to get it for unauthenticated users, it may show such error(s).

It's better to use getDownloadURL() for unauthenticated users. getSignedUrl() should to used for authenticated users only.

Pankaj Kumar
  • 422
  • 5
  • 10