22

I am trying to upload images to s3 with a pre signed url generated from the aws sdk.

router.get('/upload-url', async(req, res) => {
    try {

        AWS.config.update({
            secretAccessKey: process.env.AWS_SECRET_ACCESS,
            accessKeyId: process.env.AWS_ACCESS_KEY,
            region: 'ap-southeast-1'

        });

        const s3 = new AWS.S3();
        var params = { Bucket: process.env.bucket_name, Key: 'products', ContentType: 'image/jpeg' };
        s3.getSignedUrl('putObject', params, function(err, url) {
            if (err) {
                throw (err)
            }
            res.status(200).send({ link: url })
        })

    } catch (err) {
        res.status(400).send({ message: err.message })
    }
})

with the return url above, It give an error when I trying to access it

<Error>
<script class="__REQUESTLY__SCRIPT">(function(namespace) { window[namespace] = window[namespace] || {}; window[namespace].responseRules = {}; let open = XMLHttpRequest.prototype.open; XMLHttpRequest.prototype.open = function(method) { this.addEventListener('readystatechange', function() { if (this.readyState === 4 && window[namespace].responseRules.hasOwnProperty(this.responseURL)) { const responseRule = window[namespace].responseRules[this.responseURL]; const {response, id} = responseRule; const responseType = this.responseType; let customResponse; customResponse = response.type === 'code' ? responseRule.evaluator({ method, url: this.responseURL, requestHeaders: this.requestHeaders, requestData: this.requestData, responseType: this.responseType, response: this.response }) : response.value; Object.defineProperty(this, 'response', { get: function () { if (response.type === 'static' && responseType === 'json') { return JSON.parse(customResponse); } return customResponse; } }); if (responseType === '' || responseType === 'text') { Object.defineProperty(this, 'responseText', { get: function () { return customResponse; } }); } window.postMessage({ from: 'requestly', type: 'response_rule_applied', id }, window.location.href); } }, false); open.apply(this, arguments); }; let send = XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.send = function(data) { this.requestData = data; send.apply(this, arguments); }; let setRequestHeader = XMLHttpRequest.prototype.setRequestHeader; XMLHttpRequest.prototype.setRequestHeader = function(header, value) { this.requestHeaders = this.requestHeaders || {}; this.requestHeaders[header] = value; setRequestHeader.apply(this, arguments); } })('__REQUESTLY__')</script>
<Code>SignatureDoesNotMatch</Code>
<Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message>
<AWSAccessKeyId>DHAWUIHDUIAWHDAWUI</AWSAccessKeyId>
<StringToSign>GET 1594960145 /bucketname/products</StringToSign>
<SignatureProvided>oD2y%2Ftv04ernxLiNdMAETiebi1KXY%3D</SignatureProvided>
<StringToSignBytes>47 45 54 0a 0a 0a 31 35 30 34 39 36 30 31 34 35 0a 2f 64 65 76 2e 6b 6f 6c 2e 73 68 6f 70 2f 70 70 6f 64 75 63 74 73</StringToSignBytes>
<RequestId>11A7BD415D04AAFE7E</RequestId>
<HostId>3zFeVQCbO+LraKZ7sR1j7rgMR9KdyOEqKFGX/5QCWXMhXLBubzre7Lb1eun/zATJU/xz69mpg+o=</HostId>
</Error>

I have been looking around other posts, but not yet found a solution.

A few suggestion that I found was:

  • not to include "/" in SECRET ACCESS KEY. (my key doesn't contain slash)
  • incorrect credentials (I have tested the keys with aws cli, it works)
  • updating bucket permission and policy. ( I have updated to all access on the policy)

My bucket settings are as follow:

Bucket policy

{
    "Version": "2012-10-17",
    "Id": "Policy1594951813323",
    "Statement": [
        {
            "Sid": "Stmt15949510950",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::<AccountNumber>:root"
            },
            "Action": "s3:*",
            "Resource": "arn:aws:s3:::<bucketname>/*"
        }
    ]
}

CORS configuration

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
    <AllowedOrigin>*</AllowedOrigin>
    <AllowedMethod>POST</AllowedMethod>
    <AllowedMethod>GET</AllowedMethod>
    <AllowedMethod>PUT</AllowedMethod>
    <AllowedMethod>DELETE</AllowedMethod>
    <AllowedMethod>HEAD</AllowedMethod>
    <MaxAgeSeconds>3000</MaxAgeSeconds>
    <AllowedHeader>*</AllowedHeader>
    <AllowedHeader>Authorization</AllowedHeader>
</CORSRule>
</CORSConfiguration>


UPDATED SOLUTION

Use PUT Instead of POST for the pre-sign url to upload file to S3
Jackal
  • 493
  • 1
  • 5
  • 16

3 Answers3

2

I solved this problem by recreating the key pair. As it turned out, my secret key started with an equals '=' sign. Not only did that mess with the downloaded CSV, but my guess is that is messed with the signature generation parsing software. Re-generating the key pair made the system work.

2

Not sure about the s3 bucket policy side, which is maintained by our backend team. But I was able to upload files to our public-asset bucket using both post and put method.

POST

Generate presigned URL

response = s3.generate_presigned_post(Bucket=BUCKET,
                                        Key=KEY,
                                        ExpiresIn=3600)

Upload file using presigned URL

signed_url_post = response['url']
data = response['fields']
key = response['key']

file_path = [YOUR_FILE_PATH]
with open(file_path, 'rb') as f:
    files = {'file': (file_path, f)}
    http_response = requests.post(url=signed_url_post, data=data, files=files)

PUT

Generate presigned URL

url = s3.generate_presigned_url('put_object',
                             ExpiresIn=3600,
                             Params={"Bucket": BUCKET,
                             "Key": KEY,
                             "ContentType": "application/octet-stream"})

Upload file using presigned URL

headers = {'content-type': 'application/octet-stream'}
with open(file_path_, 'rb') as f:
    http_response = requests.put(url=url, data=f, headers=headers)

Make sure to add headers = {'content-type': 'application/octet-stream'} if you are using put method

infinity_coding7
  • 434
  • 5
  • 16
1

While using Lambda functions with API Gateway, There are a few things to consider,

  1. While using the pre-signed URL, check if your request method is PUT or POST. It should be PUT.
  2. Check your lambda function in AWS and make sure the ContentType matches with your upload.
  3. Make sure the headers you set with the request match the params in the lambda. function.
Lenzman
  • 1,177
  • 18
  • 30