GitHub
02/22/2023, 6:31 PMSigning 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
#!/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
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
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
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
{ consumer: 'consumer-service',
state: 'Is authenticated',
states: [ 'Is authenticated' ],
params: {} }
2. The stateHandler is called
creating AWS signed headers
created AWS signed headers
3. The pact under tests, request path is called
req.path /helloworld
req.body undefined
4. For a post request, it might look like
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-jsGitHub
02/22/2023, 6:31 PM