Signed Widget Upload - No Documents or Examples - React Client

Comments

13 comments

  • Avatar
    Aleksandar Kostadinov

    Hi Daniel,

    I see you commented on the Github thread as well and there you specifically asked about generating the signature. If I understanding correctly you're looking for a server-side example of the function that receives the parameters sent by the Upload Widget, signs them and returns the payload back? If this is the case then my colleague Yakir has previously shared a NodeJS example of receiving the parameters to sign from the Widget, generating the signature using the built-in helper method 'api_sign_request' and returning it back to the client. Link to this example: https://github.com/cloudinary/cloudinary_npm/issues/145#issuecomment-406852031

    This assumes that you have configured the Widget on the front-end and have supplied a function to the 'uploadSignature' parameter which will perform the AJAX request to the server-side code.

    If this is not the part you're looking for help on, then please let me know which steps you've set-up from (https://cloudinary.com/documentation/upload_widget#signed_uploads) and which part isn't working. Please share your Widget configuration and also the server-side code you're using to receive and sign the parameters and we'll be able to take a look at that for you.

    Best regards,

    Aleksandar

    0
    Comment actions Permalink
  • Avatar
    Arbortree Creations

    Hello Mr Kostadinov

    Yes, I am trying to get this to work. With no progress after many many hours. Currently, I am getting the "invalid signature" error on the widget when I try to upload.

    I do retrieve the signature from my server. The issue is, I cannot debug this or figure out why with the level of documentation provided, and/or examples.

    I have tried skimming params down to a bare minimum to attempt rule out making a mistake there. Unsigned works fine.

    I have also read the error message and used only the params in that error to make the signature, with no luck.



    Let me show you my code

    // SERVER ENDPOINT ---------------------------------------

    const CLOUDINARY_CONFIG = {
        cloud_name: "thearsenalreview",
        api_key: "*******************",
        api_secret: "****************************"
    };

    cloudinary.config(CLOUDINARY_CONFIG);


    aRouter.put("/api/images/signiture", async function(req, res) {
        try {
            let signiture = await cloudinary.utils.api_sign_request(req.body, CLOUDINARY_CONFIG.api_secret);
            res.status(200).json(signiture);
        } catch (err) { handleErr(res, err); }
    });

    ---------------------------------------------------------------

     

     

    // COMPONENT FUCNTION ----------------------------------------------------------------------

    import images from "../images";

    onUploadImage = () => {

        const { id } = this.props.user;

        const folder = "users/avatars/";
        const filename = id;

        var obj = this;

        images.upload(folder, filename, folders.avatar.preset, false, false, true, (error, result) => {
        if (error) console.log("ERROR ", error);
        else console.log("RESULT ", result);
        if (result.event && result.event === "close") {
            obj.props.fetchAvatars(id);
            if (result.event.failed) actions.showSnakbar("Image Uploaded Failed ...");
            else actions.showSnakbar("Image Uploaded Successfully ...");
        }
    });

    };

    ----------------------------------------------------------------------------------------

     

    // MY IMAGE JS FILE TO HANDLE IMAGE STUFF

    // UTILITIES
    import async from "async";
    import axios from "axios";
    import utils from "./utils";

    async function upload(
            folder,
            publicId = null,
            present = "arusers",
            multiple = true,
            showUploadMoreButton = true,
            singleUploadAutoClose = false,
            callback
        ) {
            const { cloudinary } = window;
            const now = utils.uDate.now();
            const params = {
                cloudName: "thearsenalreview",
                uploadPreset: present,
                sources: ["local"],
                maxFileSize: 5000000,
                maxImageWidth: 2000,
                minImageWidth: 400,
                apiKey: "**********",
                timestamp: now,
               clientAllowedFormats: ["png", "jpeg"],
               showPoweredBy: false,
               folder: folder,
               publicId: publicId,
               multiple: multiple,
               showUploadMoreButton: showUploadMoreButton,
               singleUploadAutoClose: singleUploadAutoClose
           };
           const { data } = await axios.put("/api/images/signiture", params);
           cloudinary.openUploadWidget({
               cloudName: "thearsenalreview",
               uploadPreset: present,
               sources: ["local"],
               maxFileSize: 5000000,
               maxImageWidth: 2000,
               minImageWidth: 400,
               apiKey: "**********",
               uploadSignature: data,
               timestamp: now,
               clientAllowedFormats: ["png", "jpeg"],
               showPoweredBy: false,
               folder: folder,
               publicId: publicId,
               multiple: multiple,
               showUploadMoreButton: showUploadMoreButton,
               singleUploadAutoClose: singleUploadAutoClose
          }, callback);
    };

    export default {
         upload: upload
    };

    I have read your example shared above and all similar examples read all documentation. or most of it anyways. Googled and read all search results for this topic. Wasn't anything helpful

    Thanks 

    Daniel

    0
    Comment actions Permalink
  • Avatar
    Shirly Manor

    Hi Daniel,

     

    Looking at our logs, it might be invalid Signature. The string to sign - need to includes the folder without the last `/` For example: 'folder=users/XXX/&public_id=auth0|XXXX&source=uw&timestamp=1566603428&upload_preset=XXXXX'.

    You can read more about it here:

    https://support.cloudinary.com/hc/en-us/articles/203817991-How-to-generate-a-Cloudinary-signature-on-my-own-

    Can you please try again if it doesn't work can you please open a support ticket at support@cloudinary.com ?

     

    0
    Comment actions Permalink
  • Avatar
    Arbortree Creations

    Hi

    I tried removing the last character from the folder, with no luck.

    Your link doesn't provide any explanation. Explains a string but nothing in relation to me. I have read this and all other of your links many times.

    Daniel

    0
    Comment actions Permalink
  • Avatar
    Aleksandar Kostadinov

    Hi Daniel,

    Please note that the Widget would automatically send the parameters needed to sign and you wouldn't need to create this list yourself like you're doing with the 'params' constant which can be removed. Currently, your params list of parameters is supplying options that shouldn't be included when generating a signature thus you're getting an invalid signature. Hence it's easier if you accept the automatic ones that the Widget supplies so you won't need to manage/worry about which ones should and shouldn't be supplied.

    When providing a function to the uploadSignature option, the Widget would automatically invoke this function upon upload and pass in 2 parameters. Therefore the function you supply should accept 2 parameters also. The first argument it will pass is a resultCallback (function) and the second is an object with the relevant upload parameters that are needed for generating the signature.

    What you should do is define a function that accepts those parameters and inside it perform your axios request to your backend for the signature. Then on a successful generation, supply the result of this to the callback function that the Upload Widget supplies.

    For example;

    // This is an example function which you can supply to the Upload Widget generateSignature:
    // Notice the signarure is passed back to the callback function for the widget once the AJAX request completes (onsuccess)

    var generate_signature = function( callback, params_to_sign ) {
    $.ajax({
    url : "https://www.example.com/generate_signature_endpoint",
    type : "POST",
    dataType: "text",
    data : { data: params_to_sign },
    success : function(signature, textStatus, xhr) { callback(signature); },
    error : function(xhr, status, error) { /*handle error*/ }
    });
    }

    cloudinary.openUploadWidget({
    cloudName : '<cloud_name>',
    apiKey : <api_key>,
    uploadSignature : generate_signature,
    });

    In your backend code used for signing you can accept and sign all the params that the Widget provides and return the resulting string.

    May I please ask you to to give this a try and let me know how it goes?

    Best regards,

    Aleksandar

    0
    Comment actions Permalink
  • Avatar
    Arbortree Creations

    Ok Thanks 

    This worked. I needed this function as you explained. 

    var generate_signature = async function(callback, params_to_sign) {
       const { data } = await axios.put("/api/images/signiture", params_to_sign);
       callback(data);
    }

    I think the part I refused to catch in your documentation was that the "uploadSignature" has to be a function itself, and not the result from a function. Otherwise, it doesn't work. 

    Seeing this like this 

    • signature: bfd09f95f331f558cbd1320e67aa8d488770583e

    This allowed me to think the result from the function is what's required, not the actual function. 

    So I take my hat off to you guys and apologise for losing the plot. 

    I would advise though adding a single line of text saying "Add the function as the upload signature, not the prior function results".

    Thanks, Guys. I do look forward to building many successful apps with your people. 

    Daniel

    0
    Comment actions Permalink
  • Avatar
    Aleksandar Kostadinov

    Hi Daniel,

    Thanks for your reply, great to hear this has helped and you have got it working!

    Your understanding was correct in that the uploadSignature can both be a Function that generates the signature or a String with the computed signature. I believe the confusion might've come from the parameters that actually need to be signed when generating the signature as in your example (const params) you were passing values which shouldn't be included in the signature generation and that resulted in an invalid signature.

    https://cloudinary.com/documentation/upload_widget#signed_uploads - Step 3 in this guide highlights both String and Function cases, but more importantly is the last part bullet point that links to the part that describes the signature generation (https://cloudinary.com/documentation/upload_images#generating_authentication_signatures) and specifically which parameters should be included. The main caveat with using a String as the uploadSignature rather than Function is that it needs to be calculated on page load and thus requires all parameters that need signing to be known. This means if you need some user input, such as cropping enabled in the widget, then it won't work as that is only known during upload and not page load time. Personally I prefer passing a Function that will be called on Upload as it allows you not to worry about a signature generation on page load time and knowing all parameters beforehand.

    As always, don't hesitate to contact us with any questions/issues or just to leave feedback.

    Best regards,

    Aleksandar

    0
    Comment actions Permalink
  • Avatar
    AbdulGafar Olamide Ajao

    Hello, after following the docs and also trying to follow the above-proferred solution, I'm still unable to upload my files via the upload widget, please help.

    Here is my vueJS client-side code

    methods: {
    async generateUploadSignature(callback, paramsToSign) {
    console.log(paramsToSign)
    const data=await this.$axios.$post(
    'http://localhost:3000/api',
    paramsToSign
    )
    console.log(data)
    callback(data)
    // console.log(paramsToSign)
    // callback(signature)
    },
    createCloudinaryWidget(){
    // eslint-disable-next-line no-undef
    const newWidget=cloudinary.createUploadWidget(
    {
    cloudName: 'befittinglife',
    uploadPreset: 'eventimeline',
    apiKey: '169241722441159',
    timestamp: Math.round(new Date().getTime() / 1000),
    multiple: false,
    // maxFiles: 1,
    // cropping: true,
    clientAllowedFormats: ['jpg', 'png', 'jpeg', 'webp', 'gif'],
    showPoweredBy: false,
    uploadSignature: this.generateUploadSignature,
    },
    (error,result)=>{
    // check upload success
    if (!error&&result&&result.event==='success') {
    console.log('result',result.info)
    }else{
    console.log('error',error)
    }
    }
    )
    return newWidget
    },
    openCloudinaryWidget(){
    cons twidget=this.createCloudinaryWidget()
    widget.open()
    },
    }

    This is my server-side code

    (req, res) => {
    console.log(req.body);
    cons tsignature=cloudinary.utils.api_sign_request(
    req.body,
    process.env.API_SECRET
    );
    // const { name = "World" } = req.query;
    res.send(signature);
    };
    0
    Comment actions Permalink
  • Avatar
    Daniel Mendoza

    Hi,

     

    Can you try removing `timestamp: Math.round(new Date().getTime() / 1000)` from createUploadWidget.

    Let me know if that works?


    Regards,
    Daniel

    0
    Comment actions Permalink
  • Avatar
    AbdulGafar Olamide Ajao

    It's still not working.
    The timestamp wasn't there before, I added it when I saw the above posts. Either ways, it's not working.

    0
    Comment actions Permalink
  • Avatar
    Michal Kuperman

    Hi,

    I've created a signature on our end with the upload params and it seems to be a different signature than created by your server. This can be due to using the wrong timestamp or a typo in the API secret that is used.

    Can you please check that you are using the exact timestamp that is passed in the params_to_sign, and in addition validate the API secret and let us know if it works?

    If privacy is a concern please open a support ticket with your account details to  support@cloudinary.com to continue this investigation.

    Best,

    Michal

    0
    Comment actions Permalink
  • Avatar
    AbdulGafar Olamide Ajao

    Yeah! It worked!!! My API_SECRET was incorrect... I was using small letter l(l for leg) in place of the capital letter I(I for Ice-cream).

    You're the best.

    0
    Comment actions Permalink
  • Avatar
    Michal Kuperman

    Great! Thanks for the update.

    0
    Comment actions Permalink

Please sign in to leave a comment.