s3 SignedUrl x-amz-security-token

I just solved a very similar, probably the same issue as you have. I say probably because you dont say what deployment entails for you. I am assuming you are deploying to Lambda but you may not be, this may or may not apply but if you are using temporary credentials this will apply.

I initially used the method you use above but then was using the npm module aws-signature-v4 to see if it was different and was getting the same error you are.

You will need the token, it is needed when you have signed a request with temporary credentials. In Lambda's case the credentials are in the runtime, including the session token, which you need to pass, the same is most likely true elsewhere as well but I'm not sure I haven't used ec2 in a few years.

Buried in the docs (and sorry I cannot find the place this is stated) it is pointed out that some services require that the session_token be processed with the other canonical query params. The module I'm using was tacking it on at the end, as the sig v4 instructions seem to imply, so I modified it so the token is canonical and it works.

We've updated the live version of the aws-signature-v4 module to reflect this change and now it works nicely for signing your s3 requests.

Signing is discussed here.

I would use the module I did as I have a feeling the sdk is doing the wrong thing for some reason.

usage example (this is wrapped in a multiPart upload thus the part number and upload Id):

function createBaseUrl( bucketName, uploadId, partNumber, objectKey ) {
  let url = sig4.createPresignedS3URL( objectKey, {
    method: "PUT",
    bucket: bucketName,
    expires: 21600,
    query: `partNumber=${partNumber}&uploadId=${uploadId}`
  });
  return url;
}

I was having the same issue. Everything was working flawlessly using serverless-offline but when I deployed to Lambda I started receiving AccessDenied issues on the URL. When comparing the URLs returned between the serverless-offline and AWS deployments I noticed the only difference was the inclusion of the X-Amz-Security-Token in the URL as a query string parameter. After some digging I discovered the token being assigned was based upon the assumed role the lambda function had. All I had to do was grant the appropriate S3 policies to the role and it worked.