DEV Community

Cover image for Authorization and Amazon Verified Permissions - A New Way to Manage Permissions Part XV: AVP with Cognito groups
Daniel Aniszkiewicz for AWS Heroes

Posted on

Authorization and Amazon Verified Permissions - A New Way to Manage Permissions Part XV: AVP with Cognito groups

Welcome back to my blog post series dedicated to building authorization using Cedar and Amazon Verified Permissions. In a previous blog post, we learned about the usage of AVP getting started. Today, we will cover the topic of using Cognito Groups with AVP.

Related to AVP getting started, Cognito groups are now supported in the AVP, as you well know from the previous blogpost. However, in this blogpost we won't focus on building a getting started gateway with AVP, but we will focus on building authorization with AVP itself using cognito groups (So you can use it for API gateway, resource server, and enhance it for your use case pretty easily. If you want to see the finished result, you can use the scenario from avp-cli here.

In addition, we will also use the new BatchIsAuthorizedWithToken API operation that was recently released.

Cognito groups support

Currently, AVP only supports Cognito as an external identity provider(IdP). We create the reference from Cognito through the CreateIdentitySource action.

For configuration, we need to specify:

  • policyStoreID
    • userPoolArn (ARN of the Cognito user pool),
  • clientIds (optional, unique application client IDs that are associated with the specified Amazon Cognito user pool.)
  • principalEntityType (namespace and data type for the entity that will be a principal for our policy store)

What was introduced into the CognitoUserPoolConfiguration, that groupConfiguration is the entity type that the policy store maps to groups from the Cognito user pool identity source. In short, which entity from the policy store will be mapped from the Cognito group.

Below you can see example values for principalEntityType and groupEntityType in the case of namespace MyApp for the AVP policy store to see how it will look in reality.
Explanation

That is, in short, when building an identity source for a policy store, we need to pass it on, and there you go!

Practice

Today, we would like to build an authorization system for the banking transaction system. Within, we have a user who can perform operations such as:

  • View
  • Approve
  • Edit for resources of type TransactionRecord.

We would like to introduce here RBAC based on groups:

  • transaction_viewers
  • transaction_approvers
  • transaction_editors in Cognito, that is, depending on the role, the user can perform different operations.

System

Cognito part

Let's start with our IDP from Cognito, we will create a user pool with groups, app client id, and add users there and add them to groups. Then we will obtain a Cognito token and use it for testing.

In order not to build this from scratch, for the purpose of this blogpost I created a ready-made Cloudformation template that will create a Cognito user pool, groups, and app client, we just need to later create users and add them to groups.

Navigate to the AWS console, and open the AWS console, open Cloudformation, and upload the authentication.yaml from the avp-cli repo.

CF

After the deployment, as outputs you can see both the CognitoUserPoolId as well as CognitoUserPoolId.

Navigate to the Cognito user pool.

Groups

You will see 3 groups generated, now what you need to do, is create users and assign them to the groups:

Groups assigment

Later we will need to copy the ARN of the Cognito User Pool, App Client Id (it is called avp-client, and initiate the auth with our users.

I am aware I am showing the cognito user pool, however before publishing the blogpost the cognito user pool on my site will be already deleted. Never share credentials, ARNs, JWTs, ClientIds, or secrets over the internet!

Policy Store part

Let's start building a policy store, log in to your AWS console, open the AVP service, and create an empty policy store.

Go to the schema tab, and paste this schema. Save the changes, you should see this action diagram and entity types diagram:

Actions diagram

Entity types diagram

As we can see, we have principal as user (i.e., who), actions like approve, edit, view (i.e., what operation we want to perform), and resource (i.e., on what resource we want to do), i.e., TransactionRecord.

In addition, we can see that we have an entity of type Group, to which our user belongs (Hierarchy).

Identity Source.

Now, we can reference our IDP (so Cognito) with AVP. Navigate to the Identity sources in the AVP console and create a new one. Fill the user pool ID (copy it from the user pool details from the Cognito details page, the same for the client application ID (you will find it in the App integration section of the user pool).

For the principal details we will use the Principal type as BankingTransationSystem::User so this will map our user pool to Cognito as a principal. Moreover the principal ID so the token issued for the connected user pool will be mapped to a principal ID in the format ‘|

Unfortunately, we cannot add groupEntityType from the AWS console position (we cannot both see the value, modify it, or add it from the console). Although we can do it through AWS-CLI, AWS-SDK, Cloudformation, or through AVP-CLI.

AVP CLI for CreateIdentitySource

So if we want to do it via AVP-CLI, we can do it with:

➜  avp-cli git:(main) node index.js
🚀 Welcome to the AVP CLI Tool!
Designed to streamline your interactions with the Amazon Verified Permissions (AVP) service.
🔧 Create, manage, and delete policy stores, schemas, and policies. Plus, deploy and test with predefined scenarios!
⚠️ Ensure your AWS credentials are correctly set up before proceeding.
? What would you like to do? Manual approach
? What would you like to do? Create Identity Source
? Enter the ID of the policy store TKdiMU5n3DeypqsnEHb9d6
? Enter the entity type of the principal BankingTransactionSystem::User
? Enter the Amazon Cognito User Pool ARN arn:aws:cognito-idp:eu-west-1:ACCOUND_ID_GOES_HERE:userpool/eu-west-1_sXVC4jUDf
? Enter the Amazon Cognito App Client ID 5b33v2gf0cr1c4n6qj04rajjhf
? Enter the entity type for groups, or press enter to skip 
Enter fullscreen mode Exit fullscreen mode

Under the hood it will make an SDK call like this:

{
  policyStoreId: 'TKdiMU5n3DeypqsnEHb9d6',
  principalEntityType: 'BankingTransactionSystem::User',
  configuration: {
    cognitoUserPoolConfiguration: {
      userPoolArn: 'arn:aws:cognito-idp:eu-west-1:ACCOUNT_ID_GOES_HERE:userpool/eu-west-1_sXVC4jUDf',
      clientIds: [Array],
      groupConfiguration: [Object]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

In the console you will see it like:

Identity Source details

Hopefully, soon we will see the group over there.

Policies

Now it's time to add three access policies, for each action separately, it will look like this:

permit (
    principal in
        BankingTransactionSystem::Group::"<user-pool-id-goes-here>|<cognito-group-name-here>",
    action in [BankingTransactionSystem::Action::"View"],
    resource
);
Enter fullscreen mode Exit fullscreen mode

What we see is:

  • we check if the principal is (with in operator which checks hierarchies) in a specific group (in the above example transaction_viewers) from the Cognito user pool.
  • we check a specific action
  • resource is any.

We need to add it for all three groups (per action) accordingly, and it will look like this:

Policy.

Feel free to add those three policies from the AVP-CLI repo, remember to replace <user-pool-id-goes-here> with your Cognito user pool ID (not the ARN!)

Remember about | between cognito user pool id and group name from the Cognito!

Approach with AVP-CLI scenario

As you can see, a little has to be done to get everything ready. What you can do more simply is to use avp-cli to do the deployment of all the things you need.

Remember to first do the deployment of the authentication.yml file in the Cloudformation console if you haven't done it before.

Next, simply use the AVP-CLI:

➜  avp-cli git:(main)AWS_PROFILE=personal node index.js
🚀 Welcome to the AVP CLI Tool!
Designed to streamline your interactions with the Amazon Verified Permissions (AVP) service.
🔧 Create, manage, and delete policy stores, schemas, and policies. Plus, deploy and test with predefined scenarios!
⚠️ Ensure your AWS credentials are correctly set up before proceeding.
? What would you like to do? Use prepared scenarios
? Choose a scenario Ecommerce with Cognito Groups Scenario
? Enter the ARN of the Cognito User Pool arn:aws:cognito-idp:eu-west-1:ACCOUNT_NUMBER:userpool/eu-west-1_sXVC4jUDf
? Enter the App Client ID 5b33v2gf0cr1c4n6qj04rajjhf
./scenarios/ecommerceCognitoGroupsScenario/ecommerceCognitoGroupsScenario.json
Starting creating scenario: Ecommerce Cognito Group Scenario
description: This is a basic scenario with a group cognito management platform schema and three policies.
Policy store created with ID: Ny9v75vt5cQGxzEovBgL9R
Schema put successfully for policy store ID: Ny9v75vt5cQGxzEovBgL9R
Creating an identity source...
Identity source created with ID: 9M6FTv52wSnLTR2LAJkRYa
Creating a static policy...
Static policy created with ID: W4Z31yfapD5jWJqvXr57XH
Creating a static policy...
Static policy created with ID: 5k1QXfuWxYJqQa9r3Mnygh
Creating a static policy...
Static policy created with ID: WDu7Pac7umBpHrY2o55TYF
┌────────────────────────────────────────┬────────────────────────────────────────┬────────────────────────────────────────┐
│ Policy ID                              │ Policy Store ID                        │ Created Date                           │
├────────────────────────────────────────┼────────────────────────────────────────┼────────────────────────────────────────┤
│ W4Z31yfapD5jWJqvXr57XH                 │ Ny9v75vt5cQGxzEovBgL9R                 │ 2024-04-27 18:00                       │
├────────────────────────────────────────┼────────────────────────────────────────┼────────────────────────────────────────┤
│ 5k1QXfuWxYJqQa9r3Mnygh                 │ Ny9v75vt5cQGxzEovBgL9R                 │ 2024-04-27 18:00                       │
├────────────────────────────────────────┼────────────────────────────────────────┼────────────────────────────────────────┤
│ WDu7Pac7umBpHrY2o55TYF                 │ Ny9v75vt5cQGxzEovBgL9R                 │ 2024-04-27 18:00                       │
└────────────────────────────────────────┴────────────────────────────────────────┴────────────────────────────────────────┘
Generating of the undefined is finished. Open the AWS console to play around with that.

Consider testing it with our prepared test scenarios:
Either use `Test Scenario` in main CLI to select the specific test scenario, or use below path as argument to `IsAuthorized` from the manual approach option of the CLI:
 Remember to update the policy-store-id within files.
- ./scenarios/ecommerceCognitoGroupsScenario/allow_test_1.json (Access for user who has transaction_viewer group can view) allow
- ./scenarios/ecommerceCognitoGroupsScenario/allow_test_2.json (Access for user who has transaction_approve group can approve) allow
- ./scenarios/ecommerceCognitoGroupsScenario/allow_test_3.json (Access for user who has transaction_edit group can edit) allow

Generating of the ecommerceCognitoGroupsScenario is finished. Open the AWS console to play around with that.
Enter fullscreen mode Exit fullscreen mode

And that's it, you can test.

Prepration for tests

First, we need an access token (or ID token) from Cognito for our user.

Remember, when you create a new user, you must generate a new password for them, which can be done in the AWS CLI with:

aws cognito-idp admin-respond-to-auth-challenge 
Enter fullscreen mode Exit fullscreen mode

You can generate code via AWS-CLI, SDK, or by doing the HTTP request via POSTMAN or curl like this:

curl --location 'https://cognito-idp.<REGION>.amazonaws.com/<USER_POOL_HERE!!!!!>' \
--header 'Content-Type: application/x-amz-json-1.1' \
--header 'Accept: */*' \
--header 'X-Amz-Target: AWSCognitoIdentityProviderService.InitiateAuth' \
--data-raw '{
  "AuthFlow": "USER_PASSWORD_AUTH",
  "ClientId": "<ID_HERE>",
  "AuthParameters": {
    "USERNAME": "<USERNAME>",
    "PASSWORD": "<PASSWORD>"
  }
}
'
Enter fullscreen mode Exit fullscreen mode

As a response, you will obtain both an access token and an id token, use the access token.

If you decode the token with jwt.io you will see:

{
  "sub": "sub",
  "cognito:groups": [
    "transaction_viewers"
  ],
  "iss": 'BLABLA",
  ...
  "auth_time": 1713899648,
  "exp": 1713903248,
  "iat": 1713899648,`
  "username": "daniel"
}
Enter fullscreen mode Exit fullscreen mode

so you can double check at this point, whether a proper group is within the JWT of a user.

Testing

So we can't test it using the test bench because it doesn't support isAuthorizedWithToken operations and for Batch, so we can do it through AWS CLI, SDK, or through AVP-CLI.

With isAuthorizedWithToken

For authorization with a token, we will use the isAuthorizedWithToken operation.

We can provide both an access token and an ID token, we will use the access token. It is worth reminding that our principal is a user from the Cognito user pool. AVP on its side will decode the token and check if it is ok, so we don't need to do it on our side.

*If you delete an Amazon Cognito user pool or user, tokens from that deleted pool or that deleted user continue to be usable until they expire.
*

The structure of the request is here. For use usecase we can use this allow test. Generate the access token, and pass it together with policy store ID.

So I've generated an access token for a user which is in the transaction_viewers group, and based on the allow test I will try to access the View action.

now we will use AVP-CLI:

? What would you like to do? Manual approach
? What would you like to do? Make an authorization decision with Cognito Identity Token
? Enter the path for json test file scenarios/ecommerceCognitoGroupsScenario/allow_test_1.json

┌──────────┬──────────────────────────────┬────────────────────┬──────────────────────────────┬──────────────────────────────┬──────────────────────────────┬──────────────────────────────┬──────────────────────────────┐
│ Decision │ Determining Policies         │ Errors             │ Policy Store ID              │ Principal                    │ Action                       │ Resource                     │ Context                      │
├──────────┼──────────────────────────────┼────────────────────┼──────────────────────────────┼──────────────────────────────┼──────────────────────────────┼──────────────────────────────┼──────────────────────────────┤
│ ALLOW    │ 7UQxoV3bvzpYvGnm939HZH       │                    │ TKdiMU5n3DeypqsnEHb9d6       │ BankingTransactionSystem::Us │ BankingTransactionSystem::Ac │ BankingTransactionSystem::Tr │ {}                           │
│          │                              │                    │                              │ er::eu-west-1_sXVC4jUDf|f245 │ tion::View                   │ ansactionRecord::*           │                              │
│          │                              │                    │                              │ a454-90c1-7084-7709-1dfbfdb1 │                              │                              │                              │
│          │                              │                    │                              │ 0930                         │                              │                              │                              │

Enter fullscreen mode Exit fullscreen mode

If I will try the second test scenario I will be rejected, as my user is only allowed to view.

With BatchIsAuthorizedWithToken

As recently BatchIsAuthorizedWithToken has been released, we can use it to test our scenario.

With this operation, we can make up to 30 requests during a single request (and we paid per request, and performance is better). Note that the current request limit is 30, and also our batch can contain up to 100 resources and up to 99 user groups. Example structure is here.

We can pass either an access token or an ID token. The request is either principal or resource oriented.

So, in our scenario, what we can do, is to check, whether my user can access multiple actions on a single resource by making one request to AVP.

Batch

I've prepared test file for that here.

Let's try with AVP-CLI now:

➜  avp-cli AWS_PROFILE=personal node index.js
🚀 Welcome to the AVP CLI Tool!
Designed to streamline your interactions with the Amazon Verified Permissions (AVP) service.
🔧 Create, manage, and delete policy stores, schemas, and policies. Plus, deploy and test with predefined scenarios!
⚠️ Ensure your AWS credentials are correctly set up before proceeding.
? What would you like to do? Manual approach
? What would you like to do? Make a batch authorization decision with Cognito Identity Token
? Enter the path for batch authorization with token json test file scenarios/ecommerceCognitoGroupsScenario/batch_allow_test.json
Making batch with token authorization decision...

┌──────────┬──────────────────────────────┬────────────────────┬──────────────────────────────┬──────────────────────────────┬──────────────────────────────┬──────────────────────────────┬──────────────────────────────┐
│ Decision │ Determining Policies         │ Errors             │ Policy Store ID              │ Principal                    │ Action                       │ Resource                     │ Context                      │
├──────────┼──────────────────────────────┼────────────────────┼──────────────────────────────┼──────────────────────────────┼──────────────────────────────┼──────────────────────────────┼──────────────────────────────┤
│ ALLOW    │ 7UQxoV3bvzpYvGnm939HZH       │                    │ TKdiMU5n3DeypqsnEHb9d6       │ BankingTransactionSystem::Us │ BankingTransactionSystem::Ac │ BankingTransactionSystem::Tr │ {}                           │
│          │                              │                    │                              │ er::eu-west-1_sXVC4jUDf|f245 │ tion::View                   │ ansactionRecord::123         │                              │
│          │                              │                    │                              │ a454-90c1-7084-7709-1dfbfdb1 │                              │                              │                              │
│          │                              │                    │                              │ 0930                         │                              │                              │                              │
├──────────┼──────────────────────────────┼────────────────────┼──────────────────────────────┼──────────────────────────────┼──────────────────────────────┼──────────────────────────────┼──────────────────────────────┤
│ DENY     │                              │                    │ TKdiMU5n3DeypqsnEHb9d6       │ BankingTransactionSystem::Us │ BankingTransactionSystem::Ac │ BankingTransactionSystem::Tr │ {}                           │
│          │                              │                    │                              │ er::eu-west-1_sXVC4jUDf|f245 │ tion::Edit                   │ ansactionRecord::123         │                              │
│          │                              │                    │                              │ a454-90c1-7084-7709-1dfbfdb1 │                              │                              │                              │
│          │                              │                    │                              │ 0930                         │                              │                              │                              │
├──────────┼──────────────────────────────┼────────────────────┼──────────────────────────────┼──────────────────────────────┼──────────────────────────────┼──────────────────────────────┼──────────────────────────────┤
│ DENY     │                              │                    │ TKdiMU5n3DeypqsnEHb9d6       │ BankingTransactionSystem::Us │ BankingTransactionSystem::Ac │ BankingTransactionSystem::Tr │ {}                           │
│          │                              │                    │                              │ er::eu-west-1_sXVC4jUDf|f245 │ tion::Approve                │ ansactionRecord::123         │                              │
│          │                              │                    │                              │ a454-90c1-7084-7709-1dfbfdb1 │                              │                              │                              │
│          │                              │                    │                              │ 0930                         │                              │                              │                              │
└──────────┴──────────────────────────────┴──────────
Enter fullscreen mode Exit fullscreen mode

We will obtain the array of authorization request decisions. As you can see, my user is only allowed to View.

Sumup

That's it for today. We now know:

  • how to build AVP policy stores with Cognito as IDP
  • use Cognito groups within the access policy
  • how to test it.

Now go build!

Top comments (0)