Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ $ cdk destroy

| Example | Description |
|---------|-------------|
| [amazon-verified-permissions-rest-api](https://github.com/aws-samples/aws-cdk-examples/tree/master/python/amazon-verified-permissions-rest-api/) | Creating a REST API Gateway with Amazon Verified Permissions for fine-grained authorization |
| [api-cors-lambda](https://github.com/aws-samples/aws-cdk-examples/tree/master/python/api-cors-lambda/) | Shows creation of Rest API (GW) with an /example GET endpoint, with CORS enabled |
| [application-load-balancer](https://github.com/aws-samples/aws-cdk-examples/tree/master/python/application-load-balancer/) | Using an AutoScalingGroup with an Application Load Balancer |
| [appsync-graphql-dynamodb](https://github.com/aws-samples/aws-cdk-examples/tree/master/python/appsync-graphql-dynamodb/) | Creating a single GraphQL API with an API Key, and four Resolvers doing CRUD operations over a single DynamoDB |
Expand Down
10 changes: 10 additions & 0 deletions python/amazon-verified-permissions-rest-api/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
*.swp
package-lock.json
__pycache__
.pytest_cache
.venv
*.egg-info

# CDK asset staging directory
.cdk.staging
cdk.out
107 changes: 107 additions & 0 deletions python/amazon-verified-permissions-rest-api/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@

# Amazon Verified Permissions REST API (AWS CDK Python)

This example deploys a REST API secured by Amazon Verified Permissions (AVP) and Amazon Cognito. The stack demonstrates how to combine a Cedar policy store with an API Gateway Request Authorizer so that only members of the appropriate Cognito group can invoke protected routes.

> :information_source: Detailed guides live in the `docs/` directory:
> - [`docs/architecture.md`](docs/architecture.md) – system design and authorization model
> - [`docs/deployment.md`](docs/deployment.md) – environment setup and deployment steps
> - [`docs/operations.md`](docs/operations.md) – testing, troubleshooting, and cleanup

## Solution Highlights

- **End-to-end RBAC** – Cognito users receive access tokens that are evaluated by AVP before API Gateway invokes your Lambda handlers.
- **Cedar-first design** – The Cedar schema and policies are provisioned with the `cdklabs.cdk_verified_permissions` library for repeatable deployments.
- **Composable infrastructure** – Cognito, Verified Permissions, and API Gateway live in dedicated nested stacks to make future customization straightforward.
- **Language mix** – The authorizer runs on Node.js (to use the AWS SDK for AVP), while the demo business logic stays in simple Python Lambda handlers.

| Resource | Description |
|----------|-------------|
| Cognito User Pool & Client | Provides user management and issues JWT access tokens. Adds `admin` and `user` groups. |
| Verified Permissions Policy Store | Hosts the Cedar schema and static policies mapping Cognito groups to REST actions. |
| API Gateway REST API | Exposes `/`, `/user`, and `/admin` endpoints secured by a custom Request Authorizer. |
| Lambda Functions | Node.js authorizer for AVP calls plus Python handlers that simulate protected resources. |

## Quick Start

### 1. Set Up the Environment

```bash
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
```

Install or upgrade the AWS CDK Toolkit if needed:

```bash
npm install -g aws-cdk@latest
```

Bootstrap the target account and region the first time you deploy CDK there:

```bash
cdk bootstrap aws://<ACCOUNT_ID>/<REGION>
```

### 2. Deploy the Stack

```bash
cdk deploy
```

Note the `ApiEndpoint` output once the deployment completes.

For additional deployment options, refer to [`docs/deployment.md`](docs/deployment.md).

## Trying the API

1. Create or confirm a Cognito user and add them to the `user` and/or `admin` groups. The implementation guide provides ready-to-run CLI snippets.
2. Authenticate against the user pool (for example, with `aws cognito-idp initiate-auth`) and capture the access token.
3. Call the API with the bearer token:

```bash
curl -i \
-H "Authorization: Bearer $ACCESS_TOKEN" \
"$API_URL/user"
```

- Members of the `user` group receive `Hello from User!`.
- Only `admin` members may call `/admin`.

## Project Layout

```
├── app.py # CDK entry point
├── stack/
│ ├── main.py # Root stack wiring together nested stacks
│ ├── apigw/ # REST API, Lambda integrations, authorizer
│ ├── cognito/ # Cognito user pool, client, and groups
│ ├── verified_permissions/ # Cedar schema and policies
│ └── lambdas/ # Authorizer (Node.js) and demo handlers (Python)
└── docs/implementation-guide.md # In-depth deployment and operations guide
```

## Customizing Verified Permissions

1. Update `stack/verified_permissions/schema.py` with new entity types or actions.
2. Modify or add policy definitions under `stack/verified_permissions/policy/`.
3. Redeploy with `cdk deploy` to publish the schema and policies.

Consider migrating to policy templates if you need per-tenant or per-resource authorization decisions.

## Cleanup

Destroy the stack when you are finished to avoid unexpected charges:

```bash
cdk destroy
```

## Additional Reading

- [`docs/architecture.md`](docs/architecture.md) – architecture diagrams, component breakdown, and Cedar policy model.
- [`docs/deployment.md`](docs/deployment.md) – prerequisites, bootstrapping, and deployment workflows.
- [`docs/operations.md`](docs/operations.md) – post-deployment tasks, troubleshooting tips, and sample CLI commands.
- [Amazon Verified Permissions User Guide](https://docs.aws.amazon.com/verifiedpermissions/latest/userguide/)
- [Cedar policy language](https://www.cedarpolicy.com/)
16 changes: 16 additions & 0 deletions python/amazon-verified-permissions-rest-api/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/usr/bin/env python3
import os
import aws_cdk as cdk
from stack.main import Backend

app = cdk.App()
Backend(
app,
"AmazonVerifiedPermissionsRestAPI",
env=cdk.Environment(
account=os.getenv("CDK_DEFAULT_ACCOUNT"),
region=os.getenv("CDK_DEFAULT_REGION"),
),
)

app.synth()
87 changes: 87 additions & 0 deletions python/amazon-verified-permissions-rest-api/cdk.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
{
"app": "python3 app.py",
"watch": {
"include": [
"**"
],
"exclude": [
"README.md",
"cdk*.json",
"requirements*.txt",
"source.bat",
"**/__init__.py",
"**/__pycache__",
"tests"
]
},
"context": {
"@aws-cdk/aws-lambda:recognizeLayerVersion": true,
"@aws-cdk/core:checkSecretUsage": true,
"@aws-cdk/core:target-partitions": [
"aws",
"aws-cn"
],
"@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true,
"@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true,
"@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true,
"@aws-cdk/aws-iam:minimizePolicies": true,
"@aws-cdk/core:validateSnapshotRemovalPolicy": true,
"@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true,
"@aws-cdk/aws-s3:createDefaultLoggingPolicy": true,
"@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true,
"@aws-cdk/aws-apigateway:disableCloudWatchRole": true,
"@aws-cdk/core:enablePartitionLiterals": true,
"@aws-cdk/aws-events:eventsTargetQueueSameAccount": true,
"@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true,
"@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true,
"@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true,
"@aws-cdk/aws-route53-patters:useCertificate": true,
"@aws-cdk/customresources:installLatestAwsSdkDefault": false,
"@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true,
"@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true,
"@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true,
"@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true,
"@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true,
"@aws-cdk/aws-redshift:columnId": true,
"@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true,
"@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true,
"@aws-cdk/aws-apigateway:requestValidatorUniqueId": true,
"@aws-cdk/aws-kms:aliasNameRef": true,
"@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true,
"@aws-cdk/core:includePrefixInUniqueNameGeneration": true,
"@aws-cdk/aws-efs:denyAnonymousAccess": true,
"@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true,
"@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": true,
"@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": true,
"@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters": true,
"@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": true,
"@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": true,
"@aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForCodeCommitSource": true,
"@aws-cdk/aws-cloudwatch-actions:changeLambdaPermissionLogicalIdForLambdaAction": true,
"@aws-cdk/aws-codepipeline:crossAccountKeysDefaultValueToFalse": true,
"@aws-cdk/aws-codepipeline:defaultPipelineTypeToV2": true,
"@aws-cdk/aws-kms:reduceCrossAccountRegionPolicyScope": true,
"@aws-cdk/aws-eks:nodegroupNameAttribute": true,
"@aws-cdk/aws-ec2:ebsDefaultGp3Volume": true,
"@aws-cdk/aws-ecs:removeDefaultDeploymentAlarm": true,
"@aws-cdk/custom-resources:logApiResponseDataPropertyTrueDefault": false,
"@aws-cdk/aws-s3:keepNotificationInImportedBucket": false,
"@aws-cdk/aws-ecs:enableImdsBlockingDeprecatedFeature": false,
"@aws-cdk/aws-ecs:disableEcsImdsBlocking": true,
"@aws-cdk/aws-ecs:reduceEc2FargateCloudWatchPermissions": true,
"@aws-cdk/aws-dynamodb:resourcePolicyPerReplica": true,
"@aws-cdk/aws-ec2:ec2SumTImeoutEnabled": true,
"@aws-cdk/aws-appsync:appSyncGraphQLAPIScopeLambdaPermission": true,
"@aws-cdk/aws-rds:setCorrectValueForDatabaseInstanceReadReplicaInstanceResourceId": true,
"@aws-cdk/core:cfnIncludeRejectComplexResourceUpdateCreatePolicyIntrinsics": true,
"@aws-cdk/aws-lambda-nodejs:sdkV3ExcludeSmithyPackages": true,
"@aws-cdk/aws-stepfunctions-tasks:fixRunEcsTaskPolicy": true,
"@aws-cdk/aws-ec2:bastionHostUseAmazonLinux2023ByDefault": true,
"@aws-cdk/aws-route53-targets:userPoolDomainNameMethodWithoutCustomResource": true,
"@aws-cdk/aws-elasticloadbalancingV2:albDualstackWithoutPublicIpv4SecurityGroupRulesDefault": true,
"@aws-cdk/aws-iam:oidcRejectUnauthorizedConnections": true,
"@aws-cdk/core:enableAdditionalMetadataCollection": true,
"@aws-cdk/aws-lambda:createNewPoliciesWithAddToRolePolicy": true,
"@aws-cdk/aws-s3:setUniqueReplicationRoleName": true
}
}
61 changes: 61 additions & 0 deletions python/amazon-verified-permissions-rest-api/docs/architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Architecture Overview

## Solution Summary

This project deploys a reference REST API that protects endpoints with [Amazon Verified Permissions (AVP)](https://aws.amazon.com/verified-permissions/) and Amazon Cognito. It demonstrates how to combine a Cedar policy store with an API Gateway Request Authorizer so that only members of the appropriate Cognito group can invoke protected routes.

## High-Level Diagram

![High-level architecture diagram](diagram.svg)

## Core Components

| Component | Purpose | Source Reference |
|-----------|---------|------------------|
| Amazon Cognito User Pool | Manages users and issues JWT access tokens. Provides `admin` and `user` groups for RBAC. | `stack/cognito/main.py` |
| Amazon Verified Permissions | Stores the Cedar schema and group-based policies. | `stack/verified_permissions/` |
| Amazon API Gateway REST API | Provides `/`, `/user`, and `/admin` resources secured by the custom authorizer. | `stack/apigw/main.py` |
| Request Authorizer Lambda (Node.js) | Exchanges the bearer token and request context for an AVP decision via `isAuthorizedWithToken`. | `stack/lambdas/authorizer/main.js` |
| Demo Business Lambdas (Python) | Return simple payloads to illustrate RBAC outcomes. | `stack/lambdas/{user,admin}/main.py` |

## Request Authorization Flow

1. A client authenticates against the Cognito User Pool to obtain an access token.
2. The client calls the API Gateway endpoint, sending the JWT in the `Authorization` header.
3. API Gateway forwards the request to the custom Request Authorizer Lambda.
4. The authorizer extracts the bearer token, request path, and method, then calls `VerifiedPermissions.isAuthorizedWithToken`.
5. Verified Permissions evaluates the Cedar policies against the supplied action and principal:
- Members of the `admin` group can invoke all routes.
- Members of the `user` group can invoke `/` and `/user` only.
6. API Gateway allows or denies the original request based on the evaluation result.

## Authorization Model

### Namespace and Entities

The Cedar namespace is `amazonverified`. It defines:

- `User`: Represents a Cognito user.
- `UserGroup`: Represents Cognito groups (`admin`, `user`).
- `Application`: Represents the protected API surface.

Actions map one-to-one with the REST routes: `get /`, `get /user`, and `get /admin`.

### Schema Reference

The Cedar schema is defined in `stack/verified_permissions/schema.py` and supplied to the `PolicyStore` during stack synthesis. The schema is serialized to JSON through `cedar_schema = {"cedar_json": json.dumps(cedar_json_schema)}`.

### Policies

Policies are provided via `StaticPolicyDefinitionProperty` constructs:

- `stack/verified_permissions/policy/admin.py` grants the `admin` group access to every action.
- `stack/verified_permissions/policy/user.py` grants the `user` group access to `get /` and `get /user`.

Because policies refer to Cognito groups using the pattern `"<userPoolId>|<groupName>"`, redeploying to a new environment automatically scopes the policy to the correct pool.

### Extending the Model

1. Update `cedar_json_schema` with new actions or entity types.
2. Add or modify policy definitions. You can compose static policies or migrate to policy templates for dynamic authorizations.
3. Re-deploy the stack to publish the new schema and policies.
47 changes: 47 additions & 0 deletions python/amazon-verified-permissions-rest-api/docs/deployment.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Deployment Guide

> Verified Permissions is available in specific AWS regions. Choose a supported region (for example, `ap-south-1`) before deploying.

## Prerequisites

- AWS account with permissions to create IAM, Cognito, Lambda, API Gateway, and Verified Permissions resources.
- AWS CLI configured with credentials for the target account and region.
- Node.js v22.20.0 or lts (required for the AWS CDK Toolkit and Lambda bundling).
- Python 3.13 or later.
- AWS CDK Toolkit (`npm install -g aws-cdk@latest`).

## Set Up the Project Environment

```bash
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
```

If the virtual environment fails to create automatically, create it manually with the same commands.

## Bootstrap the Environment (First Deployment Only)

```bash
cdk bootstrap aws://<ACCOUNT_ID>/<REGION>
```

Replace `<ACCOUNT_ID>` and `<REGION>` with the target deployment values.

## Deploy the Stack

```bash
cdk deploy
```

The deployment outputs the REST API endpoint (for example, `https://abc123.execute-api.us-east-1.amazonaws.com/prod/`). Record this value for testing.

## Updating the Stack

After modifying infrastructure or policies, redeploy with the same command:

```bash
cdk deploy
```

CDK will perform a change set comparison and apply only the required updates.
37 changes: 37 additions & 0 deletions python/amazon-verified-permissions-rest-api/docs/diagram.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading