googleapis / google-cloud-node

Google Cloud Client Library for Node.js
https://cloud.google.com/nodejs
Apache License 2.0
2.9k stars 590 forks source link

The request signature we calculated does not match the signature you provided. Check your Google secret key and signing method. #1976

Closed kuldeeppoonia closed 7 years ago

kuldeeppoonia commented 7 years ago

Hi team, I have spent a lot of time to solve the problem and also spent a lot of time to search the solution on net but every time I failed. The scenario is that

I am using @google-cloud/storage to upload my image on google cloud storage. Everything is fine. Images successfully uploaded on cloud. Now I want to show these images to my users but I didn't want to give public permission to images. So I created a signed url and trying to download them but everytime I am facing the following problem

<Error>
<Code>SignatureDoesNotMatch</Code>
<Message>
The request signature we calculated does not match the signature you provided. Check your Google secret key and signing method.
</Message>
<StringToSign>GET 1486639481 /<my bucket name>/<my image name></StringToSign>
</Error>

here is my code

//get signed url
    action: 'read',
        expires: Date.now() + 76000000,
        contentType: 'image/jpeg'
    };
    var file = bucket.file('TestingImage.jpg').getSignedUrl(options, function (error, signedUrl) {
        console.log('signed url...');
        console.log(signedUrl);
    });

But when I put this signed url in browser it give me the error.

I am following the url for uploading image https://googlecloudplatform.github.io/google-cloud-node/#/docs/storage/0.6.0/storage

How ever I can download these image in nodejs using file.download() but I want to view this image on browser

Please guide me when am I wrong.

stephenplusplus commented 7 years ago

The browser will need to set the matching Content Type on the request headers.

As needed. If you provide a content-type, the client (browser) must provide this HTTP header set to the same value. - https://cloud.google.com/storage/docs/access-control/signed-urls

If you remove the content type while creating the signed URL, it should work in the browser.

kuldeeppoonia commented 7 years ago

Thanks a lot

kirankumar-mahi commented 7 years ago

Hi ,

I am using latest 'aws-java-sdk-s3', version: '1.11.158'. I am getting below error when uploading the files. I am able to list the buckets. Is there any signature setting I have do.?

The request signature we calculated does not match the signature you provided. Check your Google secret key and signing method. com.amazonaws.services.s3.model.AmazonS3Exception: The request signature we calculated does not match the signature you provided. Check your Google secret key and signing method. (Service: Amazon S3; Status Code: 403; Error Code: SignatureDoesNotMatch; Request ID: null), S3 Extended Request ID: null

rubsilvah commented 6 years ago

Hi, there!

I'm facing the same problem but I've already removed the content type while creating the signed URL. The interesting fact is that this problem only happens suddenly a few days after the signed URL is created..

The code I'm following is the one below: https://github.com/firebase/functions-samples/blob/master/generate-thumbnail/functions/index.js

Thanks in advance!

stephenplusplus commented 6 years ago

I notice the example code sets the expiration date for 482 years from now, so that's probably not the issue :) (Unless-- did you change it?)

Assuming that's not the case, I'm not sure what it could be. Could you open a new issue on the https://github.com/firebase/functions-samples repo? If it turns out to be a problem with our library, we can continue to look at it and see what we can do.

jayarjo commented 4 years ago

I'm getting the same error. I do not pass content-type to signer method. Do I need to set some special permissions on the bucket maybe, have an ACL or something? Service Account that is used to sign urls has Storage Admin role.

sboudouk commented 4 years ago

Same as @jayarjo here.

Also, the file I'm trying to access have a name like Customers/{id}/kitten.jpeg. I use all this name to get my signedUrl, and not only kitten.jpeg (makes sense to me).

Is there any further explanations here ?

rishisaraf11 commented 4 years ago

Can someone provide solution for this. URL worked for few days after geenration now suddenly my android app is not able to download image and it fails with "HttpException: Invalid statusCode: 403,". From browser it gives below error

`This XML file does not appear to have any style information associated with it. The document tree is shown below.

SignatureDoesNotMatch The request signature we calculated does not match the signature you provided. Check your Google secret key and signing method. GET 16447017600 /wmiefy-577a4.appspot.com/images/thumb_5e83f485-656d-45b5-8c40-47b11453e1965771333569762950312.jpg `
SergeiEensalu commented 4 years ago

Have the same problem :)

export async function storeFileInStorage(filename: string, objectId: string, fileType: FileType, base64Content: Buffer, objectType: string): Promise<FileInfo> {

  const folderName = objectType === "PROFILE" ? `profiles` : `agreements`;

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

  const fileAsByteArray = new Uint8Array(base64Content);
  const bucketFilename = folderName + "/" + objectId + "/" + fileType + "_" + filename;
  const file = bucket.file(bucketFilename);

  let publicUrl;

  await file.save(fileAsByteArray)
      .then(async stuff => {
        publicUrl = await file.getSignedUrl({
          action: "read",
          expires: "05-09-2999"
        });
      }).catch(err => {
        throw Error("Could not store file for objectId: ${objectId} with to ${bucketFilename} error: ${err}");
      });

  return {publicUrl: publicUrl ?? "", storagePath: bucketFilename, type: fileType};
}

For example url

Can someone suggest something?

JustinBeckwith commented 4 years ago

Greetings folks! If you're having issues, please open a new issue here: https://github.com/googleapis/nodejs-storage/

ORESoftware commented 3 years ago

For me it was because I was using POST instead of PUT, it's not just about the ContentType header, the http method also has to match, in golang:

method := "PUT"   // THIS MUST MATCH
expires := time.Now().Add(time.Minute * 10)

url, err := storage.SignedURL(bucket, filename, &storage.SignedURLOptions{
  GoogleAccessID: cfg.Email,
  PrivateKey:     cfg.PrivateKey,
  Method:         method,   
  Expires:        expires,
  ContentType:    "binary/octet-stream",   // NOTE
})

if err != nil {
// ...
}

var client = http.DefaultClient
request, err := http.NewRequest("PUT", url, req.Body)  // we use PUT not POST

if err != nil {
// ....
}

request.Header.Set("Content-Type", "binary/octet-stream")  // this must match
response, err := client.Do(request)
bob-lee commented 2 years ago

I'm in the same boat, uploaded image's url works but some days later, getting 403. found suspicious note about expires parameter on the api doc:

A timestamp when this link will expire. Any value given is passed to new Date(). Note: 'v4' supports maximum duration of 7 days (604800 seconds) from now.

rahul-singh-bv commented 1 year ago

If anyone ever encounters this error again, in my case the key had an expiry date and it had expired. Generating a new key worked

schankam commented 1 year ago

I am having the same issue today: we're the 9th of August 2023, and here are the parameters: X-Goog-Date=20230803T154108Z&X-Goog-Expires=604800

The link should be valid until the 10th, but somehow I am getting Access Denied with SignatureDoesNotMatch.

For some reasons this link was working for a few days and now stopped working before expiration date.

I am generating the link using firebase admin in a Cloud run environment:

        const thumbnailUrlExpiresAt = new Date(Date.now() + 604800000); // 7 days
        const [signedUrl] = await firebase
          .storage()
          .bucket(process.env.BUCKET_VIDEO_THUMBNAILS)
          .file(lastExport.thumbnailData?.thumbnailPath)
          .getSignedUrl({
            version: 'v4',
            action: 'read',
            expires: thumbnailUrlExpiresAt
          });

What is actually going on here?

Also, please note that I am also saving the expiration Date of that link in my database and it is set to 2023-08-10T15:41:08.412+00:00

adirzoari commented 6 months ago

@jayarjo @JustinBeckwith @stephenplusplus @schankam I have the same issue index.js

const { onRequest } = require('firebase-functions/v2/https');
const webhookHandler = require('./webhook');
const getTransactionDetails = require('./getTransactionDetails');

exports.webhookHandler = onRequest(webhookHandler);
exports.getTransactionDetails = onRequest(getTransactionDetails);

webhook.js

const getFirebaseApp = require('./firebaseConfig');
const { getFirestore } = require('firebase-admin/firestore');
const { onRequest } = require('firebase-functions/v2/https');

const firebaseApp = getFirebaseApp();

exports.saveTransactionDetails = onRequest(async (req, res) => {
  try {
    console.log(req);
    console.log('req body is', req.body);
    const total = parseFloat(req.body.total) || 0;
    const counter = parseInt(req.body.counter) || 0;

    const db = getFirestore();
    const totalsRef = db.collection('totals').doc('total');
    const totalsDoc = await totalsRef.get();

    if (totalsDoc.exists) {
      const totalsData = totalsDoc.data();
      const newTotal = totalsData.total + total;
      const newCounter = totalsData.counter + counter;

      await totalsRef.update({
        total: newTotal,
        counter: newCounter
      });
    } else {
      await totalsRef.set({
        total,
        counter
      });
    }

    res.status(200).json({ message: 'Transaction details saved successfully' });
  } catch (error) {
    res.status(500).json({ message: 'Failed to save transaction details' });
  }
});

getTransactionDetails.js

const getFirebaseApp = require('./firebaseConfig');
const { getFirestore } = require('firebase-admin/firestore');
const { onRequest } = require('firebase-functions/v2/https');

const firebaseApp = getFirebaseApp();

exports.getTransactionDetails = onRequest(async (req, res) => {
  try {
    const db = getFirestore();
    const totalsRef = db.collection('totals').doc('total');
    const totalsDoc = await totalsRef.get();

    if (totalsDoc.exists) {
      const totalsData = totalsDoc.data();
      res.status(200).json({ total: totalsData.total, counter: totalsData.counter });
    } else {
      res.status(404).json({ message: 'Transaction details not found' });
    }
  } catch (error) {
    res.status(500).json({ message: 'Failed to retrieve transaction details' });
  }
});

Firebaseconfig.js

const { initializeApp } = require('firebase-admin/app');

let firebaseApp;

function getFirebaseApp() {
  if (!firebaseApp) {
    firebaseApp = initializeApp();
  }
  return firebaseApp;
}

module.exports = getFirebaseApp;