Configuring S3 Buckets with Permissions and Access Roles in AWS Cognito AuthRole

2023-02-15

#cloud#backend
Configuring S3 Buckets with Permissions and Access Roles in AWS Cognito AuthRole

This is a series of articles about setting up a complex Serverless backend infrastructure with AWS SAM and CloudFormation.

Here is the index of all the articles in case you want to jump to any of them:

1. Setup of AppSync and API Gateway with Multiple AWS Cognito User Pools

  1. Configuring S3 Buckets with Permissions and Access Roles in AWS Cognito AuthRole

3. Intro to DynamoDB Resolvers for AppSync Implementation

4. Intro to Lambda Resolvers for AppSync Implementation

5. Configuring an AWS VPC to Include Lambda Resolvers with a Fixed IP

6. Intro to Pipeline Resolvers for AppSync Implementation

7. Handling Lambda Resolver Timeouts with SNS Messages


You want users to read anything in the bucket but only write to their own folder. One wrong IAM policy and either everyone can write everywhere, or nobody can write at all.

The trick is Cognito's ${cognito-identity.amazonaws.com:sub} variable. It injects the user's identity ID directly into the IAM policy, scoping write permissions to a folder that matches their ID — without any custom backend logic.

The Setup

Create the bucket. Structure data so each user's files live under a folder named after their Cognito identity sub. Then wire up the IAM roles.

Resources:
  MyS3Bucket:
    Type: "AWS::S3::Bucket"

The Roles

The AuthRole gets read access to the entire bucket and write access only to the user's own folder. The UnAuthRole gets read-only access. An Identity Pool ties them together.

Resources:
  # Unauthenticated Role with write access to the user folder
  CognitoAuthRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Effect: "Allow"
            Principal:
              Service: "cognito-identity.amazonaws.com"
            Action: "sts:AssumeRoleWithWebIdentity"
      Policies:
        - PolicyName: "S3AccessPolicy"
          PolicyDocument:
            Statement:
              - Effect: "Allow"
                Action: "s3:GetObject"
                Resource: !Sub "arn:aws:s3:::${MyS3Bucket}/*"
              - Effect: "Allow"
                Action: "s3:PutObject"
                Resource: !Sub "arn:aws:s3:::${MyS3Bucket}/${cognito-identity.amazonaws.com:sub}/*"
  # Unauthenticated Role with read access to the S3 bucket
  CognitoUnAuthRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Principal:
              Federated: "cognito-identity.amazonaws.com"
            Action:
              - "sts:AssumeRoleWithWebIdentity"
            Condition:
              StringEquals:
                "cognito-identity.amazonaws.com:aud": !Ref CognitoIdentityPool
      Policies:
        - PolicyName: "S3ReadAccessPolicy"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: "Allow"
                Action: "s3:GetObject"
                Resource: !Sub "arn:aws:s3:::${MyS3Bucket}/*"

  # Cognito Identity Pool
  CognitoIdentityPool:
    Type: "AWS::Cognito::IdentityPool"
    Properties:
      IdentityPoolName: "MyIdentityPool"
      AllowUnauthenticatedIdentities: true
      CognitoIdentityProviders:
        - ClientId: !Ref UserPoolGeneralClient
          ProviderName: !GetAtt [UserPoolGeneral, ProviderName]

  # Attach roles to the Cognito Identity Pool
  SetIdentityPoolRoles:
    Type: "AWS::Cognito::IdentityPoolRoleAttachment"
    Properties:
      IdentityPoolId: !Ref CognitoIdentityPool
      Roles:
        authenticated: !GetAtt [CognitoAuthRole, Arn]
        unauthenticated: !GetAtt [CognitoUnAuthRole, Arn]

  # Assuming you have a user pool client for the general user pool
  UserPoolGeneralClient:
    Type: "AWS::Cognito::UserPoolClient"
    Properties:
      ClientName: "GeneralUserClient"
      UserPoolId: !Ref UserPoolGeneral
      GenerateSecret: false

The ${cognito-identity.amazonaws.com:sub} in the PutObject resource does all the heavy lifting. Each authenticated user can only write to their own folder. No application-level checks needed — IAM enforces it at the infrastructure layer.

Code examples are simplified to illustrate the approach. Some adjustments may be needed for production.


Next UP: Part 3. Intro to DynamoDB Resolvers for AppSync Implementation