Skip to content
Advertisement

401 Unauthorized error when uploading an image to Cloudinary in specific folder on signed upload preset

I have a social media app, and when a user wants to upload a profile picture, they must obtain a signature from the server. This code works when I remove the upload preset in the front end, but I want the upload preset so that the user can upload to a specific folder, and it is a signed upload preset, so why am I getting 401 unauthorized error?

on the backend

 const generateSignature = async (req, res) => {
      /* It's just getting the current time in seconds. */
      const timestamp = Math.round(new Date().getTime() / 1000);
      try {
        const signature = cloudinary.utils.api_sign_request(
          {
            timestamp,
          },
          cloudinaryConfig.api_secret
        );
    
        res.status(200).json({
          success: true,
          timestamp,
          signature,
          api_key: cloudinaryConfig.api_key,
          cloud_name: cloudinaryConfig.cloud_name,
        });
      } catch (err) {
        console.log(err);
        res.status(500).json({ success: false, message: "server error try again" });
      }
    };

on the frontend

const { timestamp, signature, api_key, cloud_name } =
      signatureResponse.data;

 const formData = new FormData();
    formData.append("file", image);
    formData.append("upload_preset", "uploadProfilePicture");// if i remove this line it works 
    formData.append("api_key", api_key);
    formData.append("cloud_name", cloud_name);
    formData.append("signature", signature);
    formData.append("timestamp", timestamp);
    console.log(formData);
    const cloudinaryResponse = await axios.post(
      `https://api.cloudinary.com/v1_1/${cloud_name}/image/upload`,
      formData
    );

Advertisement

Answer

In your current code, you’re only generating the authentication signature for your upload request using the timestamp parameter, however, that actually needs to include all parameters you’re passing as part of the API call to Cloudinary excluding file, cloud_name, api_key and resource_type.

In other words, based on your example, if you want to send an upload_preset parameter to Cloudinary, you would need to include the upload_preset: "uploadProfilePicture" in the hash you’re passing to api_sign_request() so that this parameter you will then send in the upload request be included in the signature generation. This is the reason why removing that parameter from the upload request results in a successful upload as at that point you will be passing the same values as you’re generating the signature with (i.e. just timestamp).

The same applies to any other parameters you want to pass to Cloudinary. For example, if you then want to send the use_filename parameter set to true then you would need to also include that in your signature generation code. Effectively, your frontend code should send to your server all parameters that you want to send to Cloudinary so that your backend can generate a signature based on them.

Unrelated to the above, note that the resource_type and cloud_name parameters can be removed from your FormData. That is because both of those are already passed to Cloudinary via the API URL endpoint – i.e. https://api.cloudinary.com/v1_1/${cloud_name}/image/upload – where your cloud_name will be interpolated and the resource_type will be image.

Please also see the following reference for making direct uploads and signature generation for interacting with the Cloudinary API. It includes further examples and a step-by-step guide: https://cloudinary.com/documentation/upload_images#uploading_with_a_direct_call_to_the_rest_api

User contributions licensed under: CC BY-SA
4 People found this is helpful
Advertisement