APIGateway, Lambda, DynamoDB

How do you achieve fine-grained DynamoDB access control from a Lambda function?

I am applying this policy to db access:
“Condition”: {
“ForAllValues:StringEquals”: {
“dynamodb:LeadingKeys”: [
"${cognito-identity.amazonaws.com:sub}"
]
}
}

The problem is that my lambda function is not executing in a role with the Cognito-identity set. I can access the CognitoID in the lambda function via event.requestContext.identity.cognitoIdentityId.

I have tried checking “Invoke with caller credentials” in APIGateway but it doesn’t seem to do anything. When my lambda errors out it still says the role is the one assigned to it, not the one from APIGateway. I also tried changing the trust relationship for the lamba function to one with Cognito support.

This stackoverflow question is the same problem I am hitting.


Isn’t there a simpler solution? Why is this so hard to do?

Searching around the aws forums and the web, I see dozens of people asking this same question but no good answers on how to achieve it.

I asked with more detail on AWS Lambda forum

My lambda execution role includes this:
{
“Condition”: {
“ForAllValues:StringEquals”: {
“dynamodb:LeadingKeys”: [
"${cognito-identity.amazonaws.com:sub}"
]
}
},
“Action”: [
“dynamodb:Query”,
“dynamodb:GetItem”,
“dynamodb:PutItem”,
“dynamodb:UpdateItem”,
“dynamodb:DeleteItem”
],
“Resource”: “arn:aws:dynamodb:us-east-1:*:table/digispeaker-dev”,
“Effect”: “Allow”
}

In ApiGateway I have “Invoke with caller credentials” checked.

In my lambda code I dump the variable and use it to access dynamodb…
console.error(context.identity.cognitoIdentityId);

const dynamoDb = new AWS.DynamoDB.DocumentClient();

const params = {
TableName: process.env.DYNAMODB_TABLE,
Item: {
id: context.identity.cognitoIdentityId,
timestamp: Date.now().toString(),
text: data.text,
checked: false,
createdAt: timestamp,
updatedAt: timestamp,
},
};

// write the event to the database
dynamoDb.put(params, (error) => {
// handle potential errors
if (error) {
console.error(error);
callback(new Error(‘Couldn’t create the event item.’));
return;
}

Why does the put fail? If I remove the condition on leadingkeys it will succeed. The logged value of context.identity.cognitoIdentityId is the correct user id, us-east-1:e7c7bea0-10ee-4f10-8bc5-f473f1b9bdc9

Why doesn’t ${cognito-identity.amazonaws.com:sub} in the policy resolve equal to context.identity.cognitoIdentityId inside the lambda function? In ApiGateway I have “Invoke with caller credentials” checked.

Is there a way to dump the policy with resolved variables from inside the lambda function? That would let me see what ${cognito-identity.amazonaws.com:sub} is resolving to.

2017-08-10T15:54:40.644Z 379d9913-7de4-11e7-a66b-43295915f7bd { [AccessDeniedException: User: arn:aws:sts::794688751975:assumed-role/digispeaker-dev-us-east-1-lambdaRole/digispeaker-dev-create is not authorized to perform: dynamodb:PutItem on resource: arn:aws:dynamodb:us-east-1:794688751975:table/digispeaker-dev]
message: ‘User: arn:aws:sts::794688751975:assumed-role/digispeaker-dev-us-east-1-lambdaRole/digispeaker-dev-create is not authorized to perform: dynamodb:PutItem on resource: arn:aws:dynamodb:us-east-1:794688751975:table/digispeaker-dev’,
code: ‘AccessDeniedException’,
time: Thu Aug 10 2017 15:54:40 GMT+0000 (UTC),
requestId: ‘57C54H4P1RCNUOU6FST4GF1AUBVV4KQNSO5AEMVJF66Q9ASUAAJG’,
statusCode: 400,
retryable: false,
retryDelay: 45.197168621234596 }

I finally located an explanation for this behavior…

https://forums.aws.amazon.com/thread.jspa?messageID=687420&#687420