How to test Cognito Integration with Amazon Verified Permissions using LocalStack
LocalStack now supports Amazon Verified Permissions (AVP), allowing you to test your application's authorization logic and Cognito integration entirely on your local machine. This tutorial shows how to use Cedar policies to validate access control without deploying to AWS.

Introduction
Amazon Verified Permissions (AVP) lets you manage access control separately from your application using external policies. It works with identity providers like Amazon Cognito by using Identity Sources, so your app can use JWTs from Cognito in authorization requests. AVP uses the token to identify the principal of the request and maps the claims to Cedar-specific values. The ID token claims get mapped to principal attributes and access token claims map to the request context, to decide if access should be allowed.
Testing the integration between your app logic, Cognito identities, and AVP policies usually requires deploying everything to AWS. This can make development slower and add costs during repeated testing and debugging.
This is where LocalStack comes in—Amazon Verified Permissions (AVP) is a new service provider in LocalStack’s core cloud emulator. It allows you to use the AVP APIs locally to test your authorization logic and integration points. This tutorial will walk you through:
- Creating a Cognito User Pool, User, and Group
- Setting up an AVP Policy Store and Identity Source connected to Cognito
- Writing a Cedar policy and testing an authorization request with a Cognito ID token
… all within your local LocalStack environment! Let’s dive right in, shall we?
Prerequisites
localstack
CLI with LocalStack Auth Token- Docker
- AWS CLI installed and configured with
awslocal
- jq installed (a command-line JSON processor)
Step 1: Start LocalStack
Before creating any resources, ensure your LocalStack container is running. To use AVP, make sure your Auth Token is configured.
localstack auth set-token <YOUR_LOCALSTACK_AUTH_TOKEN>localstack start
Throughout this tutorial, we will use awslocal
, a wrapper around the standard aws
CLI that automatically directs commands to your LocalStack container.
Step 2: Create a Cognito UserPool
First, create a Cognito User Pool using the CreateUserPool
API. This pool will manage the users for our test application. The following command creates a user pool named avp-test
:
USER_POOL_OUTPUT=$(awslocal cognito-idp create-user-pool \ --pool-name avp-test \ --output json)
USER_POOL_ID=$(echo $USER_POOL_OUTPUT | jq -r '.UserPool.Id')USER_POOL_ARN=$(echo $USER_POOL_OUTPUT | jq -r '.UserPool.Arn')
echo "UserPool ID: $USER_POOL_ID"echo "UserPool ARN: $USER_POOL_ARN"
The Id
and Arn
are extracted from the output using jq and made available as USER_POOL_ID
and USER_POOL_ARN
for upcoming commands.
Step 3: Create a User Pool Client
Next, create a User Pool Client. This client represents your application and allows it to interact with the User Pool (e.g., authenticate users).
Use the CreateUserPoolClient
API, using the USER_POOL_ID
created in the previous step:
CLIENT_OUTPUT=$(awslocal cognito-idp create-user-pool-client \ --user-pool-id "$USER_POOL_ID" \ --client-name avp-client \ --output json)
CLIENT_ID=$(echo $CLIENT_OUTPUT | jq -r '.UserPoolClient.ClientId')
echo "Client ID: $CLIENT_ID"
The new client’s ClientId
will be accessible via the CLIENT_ID
variable in the next steps.
Step 4: Create a Cognito Group
To test policies based on group membership, create a Cognito Group named AVPGroup
within your User Pool, using the CreateGroup
API with the USER_POOL_ID
.
awslocal cognito-idp create-group \ --user-pool-id "$USER_POOL_ID" \ --group-name AVPGroup
The output will be:
{ "Group": { "GroupName": "AVPGroup", "UserPoolId": "us-east-1_1a971945fe4f47e7836833e5bef5da86", "LastModifiedDate": "2025-05-06T14:33:50+05:30", "CreationDate": "2025-05-06T14:33:50+05:30" }}
Step 5: Create a Cognito User and Get Tokens
Now, create a test user, set their password, add them to the AVPGroup
, and initiate authentication to retrieve their ID and Access tokens.
5.1: Create the user
awslocal cognito-idp admin-create-user \ --user-pool-id "$USER_POOL_ID" \ --username avp-user \ --user-attributes Name=email,Value="avp@test.com" Name=email_verified,Value=true
5.2: Set the user’s password
awslocal cognito-idp admin-set-user-password \ --user-pool-id "$USER_POOL_ID" \ --username avp-user \ --password Test123! \ --permanent
5.3: Add the user to the group
awslocal cognito-idp admin-add-user-to-group \ --user-pool-id "$USER_POOL_ID" \ --username avp-user \ --group-name AVPGroup
5.4: Authenticate and get tokens
AUTH_OUTPUT=$(awslocal cognito-idp initiate-auth \ --auth-flow USER_PASSWORD_AUTH \ --client-id "$CLIENT_ID" \ --auth-parameters USERNAME=avp-user,PASSWORD=Test123! \ --output json)
ID_TOKEN=$(echo $AUTH_OUTPUT | jq -r '.AuthenticationResult.IdToken')
The resulting IdToken
is captured into the ID_TOKEN
variable, ready for the authorization request.
Step 6: Create a Policy Store
A Policy Store is a container for your AVP schema and policies. You can create one using the CreatePolicyStore
API.
POLICY_STORE_OUTPUT=$(awslocal verifiedpermissions create-policy-store \ --validation-settings mode=OFF \ --description "Policy Store with Cognito" \ --output json)
POLICY_STORE_ID=$(echo $POLICY_STORE_OUTPUT | jq -r '.policyStoreId')
echo "Policy Store ID: $POLICY_STORE_ID"
Step 7: Create an Identity Source
An Identity Source tells Verified Permissions how to interpret identity information from an external IdP, in this case, Cognito, using the CreateIdentitySource
API.
First, generate a configuration file named identity_source.json
using our previous variables:
cat <<EOF > identity_source.json{ "cognitoUserPoolConfiguration": { "userPoolArn": "$USER_POOL_ARN", "clientIds":["$CLIENT_ID"], "groupConfiguration": {"groupEntityType": "UserGroup"} }}EOF
echo "Generated identity_source.json:"cat identity_source.json
Now, create the Identity Source using this configuration:
awslocal verifiedpermissions create-identity-source \ --policy-store-id "$POLICY_STORE_ID" \ --principal-entity-type "User" \ --configuration file://identity_source.json
The output will be:
{ "createdDate": "2025-05-06T09:16:27.929347+00:00", "identitySourceId": "8kvcln2xNr4YvRgVKhkm4c", "lastUpdatedDate": "2025-05-06T09:16:27.929347+00:00", "policyStoreId": "dfmlV05O8OKtRvs7UjwX6C"}
This command configures AVP to recognize users from your Cognito pool and map Cognito groups to the UserGroup
entity type within AVP.
Step 8: Create a Policy
Now, create a Cedar policy that grants permission based on the user’s group membership derived from the Cognito token.
This policy allows any principal who is a member of UserGroup::"AVPGroup"
to perform the Action::"create"
on the resource Album::"vacations"
.
cat <<EOF > policy_cognito.json{ "static": { "description": "Grant any User that is part of the UserGroup AVPGroup access to create the vacations Album", "statement": "permit(principal in UserGroup::\"AVPGroup\", action == Action::\"create\", resource == Album::\"vacations\");" }}EOF
echo "Generated policy_cognito.json:"cat policy_cognito.json
Then, create the policy within your Policy Store. The policyId
of the created policy is captured for reference.
POLICY_OUTPUT=$(awslocal verifiedpermissions create-policy \ --definition file://policy_cognito.json \ --policy-store-id "$POLICY_STORE_ID" \ --output json)
POLICY_ID=$(echo $POLICY_OUTPUT | jq -r '.policyId')echo "Policy ID: $POLICY_ID"
Step 9: Authorize Request with Cognito Token
Finally, the setup can be tested by making an authorization request using the IsAuthorizedWithToken
API. This API takes the Cognito IdToken
directly.
Verified Permissions will use the Identity Source configuration to extract the principal’s identity and group membership (cognito:groups
claim) from the token and evaluate it against the policy.
awslocal verifiedpermissions is-authorized-with-token \ --policy-store-id "$POLICY_STORE_ID" \ --action actionType=Action,actionId=create \ --resource entityType=Album,entityId=vacations \ --identity-token "$ID_TOKEN"
Since the user avp-user
is part of the AVPGroup
, and the policy allows members of this group to perform the requested action on the resource, the request should be allowed:
{ "decision": "ALLOW", "determiningPolicies": [ { "policyId": "e19OlOOv8zvyB3uWYpW3ZA" } ], "errors": [], "principal": { "entityType": "User", "entityId": "us-east-1_1a971945fe4f47e7836833e5bef5da86|9fa13cb6-3238-44d8-a174-1a1970607d79" }}
The expected output showing an ALLOW
decision confirms the successful integration.
Summary
In this tutorial, you successfully tested the integration between Amazon Verified Permissions and Cognito using LocalStack. Using LocalStack’s emulation, you can develop and test applications with Verified Permissions and Cognito locally, ensuring your authorization logic works correctly before deploying to production.
You can now explore advanced use cases or scenarios, such as:
- Write Cedar policies that use additional attributes from Cognito ID or Access tokens
- Test scenarios involving multiple groups or more fine-grained attribute-based access control (ABAC)
- Incorporate integration testing into your CI/CD pipeline for automated validation