node.jsamazon-web-servicesamazon-s3multermulter-s3

Getting an 'Access Denied' error when using Multer-S3 to upload file to AWS-S3 Bucket


I have been trying to upload a file from my express backend application using Multer-S3 but I am getting an 'Access Denied' error. Printing out the error gives me this:


    {
        "success": false,
        "errors": {
            "title": "File Upload Error",
            "detail": "Access Denied",
            "error": {
                "message": "Access Denied",
                "code": "AccessDenied",
                "region": null,
                "time": "2021-06-26T16:40:47.074Z",
                "requestId": "7W7EMNWNFWTPNHHG",
                "extendedRequestId": "9tC2dSn8Zu6dplJxxUVIx3Zdr4mCk7ZVg0RcayXHHO86hTIZdO/9YZKsUKwn1ir0AeUg50Y/c94=",
                "statusCode": 403,
                "retryable": false,
                "retryDelay": 76.37236671132325,
                "storageErrors": []
            }
        }
    }

 

My AWS set up:


    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Sid": "VisualEditor0",
                "Effect": "Allow",
                "Action": [
                    "s3:PutObject",
                    "s3:GetObject",
                    "s3:DeleteObject"
                ],
                "Resource": "arn:aws:s3:::test-bucket/*"
            }
        ]
    }

where 'test-bucket' is the name of my bucket.

The user is also set with programmatic access and I have checked that the access keys are updated and active.

From what I understand, this policy will allow my IAM user to put, get and delete objects in my s3 bucket, even though it is not accessible by the public. I also should not need to carry out any further configurations like setting up CORS.

 

My code:

route.js


    router.post('/upload', uploadController.uploadToS3);

uploadController.js


    const upload = require("../util/s3");
    const singleUpload = upload.single("myfile");
    
    exports.uploadToS3 = (req, res, next) => {
        singleUpload(req, res, function (err) {
            if (err) {
                return res.json({
                    success: false,
                    errors: {
                        title: "File Upload Error",
                        detail: err.message,
                        error: err
                    }
                })
            }
    
        })
    };

s3.js


    const S3 = require('aws-sdk/clients/s3');
    const multer = require("multer");
    const multerS3 = require("multer-s3");
    
    bucket_name = process.env.AWS_BUCKET_NAME;
    bucket_region = process.env.AWS_BUCKET_REGION;
    aws_access_key = process.env.AWS_ACCESS_KEY;
    aws_secret_key = process.env.AWS_SECRET_KEY;
    
    const s3 = new S3({
        bucket_region,
        aws_access_key,
        aws_secret_key
    })
    
    const upload = multer({
        storage: multerS3({
            s3: s3,
            bucket: bucket_name,
            acl: 'private',
            metadata: function (req, file, cb) {
                cb(null, { fieldName: file.fieldname });
            },
            key: function (req, file, cb) {
                cb(null, file.originalname);
            }
        })
    });

 

I have tried some solutions that were suggested online, such as adding a policy for the bucket itself, or setting up CORS configuration.

I've even tried to change my IAM user's policy to S3FullAccess. Even then, I still get the access denied error.

I followed this Youtube video's tutorial for the AWS setup: https://www.youtube.com/watch?v=NZElg91l_ms&t=900s&ab_channel=SamMeech-Ward It doesn't seem as if anyone is facing the same issue...

Any help would be much appreciated, thanks!


Solution

  • I've managed to solve my issue. Turns out it was quite a silly mistake, nothing to do with my IAM policies at all. The issue lies in my s3.js code, when I create the s3 object:

    
        const s3 = new S3({
                bucket_region,
                aws_access_key,
                aws_secret_key
            })
    
    

    From the AWS S3 SDK documentation (https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html), the correct constructor parameters should be:

    
        const s3 = new S3({
            region,
            accessKeyId,
            secretAccessKey
        })
    
    

    With that, I was able to upload to my bucket using the AWS setup that I described.

    The reason why I noticed this was because I realized that the 'Last Used' column in the 'Access Key' section for my IAM user was constantly displaying "N/A". This made me realize that my access key was not even being called in the first place.