Access the S3 folder specific to particular user authenticated using Cognito

1

Hi,

I have been trying to getObjects from S3 using token generated from cognito authentication based on username. Basically 1 bucket for all the users but each user will have access to only their folder's objects.

I already come across the policy for identity pool and its already added to the identity pool role.

What I'm confused now is whether to use lambda to pull in the object or AWS SDK for js on client side or API gateway proxy to S3.

Any recommendations on how to achieve what I want? Or any other method?

We are using next JS , React JS and CDK for api gateway and lambda.

Any help really appreciated, Thanks

1 Answer
5
Accepted Answer

Hello,

I understand that you would like to know how to leverage Cognito Identity Pool to access AWS services like S3 to use GetObject [3] or PutObject [4] functionality.

Below I am providing you with a lab I created where I leverage the AWS APIs in AWS CLI calls directly. You can choose to use any SDKs mentioned on the AWS API page (for example - AWS SDK for .NET, AWS SDK for Java V2, AWS SDK for JavaScript etc) to meet your use case for the same in your Lambda code.


Step 1: Create Cognito User Pool - https://docs.aws.amazon.com/cognito/latest/developerguide/tutorial-create-user-pool.html

Step 2: Create an App Client without Client secret - https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-settings-client-apps.html

Step 3: Configure App Client Settings - https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-app-settings.html

Step 4: Setup Cognito Domain - https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-assign-domain-prefix.html#cognito-user-pools-assign-domain-prefix-step-1

Step 5: Create Identity Pool - https://docs.aws.amazon.com/cognito/latest/developerguide/tutorial-create-identity-pool.html

Step 6: Add User Pool as authentication provider in Identity pool - https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-integrating-user-pools-with-identity-pools.html#amazon-cognito-integrating-user-pools-with-identity-pools-configuring

Under Authentication providers, specify the User Pool ID and App Client ID. Users in this User Pool will now be associated with the Identity Pool and can assume IAM roles for authenticated users. 

Step 7: After this, you should be able to see 2 roles attached to Identity Pool. We will need to edit IAM policy of authenticated role as below:

	{
		"Version":"2012-10-17",
		"Statement":[
			{
				"Effect":"Allow",
				"Action":[
					"s3:ListBucket"
				],
				"Resource":[
					"arn:aws:s3:::bucket_name_here"
				],
				"Condition":{
					"StringLike":{
						"s3:prefix":[
							"private-resources/"
						]
					}
				}
			},
			{
				"Effect":"Allow",
				"Action":[
					"s3:GetObject",
					"s3:PutObject",
					"s3:DeleteObject"
				],
				"Resource":[
					"arn:aws:s3:::bucket_name_here/private-resources/${cognito-identity.amazonaws.com:sub}",
					"arn:aws:s3:::bucket_name_here/private-resources/${cognito-identity.amazonaws.com:sub}/*"
				]
			}
		]
	}
  • Every authenticated user in an Identity Pool is assigned a unique Cognito Identity ID. The Identity ID can be accessed in an IAM policy as an IAM variable. ${cognito-identity.amazonaws.com:sub} represents the unique Identity ID assigned to an authenticated user in the Identity Pool. The above policy allows a user say User1 to put data and files in a folder named with their Cognito Identity ID. For instance, if User1 has Cognito Identity ID - us-east-1:7cbea601-658a-4988-0000-000000000000, they can upload, read and delete files in the folder with prefix private-resources/us-east-1:7cbea601-658a-4988-0000-000000000000. Other users cannot access any files under this folder which is named after User1's Identity ID. Note here, for convenience I have created a folder private-resources under which all users would have their private folders. You can omit this and have all private folders directly in the bucket.

Note - You can also restrict your ListBucket action with prefix such as -> "private-resources/${cognito-identity.amazonaws.com:sub}"

Step 8: Setup is complete now. First thing will be to generate ID token of the User in User Pool. You can go to Hosted UI and sign in and you will receive token.

Step 9: Once you get ID token, we Pass the ID token as part of the Logins Map in the get-id call to the Identity pool. The get-id call will return the unique Identity ID for the user. More details regarding the get-id API call and structure of the Logins Map can be found at the following link - >> https://docs.aws.amazon.com/cognitoidentity/latest/APIReference/API_GetId.html

CLI example ->

$ aws cognito-identity get-id --identity-pool-id "Identity_Pool_ID" --logins "cognito-idp.region.amazonaws.com/Your_UserPool_ID=ID_Token" --region us-east-1

Step 10: You will receive unique Identity ID of user in this format - us-east-x:c9b735c9-72b9-461b-8851-897066xxxxxx

Step 11: Pass the unique Identity ID of the user in the get-credentials-for-identity API call to obtain temporary AWS credentials.

https://docs.aws.amazon.com/cognitoidentity/latest/APIReference/API_GetCredentialsForIdentity.html

$ aws cognito-identity get-credentials-for-identity --identity-id us-east-x:c9111119-7111-4123-8111-81116xxxxxx --logins "cognito-idp.region.amazonaws.com/Your_UserPool_ID=ID_Token" --region us-east-1

Step 12: Once you get temporary credentials, Set environment variables for the CLI with the obtained temporary security credentials - https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html

Step 13: Test by making calls for user. Each cognito user can only access their own folder. For instance, if User1 has Cognito Identity ID - us-east-1:7cbea601-658a-4988-0000-000000000000, they can upload, read and delete files in the folder with prefix private-resources/us-east-1:7cbea601-658a-4988-0000-000000000000. Other users cannot access any files under this folder which is named after User1's Identity ID.:

$ aws s3api put-object --bucket <BucketName> --key <ObjectKey> --body <body> 

https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html


=========== Testing on AWS CLI ================

a. Retrieved tokens from Cognito Userpool -

  • id_token=eyJraWQ.....Df1pICAoyg
  • access_token=eyJraWQiOi....ZViI2A

b. Call GetId API with ID token in the login parameter

$ aws cognito-identity get-id --identity-pool-id "us-east-x:c7XXXXXXXXXXXXXXXXXc39" --logins "cognito-idp.us-east-x.amazonaws.com/us-east-x_SXXXXXXXX=eyJraWQ.....Df1pICAoyg" --region us-east-x

Output -

{ "IdentityId": "us-east-x:e3XXXXXXXXXXXXXXXXXXXXXXXX4" }

c. Call GetCredentialsForIdentity API to exchange token for IAM Role/AWS credentials.

$ aws cognito-identity get-credentials-for-identity --identity-id us-east-x:e3XXXXXXXXXXXXXXXXXXXXXXXX4 --logins "cognito-idp.us-east-x.amazonaws.com/us-east-x_SXXXXXXXX=eyJraWQ.....Df1pICAoyg" --region us-east-x

Output -

{ "IdentityId": "us-east-x:e3XXXXXXXXXXXXXXXXXXXXXXXX4", "Credentials": { "AccessKeyId": "ASIA2XXXXXXXXXXXXXXI", "SecretKey": "1XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXF", "SessionToken": "IQ......XXXX3p", "Expiration": "2021-09-04T09:44:38-04:00" } }

d. After configuring your credentials (AccessKeyId, SecretKey, SessionToken) you can call S3 PutObject or GetObject -

$ aws s3api put-object --bucket teXXXXXXXXXXXXXXXXXXXXXXXn --key private-resources/us-east-x:e3XXXXXXXXXXXXXXXXXXXXXXXX4/swagger --body swagger.json --profile cognitotest
{
    "ETag": "\"b124..............330\""
}

or as per your use case, you can also call the GetObject API ->

$ aws s3api get-object --bucket teXXXXXXXXXXXXXXXXXXXXXXXn --key private-resources/us-east-x:us-east-x:e3XXXXXXXXXXXXXXXXXXXXXXXX4 "helloworld" --profile cognitotest

Now in regards to query with development, please note that we have the dedicated AWS Solutions Architect team which addresses the architecture and design level queries for custom solutions. I would suggest you reach out directly to our Sales team as they can help you to get in touch with our Solutions Architect (SA) team.

You can request for a member of the sales (Business Development) team to contact you using any of the following methods:

>> Via form:    	http://aws.amazon.com/contact-us/aws-sales/
>> Via Live Chat:   https://pages.awscloud.com/live-chat-contact-us.html
>> Phone support:   +1 (833) 662-9873 - 6:30am - 4:00pm (Pacific) Monday - Friday.

In case if you have any Cognito SDK queries, you can also reach out to the Dev team on Github.

https://github.com/aws-amplify/amplify-js/tree/master/packages/amazon-cognito-identity-js


I hope the above shared lab clarified the use case of leveraging Identity Pool using the AWS API calls which can also be leveraged in either AWS CLI or in your code using SDKs. In case, if you have any questions or concerns then please feel free to reach out by creating a support case with our premium support team.


References:

[1] https://docs.aws.amazon.com/cognitoidentity/latest/APIReference/API_GetId.html

[2] https://docs.aws.amazon.com/cognitoidentity/latest/APIReference/API_GetCredentialsForIdentity.html

[3] https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObject.html

[4] https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html

profile pictureAWS
SUPPORT ENGINEER
Yash_C
answered 2 years ago
  • Thank you for the detailed guide. It reduced so much efforts.

    I have 1 more question associated with above policy. How to use listObjects in above policy as it gives policy invalid and i want to list all objects with particular prefix

  • Hello everyone, this is a great answer and very detailed. I just have one question: can I substitute ${cognito-identity.amazonaws.com:sub} with, for instance, ${cognito-identity.amazonaws.com:preferred_username} to get the user names as folder names instead of ids? would be easier for manual maintenance if needed. My cognito users all have preferred_username set. Thanks!

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.

Guidelines for Answering Questions