sam0x17
By:
sam0x17

"Signature Does Not Match" when putting to presigned spaces upload url

October 24, 2017 1.6k views
DigitalOcean JavaScript Node.js

I am using the aws-sdk in Node.js to generate a presigned putObject upload url for use with DigitalOcean Spaces. The url gets generated just fine, however when I try putting to it using a REST client, I get a SignatureDoesNotMatch error. Below is the code I am using to generate the presigned url (from within a Google Cloud Function):

var AWS = require('aws-sdk');
AWS.config.loadFromPath('./credentials.json');
var s3 = new AWS.S3({params: {Bucket: 'bucket-name'}});

exports.requestUploadUrl = function requestUploadUrl(req, res) {
  var key = 'some-predefined-key';
  s3.getSignedUrl('putObject', {Bucket: 'bucket-name', Key: key}, function (err, url) {
    res.send(url);
  });
}

I am getting this sort of response when I try posting to the presigned url:

<?xml version="1.0" encoding="UTF-8" ?>
<Error>
<Code>SignatureDoesNotMatch</Code>
<RequestId>tx000000000000000664ed3-0059eeef91-3777b-nyc3a</RequestId>
<HostId>3777b-nyc3a-nyc</HostId>
 </Error>
2 comments
5 Answers
sam0x17 October 28, 2017
Accepted Answer

So the reason this wasn't working is the content type MUST be specified when generating the presigned URL and specified once again by the client when it connects, and they must exactly match. Once I specified application/octet-stream everything worked

  • @sam0x17 were you using v4 signature or v2? I can't get it to work with just setting the content-type

    • I believe I did have to use v2

      • @sam0x17 Do you remember which package you used for the signature? If it was aws-sdk, do you remember the version number?

        • This is what I'm using @vallelungabrian

          const AWS = require('aws-sdk');
          AWS.config.loadFromPath('./credentials.json');
          const s3 = new AWS.S3({signatureVersion: 'v2'});
          var params = {
            Key: cloudName,
            Bucket: '[bucket name]',
            Expires: 1000000,
            ContentType: 'application/octet-stream',
            ACL: 'public-read'
          }
          s3.getSignedUrl('putObject', params, function (err, url) {
            if(err) {
              res.send('error');
              throw err;
            }
            console.log('url:', url);
          });
          
          • This is what I am using @sam0x17

            var AWS = require("aws-sdk")
            var s3 = new AWS.S3({
              signatureVersion: 'v2',
              accessKeyId: "XXXX",
              secretAccessKey: "XXX",
              endpoint: new AWS.Endpoint("nyc3.digitaloceanspaces.com")
            });
            var params = {
              Key: "scan0001.pdf",
              Bucket: 'doppler-staging',
              Expires: 1000000,
              ContentType: 'application/octet-stream',
              ACL: 'public-read'
            }
            s3.getSignedUrl('putObject', params, function (err, url) {
              if(err) {
                res.send('error');
                throw err;
              }
              console.log('url:', url);
            });
            
          • @sam0x17 this is the output I get

            <Error>
              <Code>SignatureDoesNotMatch</Code>
              <RequestId>tx000000000000000037e94-005a2d8a0b-e3e27-nyc3a</RequestId>
              <HostId>e3e27-nyc3a-nyc</HostId>
            </Error>
            
          • @vallelungabrian I get that if I don't specify ContentType when you PUT to the generated URL.

I am also facing the same issue. I need a clarification here, what is the secrete key here ? I am taking keys generated in Personal access tokens as secret key and Spaces access keys as access key id.
AWS.config.update({
"accessKeyId": Spaces access keys,
"secretAccessKey": Personal access tokens
});

Can you please clarify if I am considering accessKeyId and secretAccessKey properly ?

@anoopg your problem is a bit more simple. The accessKeyId is the name you gave your spaces access key, and the secretAccessKey is simply the value of the key. You don't need to use a personal access token at all for this.

Have another answer? Share your knowledge.