Create an AWS-sig4 using amplify credentials

Marcos Portillo
3 min readAug 8, 2020

--

First of all, I’ll like to explain the reasons that drove me at the point of having to manually get authentications headers in order to hit lambda functions sitting behind an API gateway with IAM role authentication even though I'm already using amplify (and the API module does that for free).

Cookies..

Yap, that is the reason. So far amplify doesn’t accept set-cookie headers in the response, I have tried to send CloudFront signed cookies from my lambda function in order to authenticate future client request to CloudFront and that doesn’t work:

That was my case, but the next part may work for any cases where you need to hit your API gateway using fetch/axios.

Stop there copy paster! if the authorizer of your API gateway (or the service against you want to authenticate) is not AWS_AIM the case is much simpler, you need to pass the id_token in the Authorization header, instead of a sig4 signature. Something like this is all that you need (if you are using eg. a Cognito User Pool Authorizer):

async function compute(url, method = "GET") {try {
const user = await Auth.currentAuthenticatedUser();
const token = user.signInUserSession.idToken.jwtToken;
const headers = {'Authorization': token};
const response = await fetch(url, {
method,
mode: "cors",
cache: "no-cache",
headers,
referrer: "client",
});
if (response.ok) {
return response.json();
} else {
throw new Error("Failed Request");
}
} catch (e) {
//handle your error
}
}

Ok then, there is a step by step guide in the AWS documentation, but if you are using amplify the calculation is much easier:

import { Auth, Signer } from 'aws-amplify';// sign a request using Amplify Auth and Signer
async function signRequest(url, method, service, region) {
const essentialCredentials = Auth.essentialCredentials(await Auth.currentCredentials());

const params = { method, url};
const credentials = {
secret_key: essentialCredentials.secretAccessKey,
access_key: essentialCredentials.accessKeyId,
session_token: essentialCredentials.sessionToken,
};
// set your region and service here
const serviceInfo = {region, service};
// Signer.sign takes care of all other steps of Signature V4
return Signer.sign(params, credentials, serviceInfo);
}

The above code will provide us with a signed URL and the corresponding headers for our request:

Now you just need to use the signed request in your favorite HTTP client:

async function compute(url, method = "GET") {
try {
const signedRequest = await signRequest(url, method, "execute-api");
const response = await fetch(signedRequest.url, {
method,
mode: "cors",
cache: "no-cache",
headers: signedRequest.headers,
referrer: "client",
});
if (response.ok) {
return response.json();
} else {
throw new Error("Failed Request");
}
} catch (e) {
// handle the error
}
}

That is pretty much it…

Conclusion

The explained above allows you to get authenticated requests against an AWS service if for some reason you need to hit eg. an API-GW and not use amplify, for my specific scenario something like sending the cookies in the payload and setting them in the client will be enough, but this approach (getting cookies set by the response) is better (I guess).

Hope this can help somebody else!

--

--