I'm trying to get a signed url from a storage bucket, but am getting a signing error. I have permission to read from the bucket, and have been able to stream its contents fine.
Code:
...
const expires = new Date()
expires.setSeconds(expires.getSeconds() + 30)
const storage = new Storage({ projectId });
storage
.bucket(bucketName)
.file(`${directory}/${packageName}`)
.getSignedUrl({
expires,
action: 'read',
})
.then(url => {
res.send(url)
console.log(url)
})
.catch(console.log)
Error:
{ SigningError: Cannot sign data without `client_email`.
at /Users/pikelnys/Desktop/code/gcs-project/api/node_modules/@google-cloud/storage/src/file.js:1784:16
at Auth._signWithApi (/Users/pikelnys/Desktop/code/gcs-project/api/node_modules/google-auto-auth/index.js:331:7)
at getCredentials (/Users/pikelnys/Desktop/code/gcs-project/api/node_modules/google-auto-auth/index.js:316:14)
at googleAuthClient.getCredentials (/Users/pikelnys/Desktop/code/gcs-project/api/node_modules/google-auto-auth/index.js:194:9)
at /Users/pikelnys/Desktop/code/gcs-project/api/node_modules/google-auth-library/build/src/auth/googleauth.js:602:67
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:188:7) message: 'Cannot sign data without `client_email`.' }
@google-cloud/storage version: 1.7.0Edit: I'm getting this in both a local environment (where I authenticate with gcloud auth application-default login) and while the code is deployed to cloud functions.
I think gcloud auth application-default login doesn't support client_email and you need to get a service account instead.
Here's a related issue: https://github.com/googleapis/nodejs-storage/issues/116.
I'm getting it on Cloud Functions, though, which I believe is a service account.
I tried to reproduce it on Cloud Functions, but was able to generate signed URL from the code provided.
Default service account in Cloud Functions should have a client_email {{project_id}}@appspot.gserviceaccount.com. The only thing I had to do was to add Service Account Token Creator role to the service account from IAM.
Can you provide the error you got when running the code under Functions?
I did something soon after my original post to get it working on Functions. But I'm still getting this error locally.
As @fhinkel mentioned, it wouldn't work if you'd set up your credentials via gcloud auth application-default login. However, you should able to make it work locally using a service account.
Please let me know if this works and is an acceptable solution to you. :)
Hi @pikelnys.
You will have to specify keyFilename while creating the Storage object if you are deploying locally.
keyFilename is not required if you have deployed your code under the same project your Google Cloud Bucket was created. You just have to ensure you have the right access by having a look at your service-account-key.
The error says cannot sign without client_email. client_email is available inside the service-account-key.json but was not provided while creating the object hence the error. This error would not have arrived had you deployed your code inside the same project as your Cloud Bucket was.
Here is a sample service-account-key.json file. Look for client_email in the file.
{
"type": "service_account",
"project_id": "[PROJECT-ID]",
"private_key_id": "[KEY-ID]"
"private_key": "-----BEGIN PRIVATE KEY-----\n[PRIVATE-KEY]\n-----END PRIVATE KEY-----\n",
"client_email": "[SERVICE-ACCOUNT-EMAIL]",
"client_id": "[CLIENT-ID]",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://accounts.google.com/o/oauth2/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/[SERVICE-ACCOUNT-EMAIL]"
}
Steps to Follow.
Download the service account key in JSON format and specify its path inside keyFilename. Default service-account-key provided by Google has read-only access. So make sure if you want to write to your bucket you would have to change the permissions inside the service-account.
Here is the
Google Codelab link for reference
var storage = new Storage({
projectId: "your project id",
keyFilename:"your-service-account-key.json"
});
Hope this resolves your issue.
@ujwalkagrawal, thanks for posting such detailed explanation on service account credentials!
@pikelnys we hope this resolves your issue, please re-open if you run into anymore issues :)
Is this possible to authenticate without a keyfile?
I was getting "Cannot sign data without client_email" error when running firebase emulators:start.
I fixed it by setting up a service account and downloading the service account .json credentials file as service_account.json, then running GOOGLE_APPLICATION_CREDENTIALS="service_account.json" firebase emulators:start
Most helpful comment
Hi @pikelnys.
You will have to specify keyFilename while creating the Storage object if you are deploying locally.
keyFilename is not required if you have deployed your code under the same project your Google Cloud Bucket was created. You just have to ensure you have the right access by having a look at your service-account-key.
This is well explained here
The error says cannot sign without client_email. client_email is available inside the service-account-key.json but was not provided while creating the object hence the error. This error would not have arrived had you deployed your code inside the same project as your Cloud Bucket was.
Here is a sample service-account-key.json file. Look for client_email in the file.
Steps to Follow.
Download the service account key in JSON format and specify its path inside keyFilename. Default service-account-key provided by Google has read-only access. So make sure if you want to write to your bucket you would have to change the permissions inside the service-account.
Here is the
Google Codelab link for reference
Hope this resolves your issue.