Reputation: 41
I'm generating an AWS S3 presigned post object on my server using
. I'm then trying to upload a file directly to the S3 bucket from the client using fetch using the presigned post url & fields, but am getting a s3.createPresignedPost()
403 Forbidden
.
I've tried manually adding form fields to my FormData object to directly match this example: https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-post-example.html but continue to receive the 403 error.
Server-side function for generating the post object
const AWS = require("aws-sdk/global"); const S3 = require("aws-sdk/clients/s3"); const uuidv4 = require("uuid/v4"); AWS.config.update({ accessKeyId: process.env.S3_KEY_ID, secretAccessKey: process.env.S3_SECRET_KEY, region: "us-east-1" }); const s3 = new S3(); const getPresignedPostData = (bucket, directory) => { const key = `${directory}/${uuidv4()}`; const postData = s3.createPresignedPost({ Bucket: bucket, Fields: { Key: key, success_action_status: "201" }, Conditions: [{ acl: "public-read" }], ContentType: "image/*", Expires: 300 }); return postData; };
Returns something that looks like:
{ fields: { Key: "5cd880a7f8b0480b11b9940c/86d5552b-b713-4023-9363-a9b36130a03f" Policy: {Base64-encoded policy string} X-Amz-Algorithm: "AWS-HMAC-SHA256" X-Amz-Credential: "AKIAI4ELUSI2XMHFKZOQ/20190524/us-east-1/s3/aws4_request" X-Amz-Date: "20190524T200217Z" X-Amz-Signature: "2931634e9afd76d0a50908538798b9c103e6adf067ba4e60b5b54f90cda49ce3" bucket: "picture-perfect-photos" success_action_status: "201" }, url: "https://s3.amazonaws.com/picture-perfect-photos" }
My client side function looks like:
const uploadToS3 = async ({ fields, url }, file) => { const formData = new FormData(); Object.keys(fields).forEach(key => formData.append(key, fields[key])); formData.append("file", file); try { const config = { method: "POST", body: formData }; const response = await fetch(url, config); if (!response.ok) { throw new Error(response.statusText); } const data = await response.json(); return data; } catch (err) { console.log(err.message); } };
And my S3 bucket CORS config is as follows:
<?xml version="1.0" encoding="UTF-8"?> <CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"> <CORSRule> <AllowedOrigin>*</AllowedOrigin> <AllowedMethod>GET</AllowedMethod> <AllowedMethod>POST</AllowedMethod> <AllowedMethod>PUT</AllowedMethod> <AllowedMethod>DELETE</AllowedMethod> <AllowedHeader>*</AllowedHeader> </CORSRule> </CORSConfiguration>
I expect to get the XML document that is sent when success_action_status: "201"
is set, but am continually getting 403 Forbidden
Upvotes: 4
Views: 4378
Reputation: 26423
The only other answer on this thread is talking about totally different S3 API call.
There are 2 similar but different APIs:
I myself needed createPresignedPost
and was using getSignedUrl
before because i want to limit the file size which can be uploaded to much less then what AWS S3 supports. In my case I let users to upload images. Up to 10MB in size. While upload coming from client side I need way to limit file upload size. If you google around this situation, people suggest to delete already uploaded big files, but i don't like letting to upload to begin with.
So, while using the actual createPresignedPost
API call you get back not only the url
but fields
too.
Fields is object with key-value pairs.
On client side you need:
Send POST request to the url (getSignedUrl
is requiring PUT instead) and all the fields you got you must provide as form's fields. Only extra form field is called file
and that's where you put your file you upload.
Here is sample PostMan screenshot with all form filed up:
Upvotes: 2