0
votes

I'm developing a program that uploads the user Logo, or image. To do so, I first create a signature on the server-side and then upload the image from the client to cloudinary, with that signature.

Problem is that I always get the following error: `Invalid Signature "b4000077c554448649396c31750e27141820693c". String to sign - 'source=uw&timestamp=1598531294&upload_preset=signed-preset'.`` (with a different signature and timestamp ofc.)

I followed the guide on their docs: https://cloudinary.com/documentation/upload_images#generating_authentication_signatures But with no help,

Here's the client-side code:

 let signature = '';
    let timestamp = '';

    let generateSignature = function (callback, params_to_sign) {
        $.ajax({
            url: "/business/create-cloudinary-signature",
            type: "GET",
            dataType: "text",
            data: { data: params_to_sign },
            complete: function () { console.log("complete") },
            success: function (response, textStatus, xhr) {
                const res = JSON.parse(response);
                console.log(res.signature)
                signature = res.signature;
                timestamp = res.timestamp;
                console.log(signature, timestamp);
                callback(signature);
            },
            error: function (xhr, status, error) { console.log(xhr, status, error); }
        });
    }

    console.log(signature, timestamp) //correct values

    const uploadSettings = {
        apiKey: "816773373496447",
        cropping: true,
        cloudName: 'rav-chesed',
        uploadPreset: 'signed-preset'

    }

    var myWidget = cloudinary.createUploadWidget({ ...uploadSettings, uploadSignature: generateSignature, uploadSignatureTimestamp: timestamp }, (error, result) => {
        if (error) {
            console.log(error)
        }
        if (!error && result && result.event === "success") {
            console.log('Done! Here is the image info: ', result.info);
        }
    }
    )
    document.getElementById("logoUploader").addEventListener("click", function () {
        myWidget.open();
    }, false);   
    

And here's the server-side code (nodejs)

router.get("/create-cloudinary-signature", (req, res) => {
  console.log("heyy");
  console.log(req.query);
  const data = req.query.data;

  // // Get the timestamp in seconds
  // var timestamp = Math.round(new Date().getTime() / 1000);
  // console.log(timestamp);
  data.timestamp = Number(data.timestamp);
  // console.log(data);

  // // Show the timestamp
  // console.log("Timestamp:", timestamp);
  // const sts = `source=${data.source}&timestamp=${data.timestamp}&upload_preset=${data.upload_preset}`;
  // console.log(sts);

  // function sortObjKeysAlphabetically(obj) {
  //   var ordered = {};
  //   Object.keys(obj)
  //     .sort()
  //     .forEach(function (key) {
  //       ordered[key] = obj[key];
  //     });
  //   return ordered;
  // }

  // var sortedObj = sortObjKeysAlphabetically(data);
  // console.log(sortedObj);
  // params_to_sign via string
  var params_to_sign =
    "source=" +
    data.source +
    "&timestamp=" +
    data.timestamp +
    "&upload_preset=" +
    data.upload_preset;
  console.log(params_to_sign);

  // Get the signature using the Node.js SDK method api_sign_request
  var signature = cloudinary.utils.api_sign_request(
    params_to_sign,
    process.env.CLOUDINARY_API_SECRET
  );
  console.log(signature);
  res.json({ signature, timestamp: data.timestamp });
});

I get the data correctly, I even get a Hash back to the client, but for some reason, the signature isn't valid, and I can't upload the image.

Would appreciate some help on this one. Thanks in advance.

2

2 Answers

1
votes

The api_sign_request function takes a hash (object) and the API Secret String. In this case, you're supplying a String (params_to_sign) as the first parameter.

What you can do is pass the data directly as the first parameter to api_sign_request without any modifications. That will contain all the paramaters needed and match exactly what the front-end Upload Widget is sending.

0
votes

After being for hours with their support, I think I figured it out on my own,

Looks like the code that generates the signature isn't compatible with the version i used. On their documentation they ask to use var cloudinary = require("cloudinary").v2; But after I changed it back to var cloudinary = require("cloudinary");, everything started working good.

I also made some code changes: Server side:

router.get("/create-cloudinary-signature", (req, res) => {
  console.log("heyy");
  //console.log(req.query);
  const data = req.query.data;
  console.log(data);
  const timeStampSignature = Math.round(new Date() / 1000);
  console.log(timeStampSignature);


  // Get the signature using the Node.js SDK method api_sign_request
  var signature = cloudinary.utils.api_sign_request(
    data,
    process.env.CLOUDINARY_API_SECRET
  );
  console.log(signature);
  res.json({ signature: signature, timestampSignature: timeStampSignature });
});

And client-side:

<script type="text/javascript">
    let signature = '';
    let timestampSignature = '';

    let generateSignature = function (callback, params_to_sign) {
        $.ajax({
            url: "/business/create-cloudinary-signature",
            type: "GET",
            dataType: "text",
            //params _to_sign contains timestamp
            data: { data: params_to_sign },
            complete: function () { console.log("complete") },
            success: function (response, textStatus, xhr) {
                const res = JSON.parse(response);
                //console.log(res.signature)
                signature = res.signature;
                timestampSignature = res.timestampSignature;
                console.log(signature, timestampSignature);

                callback(signature);
            },
            error: function (xhr, status, error) { console.log(xhr, status, error); }
        });
    }

    const uploadSettings = {
        apiKey: "blabla",
        cropping: true,
        cloudName: 'name',
        uploadPreset: 'signed-preset'
    }

    var myWidget = cloudinary.createUploadWidget({ ...uploadSettings, uploadSignature: generateSignature, uploadSignatureTimestamp: timestampSignature }, (error, result) => {
        if (error) {
            console.log(error)
        }
        if (!error && result && result.event === "success") {
            console.log('Done! Here is the image info: ', result.info);
        }
    }
    )
    document.getElementById("logoUploader").addEventListener("click", function () {
        myWidget.open();
    }, false);
</script>