├── .gitignore ├── .npmignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── bin └── cognito_identity_pool_sample.ts ├── cdk.json ├── jest.config.js ├── lib └── cognito_identity_pool_sample-stack.ts ├── package-lock.json ├── package.json ├── react-sample ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public │ ├── index.html │ ├── manifest.json │ └── robots.txt ├── src │ ├── App.css │ ├── App.js │ ├── App.test.js │ ├── components │ │ ├── ApiAccess.js │ │ ├── S3Access.js │ │ ├── SignIn.js │ │ ├── SignUp.js │ │ └── base-components │ │ │ ├── Home.js │ │ │ └── NavBar.js │ ├── configuration.json │ ├── index.css │ ├── index.js │ ├── reportWebVitals.js │ └── setupTests.js └── webpack.config.js ├── resources ├── pictures │ ├── api-example.png │ ├── architecture-diagram.png │ ├── cdk-outputs.png │ ├── config-file.png │ ├── demo-login.png │ ├── engineering-s3-example.png │ └── legal-s3-example.png ├── pre-token-trigger.js ├── testfile-engineering │ └── testfile-engineering.txt └── testfile-legal │ └── testfile-legal.txt └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | !jest.config.js 2 | *.d.ts 3 | node_modules 4 | 5 | # CDK asset staging directory 6 | .cdk.staging 7 | cdk.out 8 | 9 | .vscode 10 | .DS_Store -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | *.ts 2 | !*.d.ts 3 | 4 | # CDK asset staging directory 5 | .cdk.staging 6 | cdk.out 7 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *main* branch. 27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 3. Ensure local tests pass. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | 43 | ## Finding contributions to work on 44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start. 45 | 46 | 47 | ## Code of Conduct 48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 50 | opensource-codeofconduct@amazon.com with any additional questions or comments. 51 | 52 | 53 | ## Security issue notifications 54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 55 | 56 | 57 | ## Licensing 58 | 59 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sample React App Using ABAC + Identity Pools to access AWS resources 2 | 3 | Project: amazon-cognito-abac-authorization-with-react-sample 4 | 5 | This open-source repository consists of two main items: 6 | * A CDK Script which deploys the backend resources required to demonstrate Attribute Based Access Control (ABAC) using Cognito 7 | * A sample React Application which uses Cognito for authentication and Authorization to AWS resources (using ABAC) 8 | 9 | ## DISCLAIMER 10 | The sample code; software libraries; command line tools; proofs of concept; templates; or other related technology (including any of the foregoing that are provided by our personnel) is provided to you as AWS Content under the AWS Customer Agreement, or the relevant written agreement between you and AWS (whichever applies). You should not use this AWS Content in your production accounts, or on production or other critical data. You are responsible for testing, securing, and optimizing the AWS Content, such as sample code, as appropriate for production grade use based on your specific quality control practices and standards. Deploying AWS Content may incur AWS charges for creating or using AWS chargeable resources, such as running Amazon EC2 instances or using Amazon S3 storage. 11 | 12 | ## Deploy backend resources 13 | This CDK script will deploy the following resources: 14 | ![picture](resources/pictures/architecture-diagram.png) 15 | 16 | (Not depicted - the Cognito Identity Pool uses the Cognito User pool as authentication provider) 17 | 18 | Step 1: Bootstrap the CDK project 19 | ```sh 20 | cdk bootstrap 21 | ``` 22 | 23 | Step 2: Deploy the CDK project 24 | ```sh 25 | cdk deploy 26 | ``` 27 | 28 | Step 3: Wait for the deployment to finish, and don't clear the outputs as this will make the next step easier. 29 | 30 | ## Run Sample React app 31 | ### Pre-requisites 32 | Run the CDK commands above to deploy the following resources in your account: 33 | 1. **Cognito User Pool** - used for authentication of users 34 | 2. **Cognito App Client** - used by the React application to interact with the User Pool 35 | 3. **Cognito Identity Pool** - used to get temporary AWS credentials. The CDK script will create the Identity Pool and use the User Pool as authentication provider. It will also create custom mappings to map the 'department' claim from the user-token to the 'department' Principal Tag, which is used for authorization to resources. 36 | 4. **Lambda pre-token-generation function** - augments the user token returned by Cognito with a 'department' claim (currently hardcoded to "Engineering" for this demo) 37 | 5. **S3 Bucket** - S3 bucket to demonstrate ABAC to AWS resources. In particular, authorization to S3 resources based on claims from the token. The CDK script adds a .zip file under the "Engineering" and "Legal" prefixes. 38 | 6. **Sample API Gateway** - Additional demo to show Authentication and Authorization to an API resource (not using ABAC) 39 | 40 | ### Setup the Configuration file 41 | Once deployed, go to `react-sample/src/configuration.json` and add the relevant inputs required to run the application. For your convenience, we've added the relevant parameters which you need for the configuration file as outputs from the CDK deployment: 42 | ![cdk-output](resources/pictures/cdk-outputs.png) 43 | 44 | Add the relevant outputs to the configuration file: 45 | ![config](resources/pictures/config-file.png) 46 | 47 | ### Create a test user 48 | In order to test the application, you will need a test-user in your Cognito User Pool. You can create a user by going to the User Pool directly in the AWS Console or you can run the following two CLI commands (which requires AWS credentials with the right permissions): 49 | ```shell 50 | aws cognito-idp admin-create-user --user-pool-id --username 51 | ``` 52 | ```shell 53 | aws cognito-idp admin-set-user-password --user-pool-id --username --password --permanent 54 | ``` 55 | ### Start the Web-server locally 56 | Go to the react-sample directory 57 | ```sh 58 | cd react-sample 59 | ``` 60 | 61 | In the main directory of the application run the following: 62 | 63 | ```sh 64 | npm install 65 | ``` 66 | 67 | After the dependencies have been installed, run the following to start a local server: 68 | 69 | ```sh 70 | npm start 71 | ``` 72 | 73 | You should now be able to go to `http://localhost:3000` to view your web-page. You can log in using the test-user credentials created in the Section above. 74 | 75 | ### Screenshots 76 | * Login page, using the test user created previously. This will output the ID token and Access token. Notice that the department claim has been added automatically. 77 | ![login](resources/pictures/demo-login.png) 78 | 79 | * API example - which uses a Cognito Authorizer to validate the token passed into the request. 80 | ![api_example](resources/pictures/api-example.png) 81 | 82 | * S3 ABAC example, when requesting S3 data for the Engineering prefix, the sample ZIP will be listed. It will also display the temporary credentials used for the AWS request. 83 | ![engineering_example](resources/pictures/engineering-s3-example.png) 84 | 85 | * S3 ABAC example, when requesting S3 data for the Legal prefix, the sample ZIP will **not** be listed because the token does not have the 'Legal' department claim. It will also display the temporary credentials used for the AWS request, but the response is an Unauthorized error. 86 | ![legal_example](resources/pictures/legal-s3-example.png) 87 | 88 | 89 | ## Important notes 90 | * This is a demonstration application, and should not be used for production applications 91 | * We do not store your user tokens in LocalStorage or Session Cookies, therefore, whenever the web-page is refreshed, you will have to re-authenticate. Moving between the different pages in the navigation bar is fine. 92 | * We print the AWS credentials on the S3 Access Tab to showcase refreshing AWS credentials. However, if these credentials were to be leaked, users can call AWS resources with the attached permissions. Currently, the role is scoped down to only allow List* access to the sample S3 files, but make sure to be careful with this mechanism. And again, don't use this for production systems. 93 | * When running `npm install` you might get a '6 moderate vulnerabilities found' output. See [this issue](https://github.com/facebook/create-react-app/issues/11174) for more details. 94 | * If you are using AWS Cloud9 to run the sample application, you can change the start script in the `react-sample/package.json` to include the following to change the preview port to 8080: `"start": "PORT=8080 react-scripts start"` 95 | 96 | ## Cleaning up 97 | Run the following command: 98 | ```sh 99 | cdk destroy 100 | ``` 101 | 102 | Or go to Cloudformation service in your console and delete the deployed stack manually. 103 | 104 | **Important** The following resources will not automatically delete: 105 | * The CognitoUser Pool 106 | * The S3 bucket 107 | 108 | Due to the fact that these two resources may contain data / objects you might want to keep, cdk destroy doesn't automatically delete them. You can still delete both resources manually if you wish. 109 | 110 | ## Security 111 | 112 | See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information. 113 | 114 | ## License 115 | 116 | This library is licensed under the MIT-0 License. See the LICENSE file. 117 | -------------------------------------------------------------------------------- /bin/cognito_identity_pool_sample.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import 'source-map-support/register'; 3 | import * as cdk from 'aws-cdk-lib'; 4 | import { CognitoIdentityPoolSampleStack } from '../lib/cognito_identity_pool_sample-stack'; 5 | 6 | const app = new cdk.App(); 7 | new CognitoIdentityPoolSampleStack(app, 'CognitoIdentityPoolSampleStack', { 8 | /* If you don't specify 'env', this stack will be environment-agnostic. 9 | * Account/Region-dependent features and context lookups will not work, 10 | * but a single synthesized template can be deployed anywhere. */ 11 | 12 | /* Uncomment the next line to specialize this stack for the AWS Account 13 | * and Region that are implied by the current CLI configuration. */ 14 | // env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION }, 15 | 16 | /* Uncomment the next line if you know exactly what Account and Region you 17 | * want to deploy the stack to. */ 18 | // env: { account: '123456789012', region: 'us-east-1' }, 19 | 20 | /* For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html */ 21 | }); -------------------------------------------------------------------------------- /cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "npx ts-node --prefer-ts-exts bin/cognito_identity_pool_sample.ts", 3 | "watch": { 4 | "include": [ 5 | "**" 6 | ], 7 | "exclude": [ 8 | "README.md", 9 | "cdk*.json", 10 | "**/*.d.ts", 11 | "**/*.js", 12 | "tsconfig.json", 13 | "package*.json", 14 | "yarn.lock", 15 | "node_modules", 16 | "test" 17 | ] 18 | }, 19 | "context": { 20 | "@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": true, 21 | "@aws-cdk/core:stackRelativeExports": true, 22 | "@aws-cdk/aws-rds:lowercaseDbIdentifier": true, 23 | "@aws-cdk/aws-lambda:recognizeVersionProps": true, 24 | "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true, 25 | "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, 26 | "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, 27 | "@aws-cdk/core:target-partitions": [ 28 | "aws", 29 | "aws-cn" 30 | ] 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testEnvironment: 'node', 3 | roots: ['/test'], 4 | testMatch: ['**/*.test.ts'], 5 | transform: { 6 | '^.+\\.tsx?$': 'ts-jest' 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /lib/cognito_identity_pool_sample-stack.ts: -------------------------------------------------------------------------------- 1 | import { Stack, StackProps, CfnOutput } from 'aws-cdk-lib'; 2 | import { Construct } from 'constructs'; 3 | 4 | import * as lambda from 'aws-cdk-lib/aws-lambda'; 5 | import * as cognito from 'aws-cdk-lib/aws-cognito'; 6 | import * as apigw from 'aws-cdk-lib/aws-apigateway'; 7 | import * as iam from 'aws-cdk-lib/aws-iam'; 8 | import * as s3 from 'aws-cdk-lib/aws-s3'; 9 | import * as s3Deploy from 'aws-cdk-lib/aws-s3-deployment'; 10 | import * as customResources from 'aws-cdk-lib/custom-resources'; 11 | 12 | export class CognitoIdentityPoolSampleStack extends Stack { 13 | constructor(scope: Construct, id: string, props?: StackProps) { 14 | super(scope, id, props); 15 | 16 | // Create a pre-token-generation lambda function which adds the 'department: Engineering' claim to user-tokens 17 | const preTokenLambda = new lambda.Function(this, "PreTokenHandler", { 18 | runtime: lambda.Runtime.NODEJS_14_X, 19 | code: lambda.Code.fromAsset("resources"), 20 | handler: "pre-token-trigger.handler", 21 | }); 22 | 23 | // Create a Cognito UserPool for authentication, attach the lambdaTrigger created above 24 | const userPool = new cognito.UserPool(this, "CognitoUserPool", { 25 | userPoolName: "AnyCompany-UserPool", 26 | selfSignUpEnabled: true, 27 | signInCaseSensitive: true, 28 | autoVerify: { 29 | email: true 30 | }, 31 | standardAttributes: { 32 | email: { 33 | required: true, 34 | mutable: false 35 | } 36 | }, 37 | userVerification: { 38 | emailSubject: "Verify your email", 39 | emailBody: "Thanks for signing up. Your verification code is {####}", 40 | emailStyle: cognito.VerificationEmailStyle.CODE 41 | }, 42 | lambdaTriggers: { 43 | preTokenGeneration: preTokenLambda 44 | }, 45 | mfa: cognito.Mfa.OPTIONAL, 46 | mfaSecondFactor: { 47 | otp: true, 48 | sms: false 49 | } 50 | }) 51 | 52 | // Define a Resource Server for the User Pool 53 | const resourceServerScope = new cognito.ResourceServerScope({scopeDescription: "Get all pets", scopeName: "read"}) 54 | const resourceServer = new cognito.UserPoolResourceServer(this, "ResourceServer", { 55 | userPool: userPool, 56 | userPoolResourceServerName: "anycompanyAPI", 57 | identifier: "anycompany", 58 | scopes: [resourceServerScope] 59 | }) 60 | 61 | // Create an App client for the User Pool 62 | // Using localhost for callback + logout for testing purposes 63 | const userPoolClient = new cognito.UserPoolClient(this, "UserPoolClient", { 64 | userPool: userPool, 65 | generateSecret: false, 66 | oAuth: { 67 | flows: { 68 | authorizationCodeGrant: true, 69 | implicitCodeGrant: true 70 | }, 71 | scopes: [ 72 | cognito.OAuthScope.OPENID, 73 | cognito.OAuthScope.PROFILE, 74 | cognito.OAuthScope.resourceServer(resourceServer, resourceServerScope) 75 | ], 76 | callbackUrls: ["http://localhost"], 77 | logoutUrls: ["http://localhost"] 78 | } 79 | }) 80 | 81 | // Get the UNIX timestamp in ms to ensure uniqueness in names 82 | const timestamp: string = String(new Date().getTime()) 83 | 84 | // Create a domain for OAuth2 communication from the application <-> Cognito 85 | userPool.addDomain("CognitoDomain", { cognitoDomain: { domainPrefix: `anycompany-domain-${timestamp}` }}) 86 | 87 | // Create a Cognito Authorizer for the sample API 88 | const auth = new apigw.CognitoUserPoolsAuthorizer(this, 'petsAuthorizer', { 89 | cognitoUserPools: [userPool] 90 | }); 91 | 92 | // Create a sample Pets API. 93 | const api = new apigw.RestApi(this, 'PetsApiGW', { 94 | description: 'Example api gateway', 95 | deployOptions: { 96 | stageName: 'Prod', 97 | }, 98 | defaultCorsPreflightOptions: { 99 | allowHeaders: [ 100 | 'Content-Type', 101 | 'X-Amz-Date', 102 | 'Authorization' 103 | ], 104 | allowMethods: ['OPTIONS', 'GET'], 105 | allowCredentials: true, 106 | allowOrigins: apigw.Cors.ALL_ORIGINS 107 | }, 108 | }); 109 | 110 | const pets = api.root.addResource('pets') 111 | 112 | // Sample integration which returns some sample Pets data. This is used if deploying the sample application through the console too. 113 | const httpIntegration = new apigw.HttpIntegration('http://petstore.execute-api.eu-west-2.amazonaws.com/petstore/pets', { 114 | httpMethod: "GET", 115 | options: { 116 | passthroughBehavior: apigw.PassthroughBehavior.WHEN_NO_MATCH, 117 | integrationResponses: [{ 118 | statusCode: "200", 119 | responseParameters: { 120 | "method.response.header.Access-Control-Allow-Origin": "'*'", 121 | } 122 | }] 123 | }, 124 | proxy: false 125 | }) 126 | 127 | // Create a GET for the sample API, attach the Cognito Authorizer 128 | pets.addMethod('GET', httpIntegration, { 129 | authorizer: auth, 130 | authorizationType: apigw.AuthorizationType.COGNITO, 131 | methodResponses: [{ 132 | statusCode: '200', 133 | responseParameters: { 134 | 'method.response.header.Access-Control-Allow-Origin': true 135 | } 136 | }] 137 | }); 138 | 139 | const s3CorsRule: s3.CorsRule = { 140 | allowedMethods: [s3.HttpMethods.GET], 141 | allowedOrigins: ["*"], 142 | allowedHeaders: ["*"], 143 | exposedHeaders: [] 144 | } 145 | 146 | // Create the demo S3 bucket 147 | const s3Bucket = new s3.Bucket(this, "S3Bucket", { 148 | bucketName: `anycompany-bucket-${timestamp}`, 149 | cors: [ s3CorsRule ] 150 | }) 151 | 152 | // Deploy sample .zip file to Engineering prefix 153 | new s3Deploy.BucketDeployment(this, "DeployEngineeringTestFile", { 154 | sources: [ 155 | s3Deploy.Source.asset('resources/testfile-engineering') 156 | ], 157 | destinationBucket: s3Bucket, 158 | destinationKeyPrefix: "Engineering" 159 | }) 160 | 161 | // Deploy sample .zip file to Legal prefix 162 | new s3Deploy.BucketDeployment(this, "DeployLegalTestFile", { 163 | sources: [ 164 | s3Deploy.Source.asset('resources/testfile-legal') 165 | ], 166 | destinationBucket: s3Bucket, 167 | destinationKeyPrefix: "Legal" 168 | }) 169 | 170 | // Create ABAC policy to allow access to S3 resources by matching prefix to department in Principal Tag (which come from the user token) 171 | const s3AccessStatement = new iam.PolicyStatement({ 172 | resources: [s3Bucket.bucketArn], 173 | actions: [ 174 | 's3:List*' 175 | ], 176 | conditions: { 177 | 'StringEquals': { 178 | 's3:prefix': '${aws:PrincipalTag/department}' 179 | } 180 | }, 181 | effect: iam.Effect.ALLOW 182 | }) 183 | 184 | const authPolicyDocument = new iam.PolicyDocument({ 185 | statements: [ 186 | s3AccessStatement 187 | ] 188 | }) 189 | 190 | const cognitoIdentityProviderProperty: cognito.CfnIdentityPool.CognitoIdentityProviderProperty = { 191 | clientId: userPoolClient.userPoolClientId, 192 | providerName: userPool.userPoolProviderName, 193 | }; 194 | 195 | // Create the Identity Pool 196 | const identityPool = new cognito.CfnIdentityPool(this, "IdentityPool", { 197 | identityPoolName: "AnyCompanyIdentityPool", 198 | allowUnauthenticatedIdentities: false, 199 | cognitoIdentityProviders: [cognitoIdentityProviderProperty] 200 | }) 201 | 202 | const authPolicyProperty: iam.CfnRole.PolicyProperty = { 203 | policyDocument: authPolicyDocument, 204 | policyName: "AuthRoleAccessPolicy" 205 | } 206 | 207 | // Create an IAM role for authenticated users, attach ABAC policy to role 208 | const authRole = new iam.CfnRole(this, "CognitoAuthRole", { 209 | roleName: "CognitoIdentityPoolRole-Authorized", 210 | assumeRolePolicyDocument: { 211 | 'Statement': [ 212 | { 213 | 'Effect': iam.Effect.ALLOW, 214 | 'Action': ['sts:AssumeRoleWithWebIdentity', 'sts:TagSession'], 215 | 'Condition': { 216 | 'StringEquals': { 217 | 'cognito-identity.amazonaws.com:aud': identityPool.ref, 218 | }, 219 | 'ForAnyValue:StringLike': { 220 | 'cognito-identity.amazonaws.com:amr': 'authenticated', 221 | }, 222 | }, 223 | 'Principal': { 224 | 'Federated': 'cognito-identity.amazonaws.com' 225 | } 226 | } 227 | ] 228 | }, 229 | policies: [ authPolicyProperty ] 230 | }) 231 | 232 | new cognito.CfnIdentityPoolRoleAttachment(this, "defaultRoles", { 233 | identityPoolId: identityPool.ref, 234 | roles: { 235 | 'authenticated': authRole.attrArn 236 | } 237 | }) 238 | 239 | const createParameters = { 240 | "IdentityPoolId": identityPool.ref, 241 | "IdentityProviderName": userPool.userPoolProviderName, 242 | "PrincipalTags": { 243 | "department": "department" 244 | }, 245 | "UseDefaults": false 246 | } 247 | 248 | const setPrincipalTagAction = { 249 | action: "setPrincipalTagAttributeMap", 250 | service: "CognitoIdentity", 251 | parameters: createParameters, 252 | physicalResourceId: customResources.PhysicalResourceId.of(identityPool.ref) 253 | } 254 | 255 | const { region, account } = Stack.of(this) 256 | const identityPoolArn = `arn:aws:cognito-identity:${region}:${account}:identitypool/${identityPool.ref}` 257 | 258 | // Creates a Custom resource (https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.custom_resources-readme.html) 259 | // This is necessary to attach Principal Tag mappings to the Identity Pool after it has been created. 260 | // This uses the SDK, rather than CDK code, as attaching Principal Tags through CDK is currently not supported yet 261 | new customResources.AwsCustomResource(this, 'CustomResourcePrincipalTags', { 262 | onCreate: setPrincipalTagAction, 263 | onUpdate: setPrincipalTagAction, 264 | policy: customResources.AwsCustomResourcePolicy.fromSdkCalls({ 265 | resources: [identityPoolArn], 266 | }), 267 | }) 268 | 269 | new CfnOutput(this, 'BucketName', { value: s3Bucket.bucketName }) 270 | new CfnOutput(this, 'ApiGatewayUrl', { value: `https://${api.restApiId}.execute-api.${region}.amazonaws.com/Prod/pets` }) 271 | new CfnOutput(this, 'UserPoolId', { value: userPool.userPoolId }) 272 | new CfnOutput(this, 'ClientId', { value: userPoolClient.userPoolClientId }) 273 | new CfnOutput(this, 'IdentityPoolId', { value: identityPool.ref }) 274 | new CfnOutput(this, 'Region', { value: region }) 275 | } 276 | } 277 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cognito_identity_pool_sample", 3 | "version": "0.1.0", 4 | "bin": { 5 | "cognito_identity_pool_sample": "bin/cognito_identity_pool_sample.js" 6 | }, 7 | "scripts": { 8 | "build": "tsc", 9 | "watch": "tsc -w", 10 | "test": "jest", 11 | "cdk": "cdk" 12 | }, 13 | "devDependencies": { 14 | "@types/jest": "^26.0.10", 15 | "@types/node": "10.17.27", 16 | "jest": "^26.4.2", 17 | "ts-jest": "^26.2.0", 18 | "aws-cdk": "^2.17.0", 19 | "ts-node": "^9.0.0", 20 | "typescript": "~3.9.7" 21 | }, 22 | "dependencies": { 23 | "aws-cdk-lib": "^2.17.0", 24 | "constructs": "^10.0.0", 25 | "source-map-support": "^0.5.16" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /react-sample/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /react-sample/README.md: -------------------------------------------------------------------------------- 1 | # Getting Started with Create React App 2 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 3 | 4 | ## Available Scripts 5 | 6 | In the project directory, you can run: 7 | 8 | ### `npm start` 9 | 10 | Runs the app in the development mode.\ 11 | Open [http://localhost:3000](http://localhost:3000) to view it in your browser. 12 | 13 | The page will reload when you make changes.\ 14 | You may also see any lint errors in the console. 15 | 16 | ### `npm test` 17 | 18 | Launches the test runner in the interactive watch mode.\ 19 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 20 | 21 | ### `npm run build` 22 | 23 | Builds the app for production to the `build` folder.\ 24 | It correctly bundles React in production mode and optimizes the build for the best performance. 25 | 26 | The build is minified and the filenames include the hashes.\ 27 | Your app is ready to be deployed! 28 | 29 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 30 | 31 | ### `npm run eject` 32 | 33 | **Note: this is a one-way operation. Once you `eject`, you can't go back!** 34 | 35 | If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 36 | 37 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own. 38 | 39 | You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it. 40 | 41 | ## Learn More 42 | 43 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 44 | 45 | To learn React, check out the [React documentation](https://reactjs.org/). 46 | 47 | ### Code Splitting 48 | 49 | This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting) 50 | 51 | ### Analyzing the Bundle Size 52 | 53 | This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size) 54 | 55 | ### Making a Progressive Web App 56 | 57 | This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app) 58 | 59 | ### Advanced Configuration 60 | 61 | This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration) 62 | 63 | ### Deployment 64 | 65 | This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment) 66 | 67 | ### `npm run build` fails to minify 68 | 69 | This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify) 70 | 71 | 72 | ## Security 73 | 74 | See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information. 75 | 76 | ## License 77 | 78 | This library is licensed under the MIT-0 License. See the LICENSE file. -------------------------------------------------------------------------------- /react-sample/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sampleapp", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.16.2", 7 | "@testing-library/react": "^12.1.3", 8 | "@testing-library/user-event": "^13.5.0", 9 | "amazon-cognito-identity-js": "^5.2.7", 10 | "aws-sdk": "^2.1354.0", 11 | "axios": "^0.26.0", 12 | "cross-fetch": "^3.1.5", 13 | "react": "^17.0.2", 14 | "react-bootstrap": "^2.2.0", 15 | "bootstrap": "^5.1.3", 16 | "react-dom": "^17.0.2", 17 | "react-json-pretty": "^2.2.0", 18 | "util": "^0.12.4", 19 | "web-vitals": "^2.1.4" 20 | }, 21 | "devDependencies": { 22 | "react-scripts": "^5.0.1", 23 | "@svgr/webpack": "^6.2.1", 24 | "nth-check": "^2.1.1" 25 | }, 26 | "overrides": { 27 | "@svgr/webpack": "$@svgr/webpack", 28 | "nth-check": "$nth-check" 29 | }, 30 | "scripts": { 31 | "start": "react-scripts start", 32 | "build": "react-scripts build", 33 | "test": "react-scripts test", 34 | "eject": "react-scripts eject" 35 | }, 36 | "eslintConfig": { 37 | "extends": [ 38 | "react-app", 39 | "react-app/jest" 40 | ] 41 | }, 42 | "browserslist": { 43 | "production": [ 44 | ">0.2%", 45 | "not dead", 46 | "not op_mini all" 47 | ], 48 | "development": [ 49 | "last 1 chrome version", 50 | "last 1 firefox version", 51 | "last 1 safari version" 52 | ] 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /react-sample/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 15 | 16 | React App 17 | 18 | 19 | 20 |
21 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /react-sample/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [], 5 | "start_url": ".", 6 | "display": "standalone", 7 | "theme_color": "#000000", 8 | "background_color": "#ffffff" 9 | } 10 | -------------------------------------------------------------------------------- /react-sample/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /react-sample/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-header { 6 | background-color: #282c34; 7 | min-height: 100vh; 8 | display: flex; 9 | flex-direction: column; 10 | align-items: center; 11 | justify-content: center; 12 | font-size: calc(10px + 2vmin); 13 | color: white; 14 | } 15 | 16 | .App-link { 17 | color: #61dafb; 18 | } 19 | -------------------------------------------------------------------------------- /react-sample/src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import 'cross-fetch/polyfill'; 4 | import * as AmazonCognitoIdentity from 'amazon-cognito-identity-js' 5 | import './App.css'; 6 | 7 | import Tabs from 'react-bootstrap/Tabs'; 8 | import Tab from 'react-bootstrap/Tab' 9 | import SignUp from './components/SignUp'; 10 | import SignIn from './components/SignIn'; 11 | import Home from './components/base-components/Home' 12 | import ApiAccess from './components/ApiAccess'; 13 | import S3Access from './components/S3Access'; 14 | 15 | const config = require('./configuration.json') 16 | 17 | class App extends React.Component { 18 | constructor(props) { 19 | super(props) 20 | 21 | this.state = { 22 | cognitoUser: null 23 | } 24 | 25 | this.poolData = { 26 | UserPoolId: config.userPoolId, 27 | ClientId: config.clientId, 28 | }; 29 | 30 | this.userPool = new AmazonCognitoIdentity.CognitoUserPool(this.poolData); 31 | } 32 | 33 | setCognitoUser = (user) => { 34 | this.setState({ cognitoUser: user }) 35 | } 36 | 37 | render() { 38 | return ( 39 |
40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 |
58 | ) 59 | } 60 | } 61 | 62 | export default App; 63 | -------------------------------------------------------------------------------- /react-sample/src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /react-sample/src/components/ApiAccess.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import axios from 'axios'; 3 | import JSONPretty from 'react-json-pretty'; 4 | 5 | import Button from 'react-bootstrap/Button' 6 | import Container from 'react-bootstrap/esm/Container'; 7 | 8 | const config = require('../configuration.json') 9 | 10 | class ApiAccess extends React.Component { 11 | constructor(props) { 12 | super(props) 13 | this.state = { 14 | api_result: "" 15 | } 16 | } 17 | 18 | /** 19 | * Call protected APIGW endpoint 20 | * 21 | * Important: 22 | * Make sure apigw cognito authorizer configuration is complete 23 | * Make sure api accepts id-token (no oauth scope defined in authorization) 24 | * You can only use id-token since custom scopes are not supported when sdk is used 25 | */ 26 | callAPIGW = () => { 27 | 28 | const apiGatewayUrl = config.apiGatewayUrl; 29 | 30 | // set ID Token in "Authorization" header 31 | const headers = { 32 | 'Content-Type': 'application/json', 33 | 'Authorization': this.props.cognitoUser.signInUserSession.idToken.jwtToken 34 | } 35 | 36 | axios.get(apiGatewayUrl, { headers: headers }).then((response) => { 37 | this.setState({api_result: JSON.stringify(response.data,null, 2)}); 38 | }).catch(function (error) { 39 | console.error(error); 40 | }); 41 | } 42 | 43 | render() { 44 | if (!this.props.cognitoUser) { return
Not logged in
} 45 | return ( 46 |
47 | 48 | 49 | 50 | { 51 | this.state.api_result && 52 | } 53 |
54 | ) 55 | } 56 | } 57 | 58 | export default ApiAccess -------------------------------------------------------------------------------- /react-sample/src/components/S3Access.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AWS from 'aws-sdk'; 3 | 4 | import JSONPretty from 'react-json-pretty'; 5 | 6 | import Form from 'react-bootstrap/Form' 7 | import Button from 'react-bootstrap/Button' 8 | 9 | import Container from 'react-bootstrap/esm/Container'; 10 | 11 | const config = require('../configuration.json') 12 | 13 | class S3Access extends React.Component { 14 | constructor(props) { 15 | super(props) 16 | this.state = { 17 | bucketName: config.bucketName, 18 | prefix: "", 19 | s3ListResults: null, 20 | errorCode: null, 21 | accessKeyId: "", 22 | secretAccessKey: "", 23 | sessionToken: "" 24 | } 25 | } 26 | 27 | /** 28 | * List files in S3 bucket 29 | * 30 | * 1. Identity pool created and configured to use user pool as idp 31 | * 2. Permissions defined on the iam role to allow s3 list 32 | * 3. Bucket created with proper x-origin policy to allow calls 33 | */ 34 | getS3Data = (event) => { 35 | event.preventDefault(); 36 | 37 | const cognitoUserpool = `cognito-idp.${config.region}.amazonaws.com/${config.userPoolId}` 38 | const identityPoolId = config.identityPoolId 39 | const identityToken = this.props.cognitoUser.signInUserSession.idToken.jwtToken 40 | 41 | AWS.config.region = config.region; 42 | AWS.config.credentials = new AWS.CognitoIdentityCredentials({ 43 | IdentityPoolId: identityPoolId, 44 | Logins: { 45 | [cognitoUserpool]: identityToken 46 | } 47 | }); 48 | 49 | // Make the call to obtain credentials 50 | AWS.config.credentials.get(() => { 51 | 52 | // Credentials will be available when this function is called. 53 | var accessKeyId = AWS.config.credentials.accessKeyId; 54 | var secretAccessKey = AWS.config.credentials.secretAccessKey; 55 | var sessionToken = AWS.config.credentials.sessionToken; 56 | 57 | var s3 = new AWS.S3(); 58 | var params = { 59 | Bucket: this.state.bucketName, 60 | Prefix: this.state.prefix 61 | }; 62 | 63 | s3.listObjects(params, (err, data) => { 64 | let error_code, s3_results 65 | if (err) { 66 | error_code = err.statusCode 67 | } 68 | else { 69 | s3_results = JSON.stringify(data.Contents,['Key'], 2) 70 | } 71 | 72 | this.setState({ 73 | errorCode: error_code, 74 | s3ListResults: s3_results, 75 | accessKeyId: accessKeyId, 76 | secretAccessKey: secretAccessKey, 77 | sessionToken: sessionToken 78 | }) 79 | }); 80 | }); 81 | } 82 | 83 | render() { 84 | if (!this.props.cognitoUser) { 85 | return
Not logged in
86 | } 87 | 88 | const unauthorized = this.state.errorCode && this.state.errorCode === 403 89 | 90 | return ( 91 |
92 | 93 |
94 | 95 | Bucket name 96 | this.setState({bucketName: e.target.value})} 101 | /> 102 | 103 | 104 | Prefix 105 | this.setState({prefix: e.target.value})} 109 | /> 110 | 111 | 112 | 115 |
116 |
117 | 118 | { 119 | unauthorized && Unauthorized 120 | } 121 | 122 |

Access Key Id: {this.state.accessKeyId}

123 |

Secret Access Key: {this.state.secretAccessKey}

124 |

Session Token: {this.state.sessionToken}

125 |
126 |
127 | ) 128 | } 129 | } 130 | 131 | export default S3Access 132 | -------------------------------------------------------------------------------- /react-sample/src/components/SignIn.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import JSONPretty from 'react-json-pretty'; 3 | 4 | import * as AmazonCognitoIdentity from 'amazon-cognito-identity-js' 5 | 6 | import Form from 'react-bootstrap/Form' 7 | import Button from 'react-bootstrap/Button' 8 | import Container from 'react-bootstrap/esm/Container'; 9 | 10 | class SignIn extends React.Component { 11 | constructor(props) { 12 | super(props) 13 | this.state = { 14 | username: "", 15 | password: "", 16 | idToken: "", 17 | accessToken: "", 18 | } 19 | } 20 | 21 | validateForm = () => { 22 | return this.state.username.length > 0 && this.state.password.length > 0; 23 | } 24 | 25 | parseJwt = (token) => { 26 | var base64Url = token.split('.')[1]; 27 | var base64 = base64Url.replace('-', '+').replace('_', '/'); 28 | return JSON.parse(window.atob(base64)); 29 | } 30 | 31 | login = (event) => { 32 | event.preventDefault(); 33 | const { username, password } = this.state 34 | 35 | var authenticationDetails = new AmazonCognitoIdentity.AuthenticationDetails({ 36 | Username: username, 37 | Password: password 38 | }); 39 | 40 | console.log("--------Authenticate --- "+username) 41 | 42 | const cognitoUser = new AmazonCognitoIdentity.CognitoUser({ 43 | Username: username, 44 | Pool: this.props.userPool, 45 | }) 46 | 47 | cognitoUser.authenticateUser(authenticationDetails, { 48 | onSuccess: function(result) { 49 | var idToken = result.getIdToken().getJwtToken(); 50 | var accessToken = result.getAccessToken().getJwtToken(); 51 | this.setState({ 52 | idToken: JSON.stringify(this.parseJwt(idToken)), 53 | accessToken: JSON.stringify(this.parseJwt(accessToken)) 54 | }) 55 | this.props.setUser(cognitoUser) 56 | }.bind(this), 57 | 58 | onFailure: function(err) { 59 | alert(err.message || JSON.stringify(err)); 60 | } 61 | }); 62 | } 63 | 64 | render() { 65 | const showButton = this.validateForm() 66 | return ( 67 |
68 | 69 |
70 | 71 | Username 72 | this.setState({username: e.target.value})} 77 | /> 78 | 79 | 80 | Password 81 | this.setState({password: e.target.value})} 85 | /> 86 | 87 | 88 | 91 |
92 |
93 | { this.state.idToken && this.state.accessToken && 94 | 95 |

ID Token

96 | 97 |

Access Token

98 | 99 |
100 | } 101 |
102 | ) 103 | } 104 | } 105 | 106 | export default SignIn -------------------------------------------------------------------------------- /react-sample/src/components/SignUp.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Form from 'react-bootstrap/Form' 3 | import Button from 'react-bootstrap/Button' 4 | import Container from 'react-bootstrap/esm/Container'; 5 | 6 | class SignUp extends React.Component { 7 | render() { 8 | return ( 9 |
10 | 11 |
12 | 13 | Name 14 | 15 | 16 | 17 | Username 18 | 19 | 20 | 21 | Email 22 | 23 | 24 | 25 | Phone 26 | 27 | 28 | 29 | 30 | Password 31 | 32 | 33 | 34 | 37 |
38 |
39 |
40 | ) 41 | } 42 | } 43 | 44 | export default SignUp -------------------------------------------------------------------------------- /react-sample/src/components/base-components/Home.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | class Home extends React.Component { 4 | render() { 5 | return ( 6 |
7 |

This is a sample react app

8 |
9 | ) 10 | } 11 | } 12 | 13 | export default Home -------------------------------------------------------------------------------- /react-sample/src/components/base-components/NavBar.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Navbar from 'react-bootstrap/Navbar' 3 | import Container from 'react-bootstrap/Container' 4 | import Nav from 'react-bootstrap/Nav' 5 | 6 | class NavBar extends React.Component { 7 | render() { 8 | return ( 9 | 10 | 11 | Sample React App 12 | 13 | 14 | 20 | 21 | 22 | 23 | ) 24 | } 25 | } 26 | 27 | export default NavBar -------------------------------------------------------------------------------- /react-sample/src/configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "bucketName": "", 3 | "apiGatewayUrl": "", 4 | "clientId": "", 5 | "identityPoolId": "", 6 | "region": "", 7 | "userPoolId": "" 8 | } 9 | -------------------------------------------------------------------------------- /react-sample/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /react-sample/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import reportWebVitals from './reportWebVitals'; 6 | 7 | import 'bootstrap/dist/css/bootstrap.min.css' 8 | 9 | ReactDOM.render( 10 | 11 | 12 | , 13 | document.getElementById('root') 14 | ); 15 | 16 | // If you want to start measuring performance in your app, pass a function 17 | // to log results (for example: reportWebVitals(console.log)) 18 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 19 | reportWebVitals(); 20 | -------------------------------------------------------------------------------- /react-sample/src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /react-sample/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /react-sample/webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // Required to fix the following error: 3 | // Module not found: Error: Can't resolve 'util' in '/sampleapp/node_modules/aws-sdk/lib' 4 | // Reference: https://github.com/aws/aws-sdk-js/issues/3501 5 | resolve: { 6 | fallback: { 7 | "util": require.resolve("util/"), 8 | }, 9 | } 10 | } -------------------------------------------------------------------------------- /resources/pictures/api-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-cognito-abac-authorization-with-react-example/2659a737709fde5cbc291fb769074ae041b947f6/resources/pictures/api-example.png -------------------------------------------------------------------------------- /resources/pictures/architecture-diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-cognito-abac-authorization-with-react-example/2659a737709fde5cbc291fb769074ae041b947f6/resources/pictures/architecture-diagram.png -------------------------------------------------------------------------------- /resources/pictures/cdk-outputs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-cognito-abac-authorization-with-react-example/2659a737709fde5cbc291fb769074ae041b947f6/resources/pictures/cdk-outputs.png -------------------------------------------------------------------------------- /resources/pictures/config-file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-cognito-abac-authorization-with-react-example/2659a737709fde5cbc291fb769074ae041b947f6/resources/pictures/config-file.png -------------------------------------------------------------------------------- /resources/pictures/demo-login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-cognito-abac-authorization-with-react-example/2659a737709fde5cbc291fb769074ae041b947f6/resources/pictures/demo-login.png -------------------------------------------------------------------------------- /resources/pictures/engineering-s3-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-cognito-abac-authorization-with-react-example/2659a737709fde5cbc291fb769074ae041b947f6/resources/pictures/engineering-s3-example.png -------------------------------------------------------------------------------- /resources/pictures/legal-s3-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-cognito-abac-authorization-with-react-example/2659a737709fde5cbc291fb769074ae041b947f6/resources/pictures/legal-s3-example.png -------------------------------------------------------------------------------- /resources/pre-token-trigger.js: -------------------------------------------------------------------------------- 1 | exports.handler = (event, context, callback) => { 2 | event.response = { 3 | claimsOverrideDetails: { 4 | claimsToAddOrOverride: { 5 | department: 'Engineering', 6 | }, 7 | }, 8 | }; 9 | 10 | callback(null, event); 11 | }; 12 | -------------------------------------------------------------------------------- /resources/testfile-engineering/testfile-engineering.txt: -------------------------------------------------------------------------------- 1 | This is a test file for the engineering bucket -------------------------------------------------------------------------------- /resources/testfile-legal/testfile-legal.txt: -------------------------------------------------------------------------------- 1 | This is a test file for the legal bucket -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2018", 4 | "module": "commonjs", 5 | "lib": [ 6 | "es2018" 7 | ], 8 | "declaration": true, 9 | "strict": true, 10 | "noImplicitAny": true, 11 | "strictNullChecks": true, 12 | "noImplicitThis": true, 13 | "alwaysStrict": true, 14 | "noUnusedLocals": false, 15 | "noUnusedParameters": false, 16 | "noImplicitReturns": true, 17 | "noFallthroughCasesInSwitch": false, 18 | "inlineSourceMap": true, 19 | "inlineSources": true, 20 | "experimentalDecorators": true, 21 | "strictPropertyInitialization": false, 22 | "typeRoots": [ 23 | "./node_modules/@types" 24 | ] 25 | }, 26 | "exclude": [ 27 | "node_modules", 28 | "cdk.out" 29 | ] 30 | } 31 | --------------------------------------------------------------------------------