POST uploads to S3 bucket failing with http 405

0

Problem

I'm trying to upload files to an S3 bucket using presigned post requests (roughly following this blog post). My server (node) generates a presigned post request using createPresignedPost, and then returns it to the client (React), which uses it to upload a user-generated file to our S3 bucket.

Currently, any POST requests from the client to the bucket all fail with 405 Method Not Allowed. I checked the response headers, and saw that the Allow header does not list POST, although the Access-Control-Allow-Methods does.

Question: is there something else I need to do in order to allow POST requests to the bucket?

note: the reason I'm trying to used presigned POST requests as opposed to the regular presigned urls (which use PUT), is because I need to specify restrictions on file type and size, which the latter doesn't seem to support - if i'm mistaken about this, and anyone knows a way to specify restrictions through presigned PUT requests, then this would also solve my problem!

Context / other info

The S3 bucket has no policy. It does have a CORS policy, which is

[
    {
        "AllowedHeaders": [
            "*"
        ],
        "AllowedMethods": [
            "GET",
            "POST",
            "PUT",
            "HEAD",
            "DELETE"
        ],
        "AllowedOrigins": [
            "http://localhost:3000",
            "http://localhost:8000",
            ...
        ],
        "ExposeHeaders": [
            "ETag",
            "x-amz-meta-custom-header"
        ]
    }
]

The server-side code for generating the presigned POST request:

const signS3PostRequest = async ({ fileName, fileType, prefix, bucket }) => {
  const params = {
    Bucket: bucket,
    Fields: {
      $key,
    },
    Expires: 60,
    ContentType: fileType,
    ACL: 'private',
    Conditions: [['content-length-range', 0, 10000000]],
  }

  const s3 = new aws.S3({
    apiVersion: 'latest',
    region: $region,
    accessKeyId: process.env.AWS_ACCESSKEYID,
    secretAccessKey: process.env.AWS_SECRETACCESSKEY,
  })

  return new Promise((resolve, reject) => {
    s3.createPresignedPost(params, (err, data) => {
      if (err) {
        ...
      } else {
        resolve(data)
      }
    })
  })
}

And the client-side code for uploading a file using this presigned request:

export const uploadFileToS3UsingSignedPostRequest = (
  file,
  signedRequestData
) => {
  let form = new FormData()
  Object.keys(signedRequestData.fields).forEach((k) =>
    form.append(k, signedRequestData.fields[k])
  )
  form.append('file', file)
  return fetch(signedRequestData.url, { method: 'POST', body: form })
}

The response I get back from s3.createPresignedPost looks like this:

{
	"url": "https://s3.amazonaws.com/$BUCKET_NAME",
	"fields": {
		"key": "test/6zb3rewm8cjydpslti3g/test.jpeg",
		"bucket": "$BUCKET_NAME",
		"X-Amz-Algorithm": "AWS4-HMAC-SHA256",
		"X-Amz-Credential": "$CREDENTIAL",
		"X-Amz-Date": "20221230T090441Z",
		"Policy": "eyJleHBpcmF0aW9uIjoiMjAyMi0xMi0zMFQwOTowNTo0MVoiLCJjb25kaXRpb25zIjp...",
		"X-Amz-Signature": "$SIGNATURE"
	}
}

and the response from S3 when trying to use the signed post request to upload the file looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<Error>
    <Code>MethodNotAllowed</Code>
    <Message>The specified method is not allowed against this resource.</Message>
    <Method>POST</Method>
    <ResourceType>OBJECT</ResourceType>
    <RequestId>HTYWNACA6A1J6BBS</RequestId>
 <HostId>BSV4mFijEfgZEiMu6nVxqP9aocuhAwN7et/UfrO4r1PmkeFZ8nVUsNmcLVZoWCe06zrEFJXDfsE=</HostId>
</Error>
asked a year ago3393 views
1 Answer
0
AWS
vtjean
answered a year ago

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.

Guidelines for Answering Questions