Benjamin Xiang
Benjamin Xiang

Reputation: 1

SignatureDoesNotMatch: rejecting SignedURL

I am pretty new to using AWS S3, and am trying to upload an image to S3, but am running into the infamous SignatureDoesNotMatch error. I made sure of any typos: (ie. leading, trailing spaces in my keys), checked if my system time was off, but all to no avail. My workflow goes as follows (which is mostly taken from a tutorial if you want to reference: https://medium.com/@diego.f.rodriguezh/direct-image-upload-to-aws-s3-with-react-and-express-2f063bc15430). I tried following all of the previous posts on this issue but I still can't solve it.

First, in a queries.js I handle the signing of the URL:

    accessKeyId: process.env.S3_KEY, // stored in the .env file
    secretAccessKey: process.env.S3_SECRET, // stored in the .env file
    region: process.env.BUCKET_REGION // This refers to your bucket configuration.
  });
// Creating a S3 instance
const s3 = new AWS.S3();

// Retrieving the bucket name from env variable
const Bucket = process.env.BUCKET_NAME; 
// PUT URL Generator
function generatePutUrl(Key, ContentType) {
  
  return new Promise((resolve, reject) => {
    // Note Bucket is retrieved from the env variable above.
    console.log('the key is:', Key)
    const params = { Bucket, ContentType, Expires:3600, Key };
        // Note operation in this case is putObject
    s3.getSignedUrl('putObject', params, function(err, url) {
      if (err) {
        console.log(err)
        reject(err);
      }
      // If there is no errors we can send back the pre-signed PUT URL
      resolve(url);
    });
  });
}

Then in index.js:


// PUT URL
app.get('/generate-put-url', (req,res)=>{
  // Both Key and ContentType are defined in the client side.
  // Key refers to the remote name of the file.
  // ContentType refers to the MIME content type, in this case image/jpeg
  const { Key, ContentType } =  req.query;
  
  generatePutUrl(Key, ContentType).then(putURL => {
    console.log({putURL})
    res.send({putURL});
  })
  .catch(err => {
    res.send(err);
  });
});

app.listen(port, () => {
  console.log(`Listening on port ${port}`);
});

And then my component uploader.js that really renders the upload button where I make the get request for the signed url, which I then return to make the put request to S3:

getImage = e => {
    const files = e.target.files;
    if (files && files.length > 0) {
      const file = files[0];
      this.setState({ file });
    }
  };

  uploadFile = e => {
    
    e.preventDefault();
    const { file } = this.state;
    this.setState({message:'Uploading...'})
    const contentType = file.type; // eg. image/jpeg or image/svg+xml
    console.log(contentType)
    const generatePutUrl = 'http://localhost:4000/generate-put-url';
    const options = {
      params: {
        Key: file.name,
        ContentType: contentType,
      },
      headers: {
        'Content-Type': contentType
      }
    };
    
    axios.get(generatePutUrl, options).then(res => {
      const {
        data: { putURL }
      } = res;
      
      axios
        .put(putURL, file, options)
        .then(res => {
            
          this.setState({message:'Upload Successful'})
          setTimeout(()=>{
            this.setState({message:''});
            document.querySelector('#upload-image').value='';
          }, 2000)
        })
        .catch(err => {
          this.setState({message:'Sorry, something went wrong'})
          console.log('err', err);
        });
    });
  };

For reference, I logged my signed URL before it entered the put request, and it looked like this (with access key x-ed out)

putURL: 'https://polici-media.s3.us-east-2.amazonaws.com/32bitadder.JPG?Content-Type=image%2Fjpeg&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIXXXXXXXXXXX%2F20200627%2Fus-east-2%2Fs3%2Faws4_request&X-Amz-Date=20200627T050401Z&X-Amz-Expires=3600&X-Amz-Signature=d12f6228e20d3bc5fab2b38bd6e250992f3e362418eb8f61ed6c886a71eb46e7&X-Amz-SignedHeaders=host'

For those that might be following along in the tutorial, all I really changed was from using port 3500 to port 4000 for my backend, but that isn't an issue in this case. I also put this URL in postman to no avail, though the URL https://polici-media.s3.us-east-2.amazonaws.com/32bitadder.JPG with the proper headers and authentication works just fine on postman

Upvotes: 0

Views: 1312

Answers (1)

vikas95prasad
vikas95prasad

Reputation: 1332

I solved this by removing the Content-Type from the headers.

Upvotes: 0

Related Questions