Using form data (Content-Type: multipart/form-data) in custom apps

:bullseye: What is your goal?

I would like to upload a binary file to S3 using Content-Type: multipart/form-data via POST from a custom app module.

:thinking: What is the problem?

The symptom I’m seeing is that S3 returns HTTP 403 (instead of the expected 201), despite all authentication fields being set correctly in the body. This same flow, with the form data fields set in the same way, works via other methods (cURL, other workflow platforms besides Make, ..).

I believe this problem is related to:

I believe, though I can’t yet prove, that the root cause here is that Make’s framework continues adding an “Authorization” header despite my explicitly unsetting the header in my module. This will cause S3 to return a 403. The framework seems to keep trying to insert a bearer token, as defined in the “Connection” section. I notice at least one other user seems to have had the same problem getting a module to connect to AWS S3 without an “Authorization:” header:

The suggestion in that thread is to use a separate, empty connection just for that module. That recommendation however doesn’t work. When I try, Make gives me the error “Provided account ‘x’ is not compatible with the ‘y’ module.”

Can anyone point to a working example, that has been verified with a successful run, of a custom Make.com app performing a successful multipart/form-upload request to S3?

Thanks in advance for any help or advice on this!

:test_tube: What have you tried so far?

I have followed the developer documentation for making multipart/form-data requests:

Details from the module I’m writing follow:

My “Communication” section:

[
	{
		// Step 1: Get a file upload ID.
		"url": "/file-uploads",  // Relative to base URL
		"method": "POST",
		"body": {
			"content_type": "{{parameters.content_type || 'application/pdf'}}",
			"filename": "{{parameters.file_name}}"
		},
		"response": {
			"temp": {
				"fileUploadId": "{{body.id}}",
				"fileUploadFields": "{{body.upload_fields}}",
				"fileUploadUrl": "{{body.upload_url}}"
			}
		}
	},
	{
		"url": "{{temp.fileUploadUrl}}",
		"method": "POST",
		"type": "multipart/form-data",
		"body": {
			"acl": "{{temp.fileUploadFields.acl}}",
			"key": "{{temp.fileUploadFields.key}}",
			"policy": "{{temp.fileUploadFields.policy}}",
			"signature": "{{temp.fileUploadFields.signature}}",
			"x-amz-algorithm": "{{temp.fileUploadFields['x-amz-algorithm']}}",
			"x-amz-credential": "{{temp.fileUploadFields['x-amz-credential']}}",
			"x-amz-date": "{{temp.fileUploadFields['x-amz-date']}}",
			"x-amz-security-token": "{{temp.fileUploadFields['x-amz-security-token']}}",
			"Content-Type": "application/pdf",
			"file": {
				"value": "{{parameters.file_data}}",
				"options": {
					"filename": "{{parameters.file_name}}"
				}
			}
		},
		"headers": {
			"authorization": "",
			"content-type": "multipart/form-data"
		}
	},
	{
		"url": "/plans",
		"method": "POST",
		"body": {
			"name": "{{parameters.planName}}",
			"file_upload_id": "{{temp.fileUploadId}}"
		}
	}
]

My “Mappable parameters” section:

[
	{
		"name": "file_name",
		"type": "text",
		"label": "File Name",
		"required": true,
		"semantic": "file:name"
	},
	{
		"name": "file_data",
		"type": "buffer",
		"label": "File",
		"required": true,
		"semantic": "file:data"
	},
	{
		"name": "content_type",
		"type": "text",
		"label": "Content Type",
		"required": false
	}
]
1 Like

Hi @Aaron_Shon,

Try unsetting the authorization header like this:

"headers": {
		"Authorization": "{{undefined}}",
        "content-type": "multipart/form-data"
},

Cheers,
Henk

1 Like

Thanks @Henk-Operative ! That removed the Authorization header just as I wanted–but it turns out something else was necessary to get a POST to S3 working.

I recalled that in a multipart/form-data POST to S3, the file form field (the thing that actually contains the binary data from the file) must come last in the form body. See this AWS documentation for reference, which clearly states that file: must come last:

Since I’d listed file: as the last entry in the .iml of the Communication section, I’d assumed that the same order would be sent over the wire. Unfortunately, this isn’t the case; the entries in the form are reordered compared to what is specified in the file (this can be seen using the Make Chrome debug extension).

I ended up creating a custom JS function to create the form fields in proper order, and used that to create the body section in the Communication section of the module. The scenario now correctly uploads to S3 :slight_smile: (It seems like it would be worthwhile to include a hint on this in the Make documentation for multipart/form-data, as the form field reordering behavior might be surprising to other folks as well..)

Thanks again for all the help!

Yes this is quite a confusing behavior. Great that you found a working salutation and sharing here!

Cheers,
Henk