Connect to API Gateway with IAM Auth
Now that we have our basic create note form working, let’s connect it to our API. We’ll do the upload to S3 a little bit later. Our APIs are secured using AWS IAM and Cognito User Pool is our authentication provider. As we had done while testing our APIs, we need to follow these steps.
- Authenticate against our User Pool and acquire a user token.
- With the user token get temporary IAM credentials from our Identity Pool.
- Use the IAM credentials to sign our API request with Signature Version 4.
In our React app we do step 1 by calling the authUser
method when the App component loads. So let’s do step 2 and use the userToken
to generate temporary IAM credentials.
Generate Temporary IAM Credentials
Our authenticated users can get a set of temporary IAM credentials to access the AWS resources that we’ve previously specified. We can do this using the AWS JS SDK.
Install it by running the following in your project root.
$ npm install aws-sdk --save
Let’s add a helper function in src/libs/awsLib.js
.
function getAwsCredentials(userToken) {
const authenticator = `cognito-idp.${config.cognito
.REGION}.amazonaws.com/${config.cognito.USER_POOL_ID}`;
AWS.config.update({ region: config.cognito.REGION });
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: config.cognito.IDENTITY_POOL_ID,
Logins: {
[authenticator]: userToken
}
});
return AWS.config.credentials.getPromise();
}
This method takes the userToken
and uses our Cognito User Pool as the authenticator to request a set of temporary credentials.
Also include the AWS SDK in our header.
import AWS from "aws-sdk";
To get our AWS credentials we need to add the following to our src/config.js
in the cognito
block. Make sure to replace YOUR_IDENTITY_POOL_ID
with your Identity pool ID from the Create a Cognito identity pool chapter and YOUR_COGNITO_REGION
with the region your Cognito User Pool is in.
REGION: "YOUR_COGNITO_REGION",
IDENTITY_POOL_ID: "YOUR_IDENTITY_POOL_ID",
Now let’s use the getAwsCredentials
helper function.
Replace the authUser
in src/libs/awsLib.js
with the following:
export async function authUser() {
if (
AWS.config.credentials &&
Date.now() < AWS.config.credentials.expireTime - 60000
) {
return true;
}
const currentUser = getCurrentUser();
if (currentUser === null) {
return false;
}
const userToken = await getUserToken(currentUser);
await getAwsCredentials(userToken);
return true;
}
We are passing getAwsCredentials
the userToken
that Cognito gives us to generate the temporary credentials. These credentials are valid till the AWS.config.credentials.expireTime
. So we simply check to ensure our credentials are still valid before requesting a new set. This also ensures that we don’t generate the userToken
every time the authUser
method is called.
Next let’s sign our request using Signature Version 4.
Sign API Gateway Requests with Signature Version 4
All secure AWS API requests need to be signed using Signature Version 4. We could use API Gateway to generate an SDK and use that to make our requests. But that can be a bit annoying to use during development since we would need to regenerate it every time we made a change to our API. So we re-worked the generated SDK to make a little helper function that can sign the requests for us.
To create this signature we are going to need the Crypto NPM package.
Install it by running the following in your project root.
$ npm install crypto-js --save
Copy the following file to src/libs/sigV4Client.js
.
This file can look a bit intimidating at first but it is just using the temporary credentials and the request parameters to create the necessary signed headers. To create a new sigV4Client
we need to pass in the following:
// Pseudocode
sigV4Client.newClient({
// Your AWS temporary access key
accessKey,
// Your AWS temporary secret key
secretKey,
// Your AWS temporary session token
sessionToken,
// API Gateway region
region,
// API Gateway URL
endpoint
});
And to sign a request you need to use the signRequest
method and pass in:
// Pseudocode
const signedRequest = client.signRequest({
// The HTTP method
method,
// The request path
path,
// The request headers
headers,
// The request query parameters
queryParams,
// The request body
body
});
And signedRequest.headers
should give you the signed headers that you need to make the request.
Now let’s go ahead and use the sigV4Client
and invoke API Gateway.
Call API Gateway
We are going to call the code from above to make our request. Let’s write a helper function to do that.
Add the following to src/libs/awsLib.js
.
export async function invokeApig({
path,
method = "GET",
headers = {},
queryParams = {},
body
}) {
if (!await authUser()) {
throw new Error("User is not logged in");
}
const signedRequest = sigV4Client
.newClient({
accessKey: AWS.config.credentials.accessKeyId,
secretKey: AWS.config.credentials.secretAccessKey,
sessionToken: AWS.config.credentials.sessionToken,
region: config.apiGateway.REGION,
endpoint: config.apiGateway.URL
})
.signRequest({
method,
path,
headers,
queryParams,
body
});
body = body ? JSON.stringify(body) : body;
headers = signedRequest.headers;
const results = await fetch(signedRequest.url, {
method,
headers,
body
});
if (results.status !== 200) {
throw new Error(await results.text());
}
return results.json();
}
We are simply following the steps to make a signed request to API Gateway here. We first ensure the user is authenticated and we generate their temporary credentials using authUser
. Then using the sigV4Client
we sign our request. We then use the signed headers to make a HTTP fetch
request.
Include the sigV4Client
by adding this to the header of our file.
import sigV4Client from "./sigV4Client";
Also, add the details of our API to src/config.js
above the cognito: {
line. Remember to replace YOUR_API_GATEWAY_URL
and YOUR_API_GATEWAY_REGION
with the ones from the Deploy the APIs chapter.
apiGateway: {
URL: "YOUR_API_GATEWAY_URL",
REGION: "YOUR_API_GATEWAY_REGION"
},
In our case the URL is https://ly55wbovq4.execute-api.us-east-1.amazonaws.com/prod
and the region is us-east-1
.
We are now ready to use this to make a request to our create note API.
If you liked this post, please subscribe to our newsletter, give us a star on GitHub, and check out our sponsors.
For help and discussion
Comments on this chapterFor reference, here is the code so far
Frontend Source :connect-to-api-gateway-with-iam-auth