Search code examples
amazon-web-servicesaws-sdkamazon-iamaws-sdk-jsaws-sdk-nodejs

Why do I get "The request signature we calculated does not match the signature you provided." for GET but not PUT for OpenSearch?


I am following this guide for signing HTTP requests to an Amazon OpenSearch Service using Node.js (version 3 of the AWS SDK for JavaScript).

When I copy the exact sample code and export my AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY of my authorised user, the PUT index/type/id request to add an item is successful:

201 Created
Response body: {"_index":"products","_type":"_doc","_id":"2","_version":1,"result":"created","_shards":{"total":2,"successful":2,"failed":0},"_seq_no":0,"_primary_term":1}

However, when I change the request to instead GET /_search endpoint, I get:

403 Forbidden
Response body: {"message":"The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details."}

The user is fully authorised against the index:

    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::**:user/aws-elasticbeanstalk-ec2-user"
      },
      "Action": "es:*",
      "Resource": "arn:aws:es:ap-southeast-2:**:domain/mydomain/*"
    },

How do I rectify my signature?

Here is my modified code from the above link:

const { HttpRequest } = require('@aws-sdk/protocol-http')
const { defaultProvider } = require('@aws-sdk/credential-provider-node')
const { SignatureV4 } = require('@aws-sdk/signature-v4')
const { NodeHttpHandler } = require('@aws-sdk/node-http-handler')
const { Sha256 } = require('@aws-crypto/sha256-browser')

const region = ''
const domain = ''
const index = 'products'
const type = '_search'
const createBody = (query) => ({
  query: {
    multi_match: {
      query,
      type: 'phrase',
      fields: [
        'tags',
        'name',
        'category',
        'maker'
      ]
    }
  },
  highlight: {
    pre_tags: [''],
    post_tags: [''],
    fields: {
      tags: {},
      name: {},
      category: {},
      maker: {}
    }
  }
})

searchIndex('sh').then(() => process.exit())

async function searchIndex (query) {
  const request = new HttpRequest({
    body: JSON.stringify(createBody(query)),
    headers: {
      'Content-Type': 'application/json',
      host: domain
    },
    hostname: domain,
    method: 'GET',
    path: index + '/' + type
  })

  const signer = new SignatureV4({
    credentials: defaultProvider(),
    region: region,
    service: 'es',
    sha256: Sha256
  })

  const signedRequest = await signer.sign(request)

  const client = new NodeHttpHandler()
  const { response } = await client.handle(signedRequest)
  console.log(response.statusCode + ' ' + response.body.statusMessage)

  let responseBody = ''
  return new Promise((resolve) => {
    response.body.on('data', (chunk) => {
      responseBody += chunk
    })
    response.body.on('end', () => {
      console.log('Response body: ' + responseBody)
      resolve(responseBody)
    })
  }, (error) => {
    console.log('Error: ' + error)
  })
}

Solution

  • i also had this issue, using the same tutorial

    reading the docs on request body searches, i found it states the following:

    Note The _search API accepts HTTP GET and POST for request body searches, but not all HTTP clients support adding a request body to a GET request. POST is the more universal choice.

    changing my method to POST solved the issue for me