Part 2: Securing AWS API Gateway using AWS Cognito OAuth2 scopes and OpenID Connect

In the previous blog, we saw how to secure APIs using OAuth2 client credentials grant. This OAuth grant is used mainly for machine to machine or app to app API authorization. If the authorization needs to be performed at an user level, we have to use OpenID Connect which adds an identity layer on top of OAuth layer. At a very high level, difference OAuth2 and OIDC w.r.t tokens is that OAuth 2 generates only access token + optional refresh token where as OIDC generates an id token + access token + optional refresh token. ID token contains user attributes which can be used to perform additional authorization checks or trigger multi-factor authentication while invoking an API.

  1. If user credentials are valid, AWS Cognito will return a JWT (JSON Web Token) formatted id_token + access_token + refresh_token
  2. Pass this token in Authorization header for all API calls
  3. API Gateway makes a call to AWS Cognito to validate the access_token.
  4. AWS Cognito returns token validation response.
  5. If token is valid, API Gateway will validate the OAuth2 scope in the JWT token and ALLOW or DENY API call. This is entirely handled by API Gateway once configuration is in place
  6. Return mock response to API Gateway.
  7. If there are no issues with the Lambda function, API Gateway will return a HTTP 200 with response data to the client application.
  1. Knowledge on AWS API Gateway , AWS Cognito services
  2. Knowledge on OIDC and OAuth2 protocol
  1. Create Cognito user to test the Authorization code grant flow
  2. Deploy a sample API Gateway application with 3 HTTP methods — GET, POST, DELETE and static response
  3. Configure Cognito Authorizer in API Gateway

Step 1: Create AWS Cognito user pool and setup a OAuth application with OpenID scopes

  • Login to AWS Management console and navigate to Cognito service
  • Select “Manage your user pools” and click “Create a user pool”
  • Enter a pool name and select “Review defaults”. Then select “Create pool”.
  • Enter a “App client name” and select “Generate client secret” checkbox. Then “Create app client”. Note down the “App client id” and “App client secret” values displayed in next page.
  • Enter “Name” and “Identifier”. It can be any value. Also, add 3 scopes and save changes.
  1. create_product
  2. delete_product

Step 2 : Create Cognito user to test the Authorization code grant flow

  • Go to “General Settings > Users and groups” and select “Create user”.
  • Enter some test user credentials. You can uncheck all the checkboxes since this is only for testing purpose.
Base domain URL (from previous step) + /oauth2/authorize/oauth2/authorize?response_type=code&client_id=<client ID from App client configuration>&redirect_uri=https://localhost/callback&identity_provider=COGNITO&scope=openid+profile+product-api/read_product+product-api/delete_product+product-api/create_productBased on this, it will be :https://api-product-test1.auth.us-east-1.amazoncognito.com/oauth2/authorize?response_type=code&client_id=6b09anoqh56v85ruv3hk9c317c&redirect_uri=https://localhost/callback&identity_provider=COGNITO&scope=openid+profile+product-api/read_product+product-api/delete_product+product-api/create_product
  • To get the id_token, access_token and refresh_token, invoke the Cognito token endpoint :
curl -X POST --user 'client_id:client_secret' -H 'Content-Type: application/x-www-form-urlencoded' "https://api-product-test1.auth.us-east-1.amazoncognito.com/oauth2/token?grant_type=authorization_code&client_id=<client_id>&scope=openid+profile&redirect_uri=https://localhost/callback&code=<authorization_code>"It will return the following response :{"id_token":"<JwT ID Token>","access_token":"<JwT access token>","refresh_token":"<JwT refresh token>"}
  • You can also check the id_token JwT which will show all the user attributes

Part 3 : Deploy a sample API Gateway application with 3 HTTP methods — GET, POST, DELETE and static response

  • Go to API Gateway service and create a new REST API
  • Expand the 200 response status and select “Mapping Templates > application / json”.
  • In the Generate template text box, enter a static response like below :
curl -X GET  https://<unique ID>.execute-api.us-east-1.amazonaws.com/dev
{
"statusCode": 200,
"message": "Get product details"
}
────────────────────────────────────────────────────────────────────
curl -X POST https://<unique ID>.execute-api.us-east-1.amazonaws.com/dev
{
"statusCode": 200,
"message": "Create a new product"
}
────────────────────────────────────────────────────────────────────
curl -X DELETE https://<unique ID>.execute-api.us-east-1.amazonaws.com/dev
{
"statusCode": 200,
"message": "Delete a product"
}

Step 4: Configure Cognito Authorizer for API Gateway

  • Go to “Amazon API Gateway > API_Cognito > Authorizers” and “Create new Authorizer”. Enter a Name and select user pool which was created in Step 1. Also, enter “Token Source” as “Authorization” header.
  • Select “Cognito_Authorizer” in “Authorization” drop-down. That should automatically add a new field “OAuth Scopes”. Enter “product-api/read_product” scope and save using the tick mark.

Step 5: Testing

Now, let us test this API using the access_token obtained from Cognito

  • Try testing the 3 curl requests from Step 3 and it should return unauthorized response:
curl -X GET  https://<unique ID>.execute-api.us-east-1.amazonaws.com/dev
{"message":"Unauthorized"}
────────────────────────────────────────────────────────────────────
curl -X POST https://<unique ID>.execute-api.us-east-1.amazonaws.com/dev
{"message":"Unauthorized"}
────────────────────────────────────────────────────────────────────
curl -X DELETE https://<unique ID>.execute-api.us-east-1.amazonaws.com/dev
{"message":"Unauthorized"}
  • Now, try passing the access_token in Authorization header and it should return successful response:
curl -X GET  -H 'Authorization: <access_token>' https://<unique ID>.execute-api.us-east-1.amazonaws.com/dev
{
"statusCode": 200,
"message": "Get product details"
}
────────────────────────────────────────────────────────────────────
curl -X POST -H 'Authorization: <access_token>' https://<unique ID>.execute-api.us-east-1.amazonaws.com/dev
{
"statusCode": 200,
"message": "Create a new product"
}
────────────────────────────────────────────────────────────────────
curl -X DELETE -H 'Authorization: <access_token>' https://<unique ID>.execute-api.us-east-1.amazonaws.com/dev
{
"statusCode": 200,
"message": "Delete a product"
}

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store