Now that our APIs have been secured with Cognito User Pool and Identity Pool, we are ready to use the authenticated user’s info in our Lambda functions.

Recall that we’ve been hard coding our user ids so far (with user id 123). We’ll need to grab the real user id from the Lambda function event.

Cognito Identity Id

Recall the function signature of a Lambda function:

export async function main(event: APIGatewayProxyEvent, context: Context) {}

Or the refactored version that we are using:

export const main = handler(async (event) => {});

So far we’ve used the event object to get the path parameters (event.pathParameters) and request body (event.body).

Now we’ll get the id of the authenticated user.

event.requestContext.authorizer?.iam.cognitoIdentity.identityId;

This is an id that’s assigned to our user by our Cognito Identity Pool.

You’ll also recall that so far all of our APIs are hard coded to interact with a single user.

userId: "123", // The id of the author

Let’s change that.

Change indicator Replace the above line in packages/functions/src/create.ts with.

userId: event.requestContext.authorizer?.iam.cognitoIdentity.identityId,

Change indicator Do the same in these files:

  • packages/functions/src/get.ts
  • packages/functions/src/update.ts
  • packages/functions/src/delete.ts

Change indicator In packages/functions/src/list.ts find this line instead.

":userId": "123",

Change indicator And replace it with.

":userId": event.requestContext.authorizer?.iam.cognitoIdentity.identityId,

Keep in mind that the userId above is the Federated Identity id (or Identity Pool user id). This is not the user id that is assigned in our User Pool. If you want to use the user’s User Pool user Id instead, have a look at the Mapping Cognito Identity Id and User Pool Id chapter.

To test these changes we cannot use the curl command anymore. We’ll need to generate a set of authentication headers to make our requests. Let’s do that next.

Test the APIs

Let’s quickly test our APIs with authentication.

To be able to hit our API endpoints securely, we need to follow these steps.

  1. Authenticate against our User Pool and acquire a user token.
  2. With the user token get temporary IAM credentials from our Identity Pool.
  3. Use the IAM credentials to sign our API request with Signature Version 4.

These steps can be a bit tricky to do by hand. So we created a simple tool called AWS API Gateway Test CLI.

You can also run it using pnpm dlx as seen in the following example. (If you have installed the tool you can use apig-test in place of pnpm dlx ).

$ pnpm dlx aws-api-gateway-cli-test \
--user-pool-id='<USER_POOL_ID>' \
--app-client-id='<USER_POOL_CLIENT_ID>' \
--cognito-region='<COGNITO_REGION>' \
--identity-pool-id='<IDENTITY_POOL_ID>' \
--invoke-url='<API_ENDPOINT>' \
--api-gateway-region='<API_REGION>' \
--username='admin@example.com' \
--password='Passw0rd!' \
--path-template='/notes' \
--method='POST' \
--body='{"content":"hello world","attachment":"hello.jpg"}'

We need to pass in quite a bit of our info to complete the above steps.

  • Use the username and password of the user created above.
  • Replace USER_POOL_ID, USER_POOL_CLIENT_ID, COGNITO_REGION, and IDENTITY_POOL_ID with the UserPoolId, UserPoolClientId, Region, and IdentityPoolId from our previous chapter.
  • Replace the API_ENDPOINT with the ApiEndpoint from our API stack outputs.
  • And for the API_REGION you can use the same Region as we used above. Since our entire app is deployed to the same region.

While this might look intimidating, just keep in mind that behind the scenes all we are doing is generating some security headers before making a basic HTTP request. We won’t need to do this when we connect from our React.js app.

If the command is successful, the response will look similar to this.

Authenticating with User Pool
Getting temporary credentials
Making API request
{
  status: 200,
  statusText: 'OK',
  data: {
    userId: 'us-east-1:06d418dd-b55b-4f7d-9af4-5d067a69106e',
    noteId: 'b5199840-c0e5-11ec-a5e8-61c040911d73',
    content: 'hello world',
    attachment: 'hello.jpg',
    createdAt: 1650485336004
  }
}

Commit the Changes

Change indicator Let’s commit and push our changes to GitHub.

$ git add .
$ git commit -m "Securing the API"
$ git push

We’ve now got a serverless API that’s secure and handles user authentication. In the next section we are going to look at how we can work with 3rd party APIs in serverless. And how to handle secrets!