API-linked policy stores - Amazon Verified Permissions

API-linked policy stores

When you create a new policy store in the Amazon Verified Permissions console, you can choose the Set up with API Gateway and an identity source option. With this option, you build an API-linked policy store, an authorization model for applications that authenticate with Amazon Cognito user pools or an OIDC identity provider (IdP) and get data from Amazon API Gateway APIs. To get started, see Create a policy store with a connected API and identity provider.

Important

Policy stores that you create with the Set up with API Gateway and an identity source option in the Verified Permissions console aren’t intended for immediate deployment to production. With your initial policy store, finalize your authorization model and export the policy store resources to CloudFormation. Deploy Verified Permissions to production programmatically with the AWS Cloud Development Kit (CDK). For more information, see Moving to production with AWS CloudFormation.

In a policy store that's linked to an API and an identity source, your application presents a user pool token in an authorization header when it makes a request to the API. The identity source of your policy store provides token validation for Verified Permissions. The token forms the principal in authorization requests with the IsAuthorizedWithToken API. Verified Permissions builds policies around the group membership of your users, as presented in a groups claim in identity (ID) and access tokens, for example cognito:groups for user pools. Your API processes the token from your application in a Lambda authorizer and submits it to Verified Permissions for an authorization decision. When your API receives the authorization decision from the Lambda authorizer, it passes the request on to your data source or denies the request.

Components of identity source and API Gateway authorization with Verified Permissions
  • An Amazon Cognito user pool or OIDC IdP that authenticates and groups users. Users' tokens populate the group membership and the principal or context that Verified Permissions evaluates in your policy store.

  • An API Gateway REST API. Verified Permissions defines actions from API paths and API methods, for example MyAPI::Action::get /photo.

  • A Lambda function and a Lambda authorizer for your API. The Lambda function takes in bearer tokens from your user pool, requests authorization from Verified Permissions, and returns a decision to API Gateway. The Set up with Cognito and API Gateway workflow automatically creates this Lambda authorizer for you.

  • A Verified Permissions policy store. The policy store identity source is your user pool. The policy store schema reflects the configuration of your API, and the policies link user groups to permitted API actions.

  • An application that authenticates users with your IdP and appends tokens to API requests.

How Verified Permissions authorizes API requests

When you create a new policy store and select the Set up with Cognito and API Gateway option, Verified Permissions creates policy store schema and policies. The schema and policies reflect API actions and the user pool groups that you want to authorize to take the actions. Verified Permissions also creates the Lambda function and authorizer. You must configure the new authorizer on a method in your API.

A diagram that displays the flow of an authorization request with Amazon API Gateway, Amazon Cognito, and Amazon Verified Permissions.
  1. Your user signs in with your application through Amazon Cognito or another OIDC IdP. The IdP issues ID and access tokens with the user's information.

  2. Your application stores the JWTs. For more information, see Using tokens with user pools in the Amazon Cognito Developer Guide..

  3. Your user requests data that your application must retrieve from an external API.

  4. Your application requests data from a REST API in API Gateway. It appends an ID or access token as a request header.

  5. If your API has a cache for the authorization decision, it returns the previous response. If caching is disabled or the API has no current cache, API Gateway passes the request parameters to a token-based Lambda authorizer.

  6. The Lambda function sends an authorization request to a Verified Permissions policy store with the IsAuthorizedWithToken API. The Lambda function passes the elements of an authorization decision:

    1. The user's token as the principal.

    2. The API method combined with the API path, for example GetPhoto, as the action.

    3. The term Application as the resource.

  7. Verified Permissions validates the token. For more information about how Amazon Cognito tokens are validated, see Authorization with Amazon Verified Permissions in the Amazon Cognito Developer Guide.

  8. Verified Permissions evaluates the authorization request against the policies in your policy store and returns an authorization decision.

  9. The Lambda authorizer returns an Allow or Deny response to API Gateway.

  10. The API returns data or an ACCESS_DENIED response to your application. Your application processes and displays the results of the API request.

Adding attribute-based access control (ABAC)

A typical authentication session with an IdP returns ID and access tokens. You can pass either of these token types as a bearer token in application requests to your API. Depending on your choices when you create your policy store, Verified Permissions expects one of the two types of tokens. Both types carry information about the user’s group membership. For more information about token types in Amazon Cognito, see Using tokens with user pools in the Amazon Cognito Developer Guide.

After you create a policy store, you can add and extend policies. For example, you can add new groups to your policies as you add them to your user pool. Because your policy store is already aware of the way that your user pool presents groups in tokens, you can permit a set of actions for any new group with a new policy.

You might also want to extend the group-based model of policy evaluation into a more precise model based on user properties. User pool tokens contain additional user information that can contribute to authorization decisions.

ID tokens

ID tokens represent a user’s attributes and have the highest level of fine-grained access control. To evaluate email addresses, phone numbers, or custom attributes like department and manager, evaluate the ID token.

Access tokens

Access tokens represent a user’s permissions with OAuth 2.0 scopes. To add a layer of authorization or to set up requests for additional resources, evaluate the access token. For example, you can validate that a user is in the appropriate groups and carries a scope like PetStore.read that generally authorizes access to the API. User pools can add custom scopes to tokens with resource servers and with token customization at runtime.

See Working with identity sources in schema and policies for example policies that process claims in ID and access tokens.

Considerations for API-linked policy stores

When you build an API-linked policy store in the Verified Permissions console, you're creating a test for an eventual production deployment. Before you move to production, establish a fixed configuration for your API and user pool. Consider the following factors:

API Gateway caches responses

In API-linked policy stores, Verified Permissions creates a Lambda authorizer with an Authorization caching TTL of 120 seconds. You can adjust this value or turn off caching in your authorizer. In an authorizer with caching enabled, your authorizer returns the same response each time until the TTL expires. This can extend the effective lifetime of user pool tokens by a duration that equals the caching TTL of the requested stage.

Amazon Cognito groups can be reused

Amazon Verified Permissions determines group membership for user pool users from the cognito:groups claim in a user's ID or access token. The value of this claim is an array of the friendly names of the user pool groups that the user belongs to. You can't associate user pool groups with a unique identifier.

User pool groups that you delete and recreate with the same name present to your policy store as the same group. When you delete a group from a user pool, delete all references to the group from your policy store.

API-derived namespace and schema are point-in-time

Verified Permissions captures your API at a point in time: it only queries your API when you create your policy store. When the schema or name of your API changes, you must update your policy store and Lambda authorizer, or create a new API-linked policy store. Verified Permissions derives the policy store namespace from the name of your API.

Lambda function has no VPC configuration

The Lambda function that Verified Permissions creates for your API authorizer isn't connected to a VPC. By default. APIs that have network access restricted to private VPCs can't communicate with the Lambda function that authorizes access requests with Verified Permissions.

Verified Permissions deploys authorizer resources in CloudFormation

To create an API-linked policy store, you must sign in a highly-privileged AWS principal to the Verified Permissions console. This user deploys an AWS CloudFormation stack that creates resources across several AWS services. This principal must have the permission to add and modify resources in Verified Permissions, IAM, Lambda, and API Gateway. As a best practice, don't share these credentials with other administrators in your organization.

See Moving to production with AWS CloudFormation for an overview of the resources that Verified Permissions creates.

Moving to production with AWS CloudFormation

API-linked policy stores are a way to quickly build an authorization model for an API Gateway API. They are designed to serve as a testing environment for the authorization component of your application. After you create your test policy store, spend time refining the policies, schema, and Lambda authorizer.

You might adjust the architecture of your API, requiring equivalent adjustments to your policy store schema and policies. API-linked policy stores don't automatically update their schema from API architecture–Verified Permissions only polls the API at the time you create a policy store. If your API changes sufficiently, you might have to repeat the process with a new policy store.

When your application and authorization model are ready for deployment to production, integrate the API-linked policy store that you developed with your automation processes. As a best practice, we recommend that you export the policy store schema and policies into a AWS CloudFormation template that you can deploy to other AWS accounts and AWS Regions.

The results of the API-linked policy store process are an initial policy store and a Lambda authorizer. The Lambda authorizer has several dependent resources. Verified Permissions deploys these resources in an automatically-generated CloudFormation stack. To deploy to production, you must collect the policy store and the Lambda authorizer resources into a template. An API-linked policy store is made of the following resources:

  1. AWS::VerifiedPermissions::PolicyStore: Copy your schema to the SchemaDefinition object. Escape " characters as \".

  2. AWS::VerifiedPermissions::IdentitySource: Copy values from the output of GetIdentitySource from your test policy store and modify as needed.

  3. One or more of AWS::VerifiedPermissions::Policy: Copy your policy statement to the Definition object. Escape " characters as \".

  4. AWS::Lambda::Function, AWS::IAM::Role, AWS::IAM::Policy, AWS::ApiGateway::Authorizer, AWS::Lambda::Permission: Copy the template from the Template tab of the stack that Verified Permissions deployed when you created your policy store.

The following template is an example policy store. You can append the Lambda authorizer resources from your existing stack to this template.

{ "AWSTemplateFormatVersion": "2010-09-09", "Resources": { "MyExamplePolicyStore": { "Type": "AWS::VerifiedPermissions::PolicyStore", "Properties": { "ValidationSettings": { "Mode": "STRICT" }, "Description": "ApiGateway: PetStore/test", "Schema": { "CedarJson": "{\"PetStore\":{\"actions\":{\"get /pets\":{\"appliesTo\":{\"principalTypes\":[\"User\"],\"resourceTypes\":[\"Application\"],\"context\":{\"type\":\"Record\",\"attributes\":{}}}},\"get /\":{\"appliesTo\":{\"principalTypes\":[\"User\"],\"resourceTypes\":[\"Application\"],\"context\":{\"type\":\"Record\",\"attributes\":{}}}},\"get /pets/{petId}\":{\"appliesTo\":{\"context\":{\"type\":\"Record\",\"attributes\":{}},\"resourceTypes\":[\"Application\"],\"principalTypes\":[\"User\"]}},\"post /pets\":{\"appliesTo\":{\"principalTypes\":[\"User\"],\"resourceTypes\":[\"Application\"],\"context\":{\"type\":\"Record\",\"attributes\":{}}}}},\"entityTypes\":{\"Application\":{\"shape\":{\"type\":\"Record\",\"attributes\":{}}},\"User\":{\"memberOfTypes\":[\"UserGroup\"],\"shape\":{\"attributes\":{},\"type\":\"Record\"}},\"UserGroup\":{\"shape\":{\"type\":\"Record\",\"attributes\":{}}}}}}" } } }, "MyExamplePolicy": { "Type": "AWS::VerifiedPermissions::Policy", "Properties": { "Definition": { "Static": { "Description": "Policy defining permissions for testgroup cognito group", "Statement": "permit(\nprincipal in PetStore::UserGroup::\"us-east-1_EXAMPLE|testgroup\",\naction in [\n PetStore::Action::\"get /\",\n PetStore::Action::\"post /pets\",\n PetStore::Action::\"get /pets\",\n PetStore::Action::\"get /pets/{petId}\"\n],\nresource);" } }, "PolicyStoreId": { "Ref": "MyExamplePolicyStore" } }, "DependsOn": [ "MyExamplePolicyStore" ] }, "MyExampleIdentitySource": { "Type": "AWS::VerifiedPermissions::IdentitySource", "Properties": { "Configuration": { "CognitoUserPoolConfiguration": { "ClientIds": [ "1example23456789" ], "GroupConfiguration": { "GroupEntityType": "PetStore::UserGroup" }, "UserPoolArn": "arn:aws:cognito-idp:us-east-1:123456789012:userpool/us-east-1_EXAMPLE" } }, "PolicyStoreId": { "Ref": "MyExamplePolicyStore" }, "PrincipalEntityType": "PetStore::User" }, "DependsOn": [ "MyExamplePolicyStore" ] } } }