Authorize API Gateway APIs using Amazon Verified Permissions and Amazon Cognito
Externalizing authorization logic for application APIs can yield multiple benefits for Amazon Web Services (AWS) customers. These benefits can include freeing up development teams to focus on application logic, simplifying application and resource access audits, and improving application security by using continual authorization. Amazon Verified Permissions is a scalable permissions management and fine-grained authorization service that you can use for externalizing application authorization. Along with controlling access to application resources, you can use Verified Permissions to restrict API access to authorized users by using Cedar policies. However, a key challenge in adopting an external authorization system like Verified Permissions is the effort involved in defining the policy logic and integrating with your API. This blog post shows how Verified Permissions accelerates the process of securing REST APIs that are hosted on Amazon API Gateway for Amazon Cognito customers.
<h2>Setting up API authorization using Amazon Verified Permissions</h2>
<p>As a developer, there are several tasks you need to do in order to use Verified Permissions to store and evaluate policies that define which APIs a user is permitted to access. Although Verified Permissions enables you to decouple authorization logic from your application code, you may need to spend time up front integrating Verified Permissions with your applications. You may also need to spend time learning the Cedar policy language, defining a policy schema, and authoring policies that enforce access control on APIs. Lastly, you may need to spend additional time developing and testing the <a href="https://aws.amazon.com/lambda/" target="_blank" rel="noopener">AWS Lambda</a> authorizer function logic that builds the authorization request for Verified Permissions and enforces the authorization decision. </p>
<h2>Getting started with the simplified wizard</h2>
<p>Amazon Verified Permissions now includes a console-based wizard that you can use to quickly create building blocks to set up your application’s API Gateway to use Verified Permissions for authorization. Verified Permissions generates an authorization model based on your APIs and policies that allows only authorized Cognito groups access to your APIs. Additionally, it deploys a Lambda authorizer, which you attach to the APIs you want to secure. After the authorizer is attached, API requests are authorized by Verified Permissions. The generated Cedar policies and schema flatten the learning curve, yet allow you full control to modify and help you adhere to your security requirements.</p>
<h2>Overview of sample application</h2>
<p>In this blog post, we demonstrate how you can simplify the task of securing permissions to a sample application API by using the Verified Permissions console-based wizard. We use a sample pet store application which has two resources:</p>
<ol>
<li><strong>PetStorePool</strong> – An Amazon Cognito <a href="https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-identity-pools.html" target="_blank" rel="noopener">user pool</a> with users in one of three groups: customers, employees, and owners.</li>
<li><strong>PetStore</strong> – An Amazon API Gateway <a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-rest-api.html" target="_blank" rel="noopener">REST API</a> derived from <a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-create-api-from-example.html" target="_blank" rel="noopener">importing the PetStore example API</a> and extended with a mock integration for administration. This mock integration returns a message with a URI path that uses <span>{“statusCode”: 200}</span> as the integration request and <span>{“Message”: “User authorized for $context.path”}</span> as the integration response.</li>
</ol>
<p>The PetStore has the following four authorization requirements that allow access to the related resources. All other behaviors should be denied.</p>
<ol>
<li>Both <strong>authenticated</strong> and <strong>unauthenticated</strong> users are allowed to access the root URL.
</li>
<li>All <strong>authenticated</strong> users are allowed to get the list of pets, or get a pet by its identifier.
<ul>
<li>GET /pets</li>
<li>GET /pets/{petid}</li>
</ul> </li>
<li>The <strong>employees</strong> and <strong>owners</strong> group are allowed to add new pets.
<ul>
<li>POST /pets</li>
</ul> </li>
<li>Only the <strong>owners</strong> group is allowed to perform administration functions. These are defined using an API Gateway <a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-set-up-simple-proxy.html" target="_blank" rel="noopener">proxy resource</a> that enables a single integration to implement a set of API resources.
<ul>
<li>ANY /admin/{proxy+}</li>
</ul> </li>
</ol>
<h2>Walkthrough</h2>
<p>Verified Permissions includes a setup wizard that connects a Cognito user pool to an API Gateway REST API and secures resources based on Cognito group membership. In this section, we provide a walkthrough of the wizard that generates authorization building blocks for our sample application.</p>
<h4>To set up API authorization based on Cognito groups</h4>
<ol>
<li>On the <a href="https://console.aws.amazon.com/verifiedpermissions" target="_blank" rel="noopener">Amazon Verified Permissions</a> page in the AWS Management Console, choose <strong>Create a new policy store</strong>.</li>
<li>On the <strong>Specify policy store details </strong>page under <strong>Starting options</strong>, select <strong>Set up with Cognito and API Gateway</strong>, and then choose <strong>Next</strong>.
<div id="attachment_34013" class="wp-caption aligncenter">
<img aria-describedby="caption-attachment-34013" src="https://infracom.com.sg/wp-content/uploads/2024/04/img1-6.png" alt="Figure 1: Starting options" width="740" class="size-full wp-image-34013">
<p id="caption-attachment-34013" class="wp-caption-text">Figure 1: Starting options</p>
</div> </li>
<li>On the <strong>Import resources and actions </strong>page<strong> </strong>under<strong> API Gateway details</strong>, select the <strong>API</strong> and <strong>Deployment stage</strong> from the dropdown lists. (A <a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-stages.html" target="_blank" rel="noopener">REST API stage</a> is a named reference to a deployment.) For this example, we selected the <strong>PetStore</strong> API and the <strong>demo</strong> stage.
<div id="attachment_34014" class="wp-caption aligncenter">
<img aria-describedby="caption-attachment-34014" src="https://infracom.com.sg/wp-content/uploads/2024/04/img2-6.png" alt="Figure 2: API Gateway and deployment stage" width="740" class="size-full wp-image-34014">
<p id="caption-attachment-34014" class="wp-caption-text">Figure 2: API Gateway and deployment stage</p>
</div> </li>
<li>Choose<strong> Import API</strong> to generate a <strong>Map of imported resources and actions</strong>. For our example, this list includes <span>Action::”get /pets”</span> for getting the list of pets, <span>Action::”get /pets/{petId}”</span> for getting a single pet, and <span>Action::”post /pets”</span> for adding a new pet. Choose <strong>Next</strong>.
<div id="attachment_34022" class="wp-caption aligncenter">
<img aria-describedby="caption-attachment-34022" src="https://infracom.com.sg/wp-content/uploads/2024/04/img3-6.png" alt="Figure 3: Map of imported resources and actions" width="740" class="size-full wp-image-34022">
<p id="caption-attachment-34022" class="wp-caption-text">Figure 3: Map of imported resources and actions</p>
</div> </li>
<li>On the <strong>Choose identity source</strong> page, select an Amazon Cognito user pool (<strong>PetStorePool</strong> in our example). For <strong>Token type to pass to API</strong>, select a token type. For our example, we chose the default value, <strong>Access token</strong>, because Cognito recommends <a href="https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-the-access-token.html" target="_blank" rel="noopener">using the access token </a>to authorize API operations. The additional claims available in an <a href="https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-the-id-token.html" target="_blank" rel="noopener">id token</a> may support more fine-grained access control. For <strong>Client application validation</strong>, we also specified the default, to not validate that tokens match a configured <a href="https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-settings-client-apps.html" target="_blank" rel="noopener">app client</a> ID. Consider validation when you have multiple <a href="https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-settings-client-apps.html" target="_blank" rel="noopener">user pool app clients</a> configured with different permissions.
<div id="attachment_34023" class="wp-caption aligncenter">
<img aria-describedby="caption-attachment-34023" src="https://infracom.com.sg/wp-content/uploads/2024/04/img4-6.png" alt="Figure 4: Choose Cognito user pool as identity source" width="740" class="size-full wp-image-34023">
<p id="caption-attachment-34023" class="wp-caption-text">Figure 4: Choose Cognito user pool as identity source</p>
</div> </li>
<li>Choose <strong>Next</strong>.</li>
<li>On the <strong>Assign actions to groups</strong> page under <strong>Group selection</strong>, choose the <a href="https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-user-groups.html" target="_blank" rel="noopener">Cognito user pool groups</a> that can take actions in the application. This solution uses native Cognito group membership to control permissions. In Figure 5, the customers group is not used for access control, we deselected it and it isn’t included in the generated policies. Instead, access to <strong>get /pets</strong> and <strong>get/pets/{petId}</strong> is granted to all authenticated users using a different authorizer that we define later in this post.
<div id="attachment_34024" class="wp-caption aligncenter">
<img aria-describedby="caption-attachment-34024" src="https://infracom.com.sg/wp-content/uploads/2024/04/img5-5.png" alt="Figure 5: Assign actions to groups" width="740" class="size-full wp-image-34024">
<p id="caption-attachment-34024" class="wp-caption-text">Figure 5: Assign actions to groups</p>
</div> </li>
<li>For each of the groups, choose which actions are allowed. In our example, <strong>post /pets</strong> is the only action selected for the <strong>employees</strong> group. For the <strong>owners</strong> group, all of the <strong>/admin/{proxy+}</strong> actions are additionally selected. Choose <strong>Next</strong>.
<div id="attachment_34025" class="wp-caption aligncenter">
<img aria-describedby="caption-attachment-34025" src="https://infracom.com.sg/wp-content/uploads/2024/04/img6-3.png" alt="Figure 6: Groups employees and owners" width="740" class="size-full wp-image-34025">
<p id="caption-attachment-34025" class="wp-caption-text">Figure 6: Groups employees and owners</p>
</div> </li>
<li>On the <strong>Deploy app integration</strong> page, review the <strong>API Gateway Integration </strong>details. Choose <strong>Create policy store</strong>.
<div id="attachment_34026" class="wp-caption aligncenter">
<img aria-describedby="caption-attachment-34026" src="https://infracom.com.sg/wp-content/uploads/2024/04/img7-2.png" alt="Figure 7: API Gateway integration" width="740" class="size-full wp-image-34026">
<p id="caption-attachment-34026" class="wp-caption-text">Figure 7: API Gateway integration</p>
</div> </li>
<li>On the <strong>Create policy store</strong> summary page, review the progress of the setup. Choose <strong>Check deployment</strong> to check the progress of Lambda authorizer.
<div id="attachment_34027" class="wp-caption aligncenter">
<img aria-describedby="caption-attachment-34027" src="https://infracom.com.sg/wp-content/uploads/2024/04/img8-2.png" alt="Figure 8: Create policy store" width="740" class="size-full wp-image-34027">
<p id="caption-attachment-34027" class="wp-caption-text">Figure 8: Create policy store</p>
</div> </li>
</ol>
<p>The setup wizard deployed a CloudFormation stack with a Lambda authorizer. This authorizes access to the API Gateway resources for the employees and owners groups. For the resources that should be authorized for all authenticated users, a separate Cognito User Pool authorizer is required. You can use the following AWS CLI apigateway <a href="https://awscli.amazonaws.com/v2/documentation/api/latest/reference/apigateway/create-authorizer.html" target="_blank" rel="noopener">create-authorizer</a> command to create the authorizer.</p>
<div class="hide-language">
<pre class="unlimited-height-code"><code class="lang-text">aws apigateway create-authorizer
–rest-api-id wrma51eup0
–name “Cognito-PetStorePool”
–type COGNITO_USER_POOLS
–identity-source “method.request.header.Authorization”
–provider-arns “arn:aws:cognito-idp:us-west-2:000000000000:userpool/us-west-2_iwWG5nyux”
After the CloudFormation stack deployment completes and the second Cognito authorizer is created, there are two authorizers that can be attached to PetStore API resources, as shown in Figure 9.</p>
<div id="attachment_34028" class="wp-caption aligncenter">
<img aria-describedby="caption-attachment-34028" src="https://infracom.com.sg/wp-content/uploads/2024/04/img9-2.png" alt="Figure 9: PetStore API Authorizers" width="780" class="size-full wp-image-34028" />
<p id="caption-attachment-34028" class="wp-caption-text">Figure 9: PetStore API Authorizers</p>
</div>
<p>In Figure 9, <strong>Cognito-PetStorePool</strong> is a Cognito user pool authorizer. Because this example uses an access token, an authorization scope (for example, a custom scope like <span>petstore/api</span>) is specified when attached to the <span>GET /pets</span> and <span>GET /pets/{petId}</span> resources.</p>
<p><strong>AVPAuthorizer-XXX</strong> is a <em>request parameter-based</em> Lambda authorizer, which determines the caller’s identity from the configured identity sources. In Figure 9, these sources are <strong>Authorization (Header)</strong>, <strong>httpMethod (Context)</strong>, and <strong>path (Context)</strong>. This authorizer is attached to the <span>POST /pets</span> and <span>ANY /admin/{proxy+}</span> resources. Authorization caching is initially set at 120 seconds and can be <a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/configure-api-gateway-lambda-authorization-with-console.html" target="_blank" rel="noopener">configured using the API Gateway console</a>.</p>
<p>This combination of multiple authorizers and caching reduces the number of authorization requests to Verified Permissions. For API calls that are available to all authenticated users, using the <strong>Cognito-PetStorePool</strong> authorizer instead of a policy permitting the customers group helps avoid chargeable authorization requests to Verified Permissions. Applications where the users initiate the same action multiple times or have a predictable sequence of actions will experience high cache hit rates. For repeated API calls that use the same token, <strong>AVPAuthorizer-XXX</strong> caching results in lower latency, fewer requests per second, and reduced costs from chargeable requests. The use of caching can delay the time between policy updates and policy enforcement, meaning that the policy updates to Verified Permissions are not realized until the timeout or the <a href="https://docs.aws.amazon.com/apigateway/latest/api/API_FlushStageAuthorizersCache.html" target="_blank" rel="noopener">FlushStageAuthorizersCache</a> API is called.</p>
<h2>Deployment architecture</h2>
<p>Figure 10 illustrates the runtime architecture after you have used the Verified Permissions setup wizard to perform the deployment and configuration steps. After the users are authenticated with the Cognito PetStorePool, API calls to the PetStore API are authorized with the Cognito access token. Fine-grained authorization is performed by Verified Permissions using a Lambda authorizer. The wizard automatically created the following four items for you, which are labelled in Figure 10:</p>
<ol>
<li>A Verified Permissions policy store that is connected to a Cognito identity source.</li>
<li>A Cedar schema that defines the User and UserGroup entities, and an action for each API Gateway resource.</li>
<li>Cedar policies that assign permissions for the employees and owners groups to related actions.</li>
<li>A <a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html" target="_blank" rel="noopener">Lambda authorizer</a> that is configured on the API Gateway.</li>
</ol>
<div id="attachment_34029" class="wp-caption aligncenter">
<img aria-describedby="caption-attachment-34029" src="https://infracom.com.sg/wp-content/uploads/2024/04/img10-2.png" alt="Figure 10: Architecture diagram after deployment" width="780" class="size-full wp-image-34029">
<p id="caption-attachment-34029" class="wp-caption-text">Figure 10: Architecture diagram after deployment</p>
</div>
<p>Verified Permissions uses the <a href="https://docs.aws.amazon.com/verifiedpermissions/latest/userguide/what-is-avp.html#avp-cedar" target="_blank" rel="noopener">Cedar policy language</a> to define fine-grained permissions. The default decision for an authorization response is “deny.” The Cedar policies that are generated by the setup wizard can determine an “allow” decision. The principal for each policy is a UserGroup entity with an entity ID format of <span>{user pool id}|{group name}</span>. The action IDs for each policy represent the set of selected API Gateway HTTP methods and resource paths. Note that <span>post /pets</span> is permitted for both employees and owners. The resource in the policy scope is unspecified, because the resource is implicitly the application.</p>
<pre class="unlimited-height-code"><code class="lang-cedar">permit (
principal in PetStore::UserGroup::"us-west-2_iwWG5nyux|employees",
action in [PetStore::Action::"post /pets"],
resource
);
permit (
principal in PetStore::UserGroup::”us-west-2_iwWG5nyux|owners”,
action in
[PetStore::Action::”delete /admin/{proxy+}”,
PetStore::Action::”post /admin/{proxy+}”,
PetStore::Action::”get /admin/{proxy+}”,
PetStore::Action::”patch /admin/{proxy+}”,
PetStore::Action::”put /admin/{proxy+}”,
PetStore::Action::”post /pets”],
resource
);
Validating API security
<p>A set of terminal-based curl commands validate API security for both authorized and unauthorized users, by using different access tokens. For readability, a set of environment variables is used to represent the actual values. <span>TOKEN_C</span>, <span>TOKEN_E</span>, and <span>TOKEN_O</span> contain valid access tokens for respective users in the customers, employees, and owners groups. <span>API_STAGE</span> is the base URL for the PetStore API and demo stage that we selected earlier.</p>
<p>To test that an unauthenticated user is allowed for the <span>GET /</span> root path (Requirement 1 as described in the Overview section of this post), but not allowed to call the <span>GET /pets</span> API (Requirement 2), run the following curl commands. The Cognito-PetStorePool authorizer should return <span>{“message”:”Unauthorized”}</span>.</p>
<div class="hide-language">
<pre class="unlimited-height-code"><code class="lang-text">curl -X GET ${API_STAGE}/
…Welcome to your Pet Store API…
curl -X GET ${API_STAGE}/pets
{“message”:”Unauthorized”}
To test that an authenticated user is allowed to call the GET /pets API (Requirement 2) by using an access token (due to the Cognito-PetStorePool authorizer), run the following curl commands. The user should receive an error message when they try to call the POST /pets API (Requirement 3), because of the AVPAuthorizer. There are no Cedar polices defined for the customers group with the action post /pets.
The following commands will verify that a user in the employees group is allowed the post /pets action (Requirement 3).
The following commands will verify that a user in the employees group is not authorized for the admin APIs, but a user in the owners group is allowed (Requirement 4).
Try it yourself
How could this work with your user pool and REST API? Before you try out the solution, make sure that you have the following prerequisites in place, which are required by the Verified Permissions setup wizard:
- A Cognito user pool, along with Cognito groups that control authorization to the API endpoints.
- An API Gateway REST API in the same Region as the Cognito user pool.
As you review the resources generated by the solution, consider these authorization modeling topics:
- Are access tokens or id tokens preferable for your API? Are there custom claims on your tokens that you would use in future Cedar policies for fine-grained authorization?
- Do multiple authorizers fit your model, or do you have an “all users” group for use in Cedar policies?
- How might you extend the Cedar schema, allowing for new Cedar policies that include URL path parameters, such as {petId} from the example?
Conclusion
This post demonstrated how the Amazon Verified Permissions setup wizard provides you with a step-by-step process to build authorization logic for API Gateway REST APIs using Cognito user groups. The wizard generates a policy store, schema, and Cedar policies to manage access to API endpoints based on the specification of the APIs deployed. In addition, the wizard creates a Lambda authorizer that authorizes access to the API Gateway resources based on the configured Cognito groups. This removes the modeling effort required for initial configuration of API authorization logic and setup of Verified Permissions to receive permission requests. You can use the wizard to set up and test access controls to your APIs based on Cognito groups in non-production accounts. You can further extend the policy schema and policies to accommodate fine-grained or attribute-based access controls, based on specific requirements of the application, without making code changes.
If you have feedback about this post, submit comments in the Comments section below. If you have questions about this post, start a new thread on the Amazon Verified Permissions re:Post or contact AWS Support.
<!-- '"` -->