[iOS] Signed upload fails: "Upload preset must be specified when using unsigned upload"
Hi everyone, I'm trying to upload an image with the signedUpload function.
Right after the call i get this error from the SDK:
"[Cloudinary]:[Error]: CLDNetworkCoordinator.swift.getSignedRequestParams(_:)[79]: Must supply api key for a signed request"
And after, when i get the respose I get this error:
"Upload preset must be specified when using unsigned upload"
This is my code:
The sensitive data is obscured
let configuration = CLDConfiguration(cloudName: "xxxxxx", secure: true)
let cloudinary = CLDCloudinary(configuration: configuration)
CLDCloudinary.logLevel = .debug
let params = CLDUploadRequestParams().setPublicId("testID")
let signatureBackend = webApiService.getSignature(for publicId: "testID")
let timestamp = NSNumber(value: signatureBackend.data.attributes.timestamp)
let signature = CLDSignature(signature: signatureBackend.data.attributes.signature, timestamp: timestamp)
params.setSignature(signature)
params.setParam("api_key", value: "xxxxxx")
let request = cloudinary
.createUploader()
.signedUpload(data: data, params: params)
Thank you very much for your help
-
Official comment
Your backend looks like it's set up correctly and all of your credentials are hidden. Note that the API_SECRET is the credential that most corresponds to a password, and should therefore never be included in your front end code. The API_KEY, which more corresponds to a username can be included in your front end code without the security risk.
What Stephen suggested in the earlier posts is that the API_KEY be included in the front end code
, so the iOS front end configuration command would look like this:
let config = CLDConfiguration(cloudName: "xxx",apiKey:"xxx", secure: true)
This key is part of the signature, but is also needed in the front end config.
-
Hi,
In the example you provided, it looks like you haven't provided the API key and secret in the SDK configuration, so the SDK code is unable to sign your request, and that's why the API call fails.
Although you've added it to the request parameters, that doesn't allow the SDK code to access it for signing purposes.You can do something like this to configure the SDK with the API secret
CLDConfiguration(cloudName: "test123", apiKey: "a", apiSecret: "b", secure: true);
Separately, may I ask what is the reason for performing a signed upload from iOS Code? If this is for testing or internal purposes that's OK, but If you were to put the API key into client code, anyone who has access to the app would be able to extract the key from the app's code and make requests on your behalf to our API.
Regards,
Stephen-2 -
it's a direct upload, the signature is created from the backend.
0 -
Hi Stephen, I followed the example provided here: https://cloudinary.com/documentation/ios_image_and_video_upload
If I use the configuration with the secret keys, it works, but it's not a recomended and secure option, as suggested by Cloudinary.
example of signed upload by Cloudinary:let params = CLDUploadRequestParams() .setPublicId("newId") var mySig = MyFunction(params) // your own function that returns a signature generated on your backend params.setSignature(CLDSignature(signature: mySig.signature, timestamp: mySig.timestamp)) let request = cloudinary.createUploader().signedUpload( url: "imageFile.jpg", params: params)
0 -
Hi again,
May I ask you to please try to set the API key using CLDConfiguration, then set the signature in the params for the uploader in the same manner as the in your first code sample and the documentation?
Many of our SDK methods will discard parameters that they're not expecting, so I suspect the API Key isn't being passed through to the API call using the method in your first message, but should be passed if the SDK is configured with it via CLDConfiguration.
Thanks,
Stephen
1 -
Thanks, I've tried and finally it worked.
Thank you very much Stephen!0 -
Hi Stephen
I have the same problem but can't figure it out how to solve it. I am trying to make a signed request.
In my iOS App I am making a GET request to my node.js backend to get the signature and timestamp. This looks something like this:const cloudinary = require('cloudinary').v2;
cloudinary.config({
cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
api_key: process.env.CLOUDINARY_API_KEY,
api_secret: process.env.CLOUDINARY_API_SECRET,
})
var timestamp = Math.round((new Date).getTime() / 1000);
// Get the signature using the Node.js SDK method api_sign_request
var signature = cloudinary.utils.api_sign_request({
timestamp: timestamp,
upload_preset: "presetname"
}, process.env.CLOUDINARY_API_SECRET);
return { signature, timestamp }After receiving the signature I try to upload it to Cloudinary:
let config = CLDConfiguration(cloudName: "xxx", secure: true)
let cloudinary = CLDCloudinary(configuration: config)
CLDCloudinary.logLevel = .debug
let params = CLDUploadRequestParams()
.setUploadPreset("presetname")
params.setResourceType(.video) // set resource type
params.setSignature(CLDSignature(signature: signature, timestamp: NSNumber(value: timestamp)))
// upload video
cloudinary.createUploader().signedUpload(url: videoUrl, params: params, ... and so on
It works if I put my API Key and Secret into CLDConfiguration like this:
let config = CLDConfiguration(cloudName: "cloud name", apiKey: "xxx", apiSecret: "xxx", secure: true)But shouldn't API Key and Secret be hidden as Rawfish mentioned?
How can I upload without revealing the keys?
Thanks in advance!0 -
Your backend looks like it's set up correctly and all of your credentials are hidden. Note that the API_SECRET is the credential that most corresponds to a password, and should therefore never be included in your front end code. The API_KEY, which more corresponds to a username can be included in your front end code without the security risk.
What Stephen suggested in the earlier posts is that the API_KEY be included in the front end code
, so the iOS front end configuration command would look like this:
let config = CLDConfiguration(cloudName: "xxx",apiKey:"xxx", secure: true)
This key is part of the signature, but is also needed in the front end.1 -
Hi Rebecca
Ohh now I get it! Thank u so much, it works now :)0 -
Hi,
I'm facing the same issue, I tried all the above code for front end for iOS.
My front end (iOS) code is as below :
let params = CLDUploadRequestParams()
params.setResourceType(resourceType)
params.setPublicId("\(PREF.USER_ID.get(DefaultValue: "0") ?? "0")_\(Date().timeIntervalSince1970)")
self.shared.generateSignedKey(eager: "", publicID: params.publicId ?? "") { (signedKey, timeStamp) in
if timeStamp.count > 0 {
params.setSignature(CLDSignature(signature: signedKey, timestamp: NSNumber.init(value: Int64(timeStamp) ?? 0)))
params.setParam("api_key", value: CLOUD_APIKEY)
onComplete(cloudinary.createUploader().signedUploadLarge(url: url, params: params, chunkSize: 5 * 1024 * 1024), nil)
} else {
onComplete(nil, signedKey)
}
}self.shared.generateSignedKey(eager: "", publicID: params.publicId ?? "")
This function is used for generating signature from backend.
But all time it gives me error like below:
Type parameter is not allowed when using unsigned upload. Only upload_preset,callback,public_id,folder,tags,context,metadata,face_coordinates,custom_coordinates,source,filename_override upload parameters are allowed., Upload preset must be whitelisted for unsigned uploads
0 -
Thank you for sharing your code. I see you are using a
cloudinary
object in the call to onComplete:onComplete(cloudinary.createUploader().signedUploadLarge(url: url, params: params, chunkSize: 5 * 1024 * 1024), nil)
It would be helpful to see how you configured the creation of this object. I think that the problem may be that the Config doesn't include the API_KEY. I can see that you send the API_KEY as one of the params of your `
uploader().signedUploadLarge`
function. I think you may also need to add the API_KEY to the Config function parameter list that is used to create thecloudinary
object like this:let config = CLDConfiguration(cloudName: "CLOUD_NAME", secure: "true", API_KEY:"<your API KEY>") let cloudinary = CLDCloudinary(configuration: config)
Don't worry about exposing the API_KEY in front end code as it is not a password. Think of it more like a username and therefore not a violation of security in the way that adding the API_SECRET would be.
If this doesn't help solve your problem, please share how you configured your
cloudinary
object. Adding the API_KEY to the Config is what helped other developers who posted on this page.0 -
I have already included the API_KEY in configuration parameter like below:
cloudinary = CLDCloudinary(configuration: CLDConfiguration(cloudName: CLOUD_NAME, apiKey: CLOUD_APIKEY, secure: true))
0 -
We tested the code you provided here with a signature generator function and it works. Can you share the code you used in your signature generator function?
0 -
Looking more closely at your error: `Type parameter is not allowed when using unsigned upload`, it appears that your upload request is being processed as an unsigned upload. This makes me wonder if your signature param is being created correctly. Is it possible for you to log or debug the signature value returned from your generate signature function to make sure it looks valid? I'm not sure which SDK you're using on the backend to generate your signature, but it should look something like the node.js code below. Make sure your timestamp, public id, and API_SECRET are all valid in the backend. Your code doesn't show how `signedKey` is generated.
```
var signature = cloudinary.utils.api_sign_request({ timestamp: timestamp, public_id: 'sample_image'}, process.env.API_SECRET);
0 -
Thanks Rebecca for the suggestions and time you spent for finding out the issue.
We got it resolved now.
You are right we have made mistakes in creating signature and now we are able to resolve it.
0 -
Hi! I'm, having a similar issue here. I'm getting 502 - Bad Gateway when uploading an image to Cloudinary.
For some reason, everything is working ok with Axios but when I try to use fetch from js, it returns the 502
Here is a snippet of my code:const CLOUDINARY_API_URL: string = `https://api.cloudinary.com/v1_1/${CLOUDINARY_CLOUD_NAME}`;
const signData = await getSignUploadForm(); //endpoint getting signature, this is working
const formData = new FormData();
formData.append("file", img);
formData.append("api_key", CLOUDINARY_API_KEY);
formData.append("timestamp", signData.timestamp);
formData.append("signature", signData.signature);
const response: any = await fetch(CLOUDINARY_API_URL + '/auto/upload',
{
method: 'POST',
body: formData,
mode: 'no-cors',
headers: {
'Content-Type': 'multipart/form-data',
}
}).then((res) =>
console.log(res)
).catch(e => {
console.log(e);
}) ;0
Post is closed for comments.
Comments
16 comments