Nodejs-storage: getSignedUrl with Cloud Functions giving “SigningError: Failure from metadata server”

Created on 12 Mar 2018  ·  9Comments  ·  Source: googleapis/nodejs-storage

Hi,

I noticed an issue when using @google-cloud/storage 1.6.0, that doesn't exist in 1.5.2.
I'm using Google Cloud Functions with getSignedUrl to upload files into storage. I was getting this error:

{ SigningError: Failure from metadata server.
at /user_code/node_modules/@google-cloud/storage/src/file.js:1715:16
at getCredentials (/user_code/node_modules/@google-cloud/storage/node_modules/google-auto-auth/index.js:264:9)
at googleAuthClient.getCredentials (/user_code/node_modules/@google-cloud/storage/node_modules/google-auto-auth/index.js:148:11)
at process._tickDomainCallback (internal/process/next_tick.js:135:7) message: 'Failure from metadata server.' }

When I downgrade to 1.5.2, the issue goes away.

My index.js looks like this:

const storage = require('@google-cloud/storage')();

exports.getSignedUrl = (req, res) => {

    if(req.method === 'POST') {

        // Perform any authorization checks here to assert
        // that the end user is authorized to upload.

        const myBucket = storage.bucket('my-bucket-name');
        const myFile = myBucket.file(req.body.filename);
        const contentType = req.body.contentType;

        // This link should only last 5 minutes
        const expiresAtMs = Date.now() + 300000;
        const config = {
            action: 'write',
            expires: expiresAtMs,
            contentType: contentType
        };

        myFile.getSignedUrl(config, function(err, url) {
            if (err) {
                console.error(err);
                res.status(500).end();
                return;
            }
            res.send(url);
        });
    } else {
        res.status(405).end();
    }
}

My package.json looks like this:

{
  "name": "sample-http",
  "version": "0.0.1",
  "dependencies": {
    "@google-cloud/storage": "1.5.2"
  }
}

After finding out that 1.5.2 works, I did not look further into this. I'm not sure if this was intentional, or if something in GCF needs to get updated. I'm opening this issue in case this is an unknown bug, and to let others know of a workaround.

https://serverfault.com/q/901144/460159

storage triage me bug

Most helpful comment

I resolved this error by adding the "Cloud Functions Service Agent" role to my service account.

https://console.cloud.google.com/iam-admin/iam

screen shot 2018-03-29 at 10 40 58 am
screen shot 2018-03-29 at 10 40 33 am

All 9 comments

I tried this using only google-auth-library and was able to reproduce-- issue opened here: https://github.com/google/google-auth-library-nodejs/issues/321. Feel free to subscribe to the issue over there to follow what we figure out. Otherwise, I'll update here when I hear back.

The issue was resolved upstream in google-auth-library, and is effectively fixed for all new deploys to GCF. Thanks for reporting!

I resolved this error by adding the "Cloud Functions Service Agent" role to my service account.

https://console.cloud.google.com/iam-admin/iam

screen shot 2018-03-29 at 10 40 58 am
screen shot 2018-03-29 at 10 40 33 am

@rscotten Perfect Solution. I meet this issue while using firebase cloud function calling getSignedUrl().
Thanks a lot.

Just to make sure I had to add this role (from @rscotten answer) to my "App Engine default service account"

It fixed my issue by adding following role to default app engine user

Service Account Token Creator

I have same error only when call a lot of promises to call getSignedUrl
`const listPromisses = await otherList.map(async row => {
list = row.list.map(async instance => {

const bucketName = "hot.ignore.com";

const [signedUrls] = await storage.bucket(bucketName)
                    .file("fileName")
                    .getSignedUrl(options)

const httpURL = signedUrls.replace('https', 'http');
instance.signedUrl = httpURL;

return instance;

})

const values = await Promise.all(listPromisses);`

i have 3k promises.

Just to make sure I had to add this role (from @rscotten answer) to my "App Engine default service account"

Like mentioned you have to add it to the default service account as well... When using firebase even when initializing your app using a different "serviceAccountId" I had to add it to the default account to make URL signing work.

Life savior change @matjazonline thanks 👍

The GCP docs say not to use Service Agent roles on your own Service Accounts.

Warning: Do not grant service agent roles to other users, groups, or service accounts. The permissions within these roles might change without notice. Instead, choose a different predefined role, or create a custom role with the permissions you need.

I looped in GCP support and found the missing role needed was roles/iam.serviceAccountTokenCreator (on top of the standard object viewer role).

Was this page helpful?
0 / 5 - 0 ratings

Related issues

munkhorgil picture munkhorgil  ·  3Comments

digitaljohn picture digitaljohn  ·  6Comments

jorilallo picture jorilallo  ·  6Comments

lukesneeringer picture lukesneeringer  ·  4Comments

kaygeeklaas picture kaygeeklaas  ·  4Comments