Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Firebase functions: Error SignatureDoesNotMatch #360

Open
rubsilvah opened this issue Apr 4, 2018 · 15 comments
Open

Firebase functions: Error SignatureDoesNotMatch #360

rubsilvah opened this issue Apr 4, 2018 · 15 comments
Assignees

Comments

@rubsilvah
Copy link

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

Ps.: I have already followed the tips in the link below but the problem remains:
googleapis/google-cloud-node#1976

Thanks in advance!

@nicolasgarnier
Copy link
Contributor

Unless you changed the expiery date to a few days in the future (instead of 400 years in the future like the sample does) then I'm not sure what could trigger this. Maybe a but with the Cloud Storage service itself...

@nicolasgarnier nicolasgarnier self-assigned this Apr 5, 2018
@ravirajdarisi
Copy link

I am facing the similar problem.

@colinjstief
Copy link

colinjstief commented Jun 4, 2018

I am facing this issue as well. Similar to @rubsilvah, my URL works for a few days, and then seems to expire with "SignatureDoesNotMatch" 403 response.

The URL seems valid too, and includes this query parameter: '...&Expires=16730323200&Signature=...'

For reference, here is a repo of relevant code:

https://github.com/colinjstief/getSignedUrl-example

Same issue reported here googleapis/google-cloud-node#1976
and here googleapis/nodejs-storage#144

@mqln
Copy link

mqln commented Jun 5, 2018

Same problem here! Can't ship to production until this is solved...

@CodingDoug
Copy link

The Firebase Admin SDK just wraps the Cloud Storage SDK, so you might be better off filing a report in the repo for the @google-cloud/storage module. https://github.com/googleapis/nodejs-storage/

@amitkukadia
Copy link

Same here, the links work for a couple of days and then the following error happens.
"The request signature we calculated does not match the signature you provided. Check your Google secret key and signing method."

@zhuixinjian
Copy link

Same here, the links work for a couple of days and then the following error happens.

@douglasulmer
Copy link

Any solution? Same here, the links work for 7 days.

@DecentGradient
Copy link

While the issues referenced above provide alternatives to getting a signed url on a google managed key, the tutorial should not be misleading and indicate that the url won't expire until the year 2500

@royherma
Copy link

This is also happening to me, works for a few days and stops. Any ideas?

@damofer
Copy link

damofer commented Dec 8, 2019

Same here, it works for a week and then it doesnt.

@tljesse
Copy link

tljesse commented Feb 7, 2020

This appears to be a known error and is linked to this issue googleapis/nodejs-storage#244

The eventual solution is to set your service account explicitly within your cloud functions googleapis/nodejs-storage#244 (comment)

I've just updated my code and will just have to wait and see if this solves it.

@otri
Copy link

otri commented Apr 14, 2020

Also seems sketchy that it's possible to set an expiry beyond the key rotation duration, should be some kind of validation checking for default configuration and warn during development if the API call with a really long duration is used. This is a tricky bug to catch!

@tljesse
Copy link

tljesse commented Apr 14, 2020

I actually had to come back to this as the images started to expire again for no apparent reason. I ended up creating my own access tokens from within the function and bypassing getSignedUrl completely. Hopefully this helps anyone down the line

import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';

const mkdirp = require('mkdirp-promise');
const spawn = require('child-process-promise').spawn;

import * as path from 'path';
import * as os from 'os';
import * as fs from 'fs';

import { uuid } from 'uuidv4';

// Max height and width of the thumbnail in pixels.
const THUMB_MAX_HEIGHT = 200;
const THUMB_MAX_WIDTH = 400;
// Thumbnail prefix added to file names.
const THUMB_PREFIX = 'thumb_';

/**
 * When an image is uploaded in the Storage bucket We generate a thumbnail automatically using
 * ImageMagick.
 * After the thumbnail has been generated and uploaded to Cloud Storage,
 * we write the public URL to the Firebase Realtime Database.
 */
export const generateThumbnail = functions.storage.object().onFinalize(async (object: any) => {
  // File and directory paths.
  const filePath = object.name;
  const contentType = object.contentType; // This is the image MIME type
  const fileDir = path.dirname(filePath);
  const fileName = path.basename(filePath);
  const thumbFilePath = path.normalize(path.join(fileDir, `${THUMB_PREFIX}${fileName}`));
  const tempLocalFile = path.join(os.tmpdir(), filePath);
  const tempLocalDir = path.dirname(tempLocalFile);
  const tempLocalThumbFile = path.join(os.tmpdir(), thumbFilePath);

  // Exit if this is triggered on a file that is not an image.
  if (!contentType.startsWith('image/')) {
    return console.log('This is not an image.');
  }

  // Exit if the image is already a thumbnail.
  if (fileName.startsWith(THUMB_PREFIX)) {
    return console.log('Already a Thumbnail.');
  }

  // Cloud Storage files.
  const bucket = admin.storage().bucket(object.bucket);
  const file = bucket.file(filePath);

  const fileUUID = uuid();
  const thumbUUID = uuid();
  //const thumbFile = bucket.file(thumbFilePath);

  const metadata = {
    contentType: contentType,
    metadata: {
      firebaseStorageDownloadTokens: fileUUID
    }
  };
  const thumbMetadata = {
    contentType: contentType,
    metadata: {
      firebaseStorageDownloadTokens: thumbUUID
    }
    // To enable Client-side caching you can set the Cache-Control headers here. Uncomment below.
    // 'Cache-Control': 'public,max-age=3600',
  };
  
  // Create the temp directory where the storage file will be downloaded.
  await mkdirp(tempLocalDir)

  // Download file from bucket.
  await file.download({destination: tempLocalFile});
  // console.log('The file has been downloaded to', tempLocalFile);

  // Set the new token on the file
  await file.setMetadata(metadata);

  // Generate a thumbnail using ImageMagick.
  await spawn('convert', [tempLocalFile, '-thumbnail', `${THUMB_MAX_WIDTH}x${THUMB_MAX_HEIGHT}>`, tempLocalThumbFile], {capture: ['stdout', 'stderr']});
  // console.log('Thumbnail created at', tempLocalThumbFile);

  // Uploading the Thumbnail.
  await bucket.upload(tempLocalThumbFile, {destination: thumbFilePath, metadata: thumbMetadata});

  //  console.log('Thumbnail uploaded to Storage at', thumbFilePath);

  // Once the image has been uploaded delete the local files to free up disk space.
  fs.unlinkSync(tempLocalFile);
  fs.unlinkSync(tempLocalThumbFile);
  // Get the Signed URLs for the thumbnail and original image.
  // const config: any = {
  //   action: 'read',
  //   expires: '03-01-2500',
  //   contentType: contentType
  // };
  // console.log('Config:', config);
  // const results = await Promise.all([
  //   thumbFile.getSignedUrl(config),
  //   file.getSignedUrl(config),
  // ]);

  // console.log('Results:', results);
  // // console.log('Got Signed URLs.');
  // const thumbResult = results[0];
  // const originalResult = results[1];
  // const thumbFileUrl = thumbResult[0];
  // const fileUrl = originalResult[0];

  const fileUrl = "https://firebasestorage.googleapis.com/v0/b/" + bucket.name + "/o/" + encodeURIComponent(YOUR_UPLOAD_PATH + '/' + fileName) + "?alt=media&token=" + fileUUID;
  const thumbFileUrl = "https://firebasestorage.googleapis.com/v0/b/" + bucket.name + "/o/" + encodeURIComponent(YOUR_UPLOAD_PATH + '/thumb_' + fileName) + "?alt=media&token=" + thumbUUID;

  const dbData = {
    name: filePath.replace(YOUR_UPLOAD_PATH + '/',''),
    path: fileUrl,
    thumbnail: thumbFileUrl,
    dateCreated: admin.firestore.FieldValue.serverTimestamp(),
    contentType: contentType
  };
  
  // Add the URLs to the Database
  let docRef = await admin.firestore().collection('mediaData').add(dbData);
  //console.log('Doc created with ID: ', docRef.id);

  await admin.firestore().collection('mediaData').doc(docRef.id).update({_id: docRef.id});
  return console.log('Thumbnail URLs saved to database.');
});

I haven't had any issues since implementing this. Good luck!

@adirzoari
Copy link

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;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet