How to use Cognito User Pool with custom OAuth scopes as authorizer for REST API

In this blog post, we will look at how to configure OAuth authentication for a REST API using AWS Cognito user Pool. We will create a REST API using AWS Lambda and API Gateway, integrate it with Cognito User Pool and create custom OAuth scopes to authenticate and authorize the REST API endpoints.

Overview

At a high level, below are the steps to be performed to setup the REST API and OAuth authentication using Cognito

  1. Create a AWS Lambda function
  2. Create a REST API in AWS API Gateway
  3. Create a Cognito User Pool
  4. Create a Resource Server and define custom scopes
  5. Update the App Client with the custom scopes
  6. Create an Authorizer in API Gateway
  7. Configure the Authorizer and the custom OAuth scope for the API endpoint
  8. Create a user in the Cognito User Pool
  9. Test the API endpoint

1. Create a AWS Lambda function

Let’s create a simple lambda function in Python that just returns a “Hello World” message. This lambda will be configured as the backend for the REST API that we will create next. 

Create a new lambda function by following the below steps

  • Navigate to Lambda service in AWS console and click “Create function” button
  • Enter “hello-world-lambda” as the “Function Name
  • Select the Runtime as “Python 3.9
  • Leave rest of the settings to their default value and click “Create function” button

Leave the default lambda function code that is present once the function is created or type in the below code and click the “Deploy” button.

import json

def lambda_handler(event, context):
    
    return {
        'statusCode': 200,
        'body': json.dumps('Hello World!')
    }

2. Create a REST API in AWS API Gateway

Create a new REST API with a single GET endpoint in AWS API Gateway. We will add authentication and authorization to this endpoint in a later step once we have the Cognito setup completed. 

Create a REST API by following the below steps

  • Navigate to API Gateway service in AWS console and click the “Create API” button
  • Click the “Build” button in the REST API section
  • In the next screen, in the “Settings” section, enter “hello-world-api” in the “API name” field and select “Edge optimized” for the “Endpoint Type
  • Create a Method by selecting “Create Method” from the “Actions” dropdown and then select “GET” as the method type and click the little check mark beside the dropdown
  • In the GET Setup page, select the checkbox “Use Lambda Proxy Integration” and then enter “hello-world-lambda” in the “Lambda Function” textbox to choose the Lambda that we created in the previous step. 
  • Click “OK” in “Add Permission to Lambda Function” dialog box to provide API Gateway permissions to invoke the lambda functio

3. Create a Cognito User Pool

Create a new Cognito User Pool that will contain the users that can access the REST API using custom OAuth scopes. In this exercise, we will utilize most of the default settings while creating and configuring the Cognito user pool.

Create and configure the Cognito User Pool by following the below steps

  • Navigate to AWS Cognito service -> “User pools” in AWS console and click the “Create user pool” button to create a new user pool
  • In the “Configure sign-in experience” step, choose “User name” in the “Cognito user pool sign-in options” section and click the “Next” button
  • In the “Configure security requirements” step, select “No MFA” in the “Multi-factor authentication” section and click the “Next” button
  • In the “Configure sign-up experience” step, leave the defaults unchanged and click the “Next” button
  • In the “Configure message delivery” step, select “Select email with Cognito” option in the “Email” section and click the “Next” button
  • In the “Integrate your app” step,
    • Enter “hello-world-user-pool” in the “User pool name” field 
    • Select “Use the Cognito Hosted UI” checkbox 
    • Select “Use a Cognito domain” option and enter “https://hello-world-api” in the “Cognito domain” field
    • In the “Initial app client” section, enter “hello-world-client” in the “App client name” field, select “Generate a client secret” option, and enter the postman callback URL “https://oauth.pstmn.io/v1/callback” in the “Allowed callback URLs” section
    • Click the “Next” button
  • In the “Review and create” step, click the “Create user pool” button

4. Create a Resource Server

A resource server is an OAuth server that authorizes access to protected resources (APIs) by verifying the access token and the access levels based on scopes present in the token. A scope is a level of access that an app can request to a resource. 

In the context of this exercise, we will create a resource server and a custom scope that will be later used to protect the endpoint that we created earlier. 

Create a resource server and a custom scope by following the below steps 

  • Navigate to Cognito service -> “User pools” -> “hello-world-user-pool” (the one we just created in the previous step) -> “App Integration” tab
  • In the “Resource servers” section, click the “Create resource server” button
  • In the “Resource server” section,
    • Enter “hello-world-resource-server” in the “Resource server name” field 
    • Enter “hello-world-api” in the “Resource server identifier” field
  • In the “Custom scopes” section, click the “Add custom scope” button
  • Enter “read.helloworld.message” in the “Scope name” field
  • Enter “read helloworld message” in the “Description” field
  • Click the “Create resource server” button

5. Update the custom scope in the app client

We need to update the custom scope we created in the previous step to the app client, so that the client app (the one that uses the client id of this app client), will receive the custom scope in the access token that gets generated.

Update the custom scope in the app client by following the below steps

  • Navigate to “App Integration” tab and scroll down to the “App clients and analytics” section
  • Click the “hello-world-client” app to open up the configuration screen for this app client
  • Click the “Edit” button In the “Hosted UI” section
  • Scroll down to the bottom of the screen and select the custom scope that we created in the previous step from the “Custom scopes” dropdown
  • Click the “Save changes” button

6. Create an Authorizer in API Gateway

Now that we have set up the Cognito side of things by creating an OAuth server and defining a custom scope and configuring the scope in the app client, let’s create an Authorizer for the REST API endpoint. 

The authorizer will be configured to use the Cognito user pool, to allow the users of this Cognito user pool to access the endpoint. In addition to it, we will also update the OAuth scope, so that in runtime, the scope will be validated against the access token claims, before allowing access to this protected API endpoint. 

Create an Authorizer for the REST API endpoint by following the below steps

  • Navigate to API Gateway service and open the “hello-world-api” that we created earlier
  • Click the “Authorizers” option from the left navigation menu
  • Click the “Create New Authorizer” button
  • Enter “hello-world-authorizer” in the “Name” field
  • Select the type as “Cognito
  • Select the “hello-world-user-pool” Cognito user pool in the “Cognito User Pool” field
  • Enter “Authorization” in the “Token Source” field
  • Click the “Create” button

7. Configure the Authorizer and the custom OAuth scope for the API endpoint

As the last step, we need to configure the Authorizer we just created and the custom OAuth scope that we created earlier for the REST API endpoint. 

Configure the Authorizer and the custom OAuth scope for the API endpoint by following the below steps

  • Navigate to API Gateway and open the “hello-world-api” again
  • In the Resources section, click the “GET” Http Method
  • Click the “Method Request” box on the right side of the screen
  • Click the pencil icon in the “Authorization” field and select the “hello-world-authorizer” that we just created and click the check mark to save the selection
  • Click the pencil icon in the “OAuth Scopes” field and paste the OAuth scope “hello-world-api/read.helloworld.message” that we created earlier. Please note we need to combine both the “Resource server identifier” and the “Custom scope” and then enter that value in the OAuth scope field. 
  • Click the check mark in the OAuth scopes field to persist the scope entered
  • Select “Deploy API” from the “Actions” dropdown
  • In the “Deploy API” dialog box, select “[New Stage]” in the “Deployment stage” dropdown and enter “dev” as the “Stage Name”. Click the “Deploy” button

8. Create a user in the Cognito User Pool

We have completed all the configurations that we need to create a REST API endpoint and secure it using Cognito User Pool utilizing OAuth Server and scopes. Before we go ahead and start testing the API using Postman, we need to create a user in the Cognito User Pool using which we will invoke the endpoint. 

Create a new user in the Cognito User Pool by following the below steps

  • Navigate to “hello-world-user-pool” in Cognito and click the “Create user” button in the “Users” section present in the “Users” tab
  • Select “Send an email invitation” option
  • Enter a user name in the “User name” field
  • Enter a valid email address in the “Email address” field.
  • Enter a password in the “Password” field
  • Click the “Create user” button

9. Test the API endpoint

Let’s use Postman to test the API endpoint. Postman will act as the client app for this REST API endpoint. 

Create a new request in Postman to invoke the hello-world-api by following the below steps

  • Create a new request by clicking the “Plus” icon and creating a new tab
  • Select the HTTP Method as “GET” and enter the API endpoint URL. If you don’t have the endpoint URL, then navigate to API Gateway and open the “hello-world-api” and click the “Stages” option in the left navigation menu and click the stage name “dev”. Pick the URL from the top of the screen displayed in the right hand side. 
  • Click the “Authorization” tab
  • Select “OAuth 2.0” in the “Type” dropdown
  • In the “Configure New Token” section -> “Configuration Options” tab,
    • Enter “accessToken” in the “Token Name” field
    • Select “Authorization Code” in the “Grant Type” dropdown
    • Select “Authorize using browser” checkbox
    • Enter the “Auth URL” by appending “/oauth2/authorize” to the “Cognito Domain” value in the Cognito user pool (Cognito Domain will be present in the “App Integration” tab in Cognito user pool. For example: https://hello-world-api.auth.us-east-2.amazoncognito.com/oauth2/authorize
    • Enter the “Access Token URL” by appending “/oauth2/token” to the “Cognito Domain”. For example: https://hello-world-api.auth.us-east-2.amazoncognito.com/oauth2/token
    • Enter the “Client ID” and “Client Secret” obtained from the “App client” that we created in the Cognito user pool. 
    • Enter the custom OAuth scope “hello-world-api/read.helloworld.message” in the “Scope” field 
    • Select “Send as Basic Auth Header” in the “Client Authentication” dropdown
  • Click the “Get New Access Token” button and enter the username / password in the Cognito login screen that opens up in the browser. 
  • Once authenticated it will route back to Postman, accept the access token and use it
  • Click the “Send” button to invoke the API endpoint which should return a 200 response with the text “Hello World!” in the Response body.

That’s it to configure OAuth Resource Server in the Cognito user pool to protect the REST API endpoints created in API Gateway using custom OAuth scopes.

10. References

Leave a Comment

Your email address will not be published. Required fields are marked *