<#304 Access and modify req.body in request filter...
# pact-js-development
g
#304 Access and modify req.body in request filters Issue created by YOU54F What am I trying to do? Trying to verify pacts against an AWS Lambda function, with an API gateway, that requires AWSv4 signed request headers Problems I need to overcome
Signing Requests
To sign a request, you first calculate a hash (digest) of the request. Then you use the hash value, some other information from the request, and your secret access key to calculate another hash known as the signature. Then you add the signature to the request in one of the following ways:
Using the HTTP Authorization header.
Basically we need to pre-sign some headers based on information on our request • For GET requests, Request query string is needed • For POST requests, Request body is needed We can acheive this with aws4 in a
stateHandler
for
isAuthenticated
, however I cannot get access to the pact under test's
path
or
requestBody
, which I need to pass to the pre-signed URL What I've tried so far Using the following Proposal: Allow customProviderHeaders to be dynamically added to different interactions , I have been able to get the verifier successfully working locally & in CI with a hardcoded request path, rather than the request path from the pact under test. Examples in CI CircleCI AWS Verification Step AWS-Provider Pact AWS-Provider Pact Verification Results Steps taken in code 1. Generate temporary AWS credentials with a bash script and export to bash & run verify script
Copy code
#!/bin/bash
set -o pipefail

  AWS_TEMP_CREDS=`aws sts assume-role --role-arn $ARN_ROLE --role-session-name api-gateway-access| jq -c '.Credentials'`
  export AWS_ACCESS_KEY_ID=`echo $AWS_TEMP_CREDS | jq -r '.AccessKeyId'`
  export AWS_SECRET_ACCESS_KEY=`echo $AWS_TEMP_CREDS | jq -r '.SecretAccessKey'`
  export AWS_SESSION_TOKEN=`echo $AWS_TEMP_CREDS | jq -r '.SessionToken'`

npx ts-node src/pact/verifier/verify.ts | grep -v Created
3. Pact is read, and state 'is authenticated' is met, passes over to the stateHandler. • Request host, path and body need to be ascertained • Request host comes from
PROVIDER_BASE_URL
which is set to
<https://3efkw1ju81.execute-api.us-east-2.amazonaws.com/default>
• For GET requests, Request path needs to come from pact under test, currently hardcoded to
default/helloworld
• For POST requests, Request body needs to come from pact under test 4. stateHandler for
is Authenticated
returns modified headers
Copy code
let signedHost: string;
let signedXAmzSecurityToken: string;
let signedXAmzDate: string;
let signedAuthorization: string;
let authHeaders: any;

const opts: VerifierOptions = {
stateHandlers: {
  "Is authenticated": async () => {
    const requestUrl = process.env.PACT_PROVIDER_URL;
    const host = new url.URL(requestUrl).host;
    const apiroute = new url.URL(requestUrl).pathname;
    const pathname = `${apiroute}/helloworld`;
    const options = {
      host,
      path: pathname,
      headers: {}
    };
    await aws4.sign(options);
    aws4.sign(options, {
      secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
      accessKeyId: process.env.AWS_ACCESS_KEY_ID,
      sessionToken: process.env.AWS_SESSION_TOKEN
    });
    authHeaders = options.headers;
    signedHost = authHeaders.Host;
    signedXAmzSecurityToken = authHeaders["X-Amz-Security-Token"];
    signedXAmzDate = authHeaders["X-Amz-Date"];
    signedAuthorization = authHeaders.Authorization;
    return Promise.resolve(`AWS signed headers created`);
  },
  "Is not authenticated": async () => {
    signedHost = null;
    signedXAmzSecurityToken = null;
    signedXAmzDate = null;
    signedAuthorization = null;
    return Promise.resolve(`Blank aws headers created`);
  }
},
5. requestFilter will set amazon signed headers if they have been set
Copy code
requestFilter: (req, res, next) => {
    // over-riding request headers with AWS credentials
    if (signedHost != null) {
      req.headers.Host = signedHost;
    }
    if (signedXAmzSecurityToken != null) {
      req.headers["X-Amz-Security-Token"] = signedXAmzSecurityToken;
    }
    if (signedXAmzDate != null) {
      req.headers["X-Amz-Date"] = signedXAmzDate;
    }
    if (signedAuthorization != null) {
      req.headers.Authorization = signedAuthorization;
    }
    next();
  },
How can I get access to the path and body of the pact under test, in the stateHandler? I logged out the
req.path
&
req.body
inside
requestFilter
Copy code
req.path /_pactSetup
req.body { consumer: 'consumer-service',
  state: 'Is authenticated',
  states: [ 'Is authenticated' ],
  params: {} }
creating AWS signed headers
created AWS signed headers
req.path /path/that/the/pact/test/is/calling
req.body undefined
It looks like 1. Pact setup is called
req.path /_pactSetup
with the body
Copy code
{ consumer: 'consumer-service',
  state: 'Is authenticated',
  states: [ 'Is authenticated' ],
  params: {} }
2. The
stateHandler
is called
Copy code
creating AWS signed headers
created AWS signed headers
3. The pact under tests, request path is called
Copy code
req.path /helloworld
req.body undefined
4. For a post request, it might look like
Copy code
req.path /helloworld
req.body {message:"hello world")
So my real question is, can the
stateHandlers
access the
req
object? Cheers for any advice and help in advance! pact-foundation/pact-js