unacorn
unacorn

Reputation: 1042

Cognito authenticated role via Lambda to GetObject - An error occurred (AccessDenied) when calling the GetObject operation: Access Denied

I have tried everything but couldn't get any clue what's wrong with my IAM policy to do with Cognito sub with identity ID access

I am using Lambda to get authentication details > get_object from a folder separated by Cognito user using boto3.

Here's my Lambda code:

import json
import urllib.parse
import boto3
import sys
import hmac, hashlib, base64

print('Loading function')

cognito = boto3.client('cognito-idp')
cognito_identity = boto3.client('cognito-identity')

def lambda_handler(event, context):
    print("Received event: " + json.dumps(event, indent=2))
    
    username = '{substitute_with_my_own_data}' //authenticated user
    app_client_id = '{substitute_with_my_own_data}' //cognito client id
    key = '{substitute_with_my_own_data}' //cognito app client secret key
    cognito_provider = 'cognito-idp.{region}.amazonaws.com/{cognito-pool-id}' 
    
    message = bytes(username+app_client_id,'utf-8')
    key = bytes(key,'utf-8')
    secret_hash = base64.b64encode(hmac.new(key, message, digestmod=hashlib.sha256).digest()).decode()
    
    print("SECRET HASH:",secret_hash)
        
    
    auth_data = { 'USERNAME': username, 'PASSWORD':'{substitute_user_password}', 'SECRET_HASH': secret_hash}
    auth_response = cognito.initiate_auth(
        AuthFlow='USER_PASSWORD_AUTH',
        AuthParameters=auth_data,
        ClientId=app_client_id
        )
    
    print(auth_response)

    # From the response that contains the assumed role, get the temporary 
    # credentials that can be used to make subsequent API calls
    auth_result=auth_response['AuthenticationResult']
    id_token=auth_result['IdToken']
    
    id_response =  cognito_identity.get_id(
        IdentityPoolId='{sub_cognito_identity_pool_id}',
        Logins={cognito_provider: id_token}
        )
    print('id_response = ' + id_response['IdentityId']) // up to this stage verified correct user cognito identity id returned
    
    credentials_response = cognito_identity.get_credentials_for_identity(
        IdentityId=id_response['IdentityId'],
        Logins={cognito_provider: id_token}
        )
        
    secretKey = credentials_response['Credentials']['SecretKey']
    accessKey = credentials_response['Credentials']['AccessKeyId']
    sessionToken = credentials_response['Credentials']['SessionToken']
    
    print('secretKey = ' + secretKey)
    print('accessKey = ' + accessKey)
    print('sessionToken = ' + sessionToken)
    
    # Use the temporary credentials that AssumeRole returns to make a 
    # connection to Amazon S3  
    s3 = boto3.client(
        's3',
        aws_access_key_id=accessKey, 
        aws_secret_access_key=secretKey, 
        aws_session_token=sessionToken,
    )
    
    # Use the Amazon S3 resource object that is now configured with the 
    # credentials to access your S3 buckets. 
    # for bucket in s3.buckets.all():
    #     print(bucket.name)
    

    # Get the object from the event and show its content type
    bucket = '{bucket-name}'
    key = 'abc/{user_cognito_identity_id}/test1.txt'
    prefix = 'abc/{user_cognito_identity_id}'
    
    try:
        response = s3.get_object(
            Bucket=bucket,
            Key=key
        )
        # response = s3.list_objects(
        #     Bucket=bucket,
        #     Prefix=prefix,
        #     Delimiter='/'
        # )
        print(response)
        return response
    except Exception as e:
        print(e)
        print('Error getting object {} from bucket {}. Make sure they exist and your bucket is in the same region as this function.'.format(key, bucket))
        raise e

What I have verified:

So it seems that there's issue with the IAM policy

IAM policy

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "s3:ListBucket"
            ],
            "Effect": "Allow",
            "Resource": [
                "arn:aws:s3:::bucket-name"
            ],
            "Condition": {
                "StringLike": {
                    "s3:prefix": [
                        "*/${cognito-identity.amazonaws.com:sub}/*"
                    ]
                }
            }
        },
        {
            "Action": [
                "s3:GetObject",
                "s3:PutObject"
            ],
            "Effect": "Allow",
            "Resource": [
                "arn:aws:s3:::bucket-name/cognito/${cognito-identity.amazonaws.com:sub}/",
                "arn:aws:s3:::bucket-name/cognito/${cognito-identity.amazonaws.com:sub}/*"
            ]
        }
    ]
}

I tried listing bucket / get object / put object, all access denied.

I did try playing around with policys such as removing the listbucket condition (obviously it allows access then since i have authenticated) / changing "s3:prefix" to "${cognito-identity.amazonaws.com:sub}/" or "cognito/${cognito-identity.amazonaws.com:sub}/" but can't make anything work.

Same goes for put or get object.

My S3 folder is bucket-name/cognito/{cognito-user-identity-id}/key

I referred to: https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_examples_s3_cognito-bucket.html

https://aws.amazon.com/blogs/mobile/understanding-amazon-cognito-authentication-part-3-roles-and-policies/

any insights on where might be wrong?

Upvotes: 2

Views: 337

Answers (1)

unacorn
unacorn

Reputation: 1042

I managed to resolve this after changing the GetObject and PutObject policy Resources from

"arn:aws:s3:::bucket-name/cognito/${cognito-identity.amazonaws.com:sub}/",
"arn:aws:s3:::bucket-name/cognito/${cognito-identity.amazonaws.com:sub}/*"

to

"arn:aws:s3:::bucket-name/*/${cognito-identity.amazonaws.com:sub}/",
"arn:aws:s3:::bucket-name/*/${cognito-identity.amazonaws.com:sub}/*"

and it works magically. I don't quite get why cognito would prevent the access since my bucket has cognito prefix after the bucket root, but this is resolved now.

Upvotes: 2

Related Questions