My application uses Cognito user pools to restrict access so that each user may access their own personal folder read/write in a "private" bucket. In a "public" bucket it is the same, except that everyone has read access to everything and write access only to their own folder. This all works fine when accessed via the JavaScript S3 SDK.
I am now trying to implement access via the API Gateway talking to a Lambda function which accesses S3.
The problem is that it appears that the Cognito Userpool identity is not being used by the Lambda function. If I give the Lambda function role total S3 access then the function is able to access S3 fine and the function works. If however I have the role policies in place to restrict by Cognito Userpool ID as described above, the I get access denied.
It appears to me that the Userpool Cognito Identity is not what the Lambda function is using.
Can anyone suggest the correct configuration for this setup?
thanks
API Gateway and Lambda do not automatically support this use case.
The Lambda function is running with the identity of it's execution role, thus its context won't contain the identity attributes from the Cognito user pool. (The context object in the Lambda function contains the context from Lambda's perspective.)
API Gateway exposes the Cognito user pool identity information via $context.authorizer.claims variable within API Gateway. To access this information from within your Lambda function, you must modify your body mapping template in API Gateway to pass the desired data from $context.authorizer.claims to your Lambda function via the request body. You're Lambda function then reads this information from the request body like any other field.
In this scenario there is no mechanism to automatically restrict permissions to S3 buckets/object on a per Cognito-user basis. You could implement logic within your Lambda function to enforce it using custom code.
Related
I have the following setup:
REST API Gateway containing one endpoint with a proxy Lambda integration and secured using the AWS_IAM authorizer. End users retrieve temporary AWS credentials from a Cognito Identity Pool in exchange for tokens retrieved from a Cognito User Pool and then call the API using these credentials.
When users call the API Gateway, I want the invoked lambda function to run with the same permissions as the invoking user. For example, if the user only has the permission to read items from DynamoDB with a PK of CUSTOMER1234, I want the lambda function to also only have that permission. Different users will have different permissions so I can't "hard-code" that into my Lambda permissions.
This seems like a fairly common use case, but I was unable to find any documentation on how to achieve something like this.
One possibility would be to send the AWS credentials (i.e. access key and secret key) of the user with every request and then construct a new session using these credentials in the Lambda, but this probably is a terrible idea (it also increases function runtime by 5x).
It doesn't seem like a good idea to send AWS credentials over the network, as there is the chance that they could be intercepted by an attacker and used to access your resources until they expired.
It could be possible to use the JWT from your Cognito User Pool to get the AWS credentials in the lambda function e.g. using:
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'IDENTITY_POOL_ID',
Logins: {
'cognito-idp.<region>.amazonaws.com/<YOUR_USER_POOL_ID>': 'ID_TOKEN'
}
});
This would mean that your lambda's API calls would run using the role mapped to in your identity pool, and you could use the LeadingKeys condition to ensure that your web identity could only access their own data:
"Condition": {
"ForAllValues:StringEquals": {
"dynamodb:LeadingKeys": ["${cognito-identity.amazonaws.com:sub}"]
}
}
However, this would add processing time because you are making extra API calls (CognitoIdentityCredentials() makes 2 API calls behind the scenes) on every request to get the credentials. Plus, you couldn't use the AWS_IAM authorization on your API Gateway method because you won't have got the credentials yet. So you are then talking about having a two authorization setups. One to allow your web identity to call the API Gateway method, and a second for your Lambda function to run as your user.
So perhaps your choices are:
Make your API calls direct from your client
Instead of going via API Gateway, you could make the DynamoDB API calls directly from the client. This would work well with your Identity Pool setup, as you can make the CognitoIdentityCredentials() call just once when the user logs in to get the AWS credentials. The subsequent DynamoDB calls would then be made with your user permissions and you could use the LeadingKeys condition to ensure that your user only accesses their own data.
Accept that your lambda function will always run with the same execution role
If you want to use API Gateway backed with Lambda, then you could just accept that you will use Lambda or Cognito Authorizers to authorize your users have permissions to execute the API method, and then some further application-level authorization in the Lambda function to ensure they only call their own partition space. In fact, this way you might not need the Identity Pool, but rather just use the sub claim from the User Pool JWT token. After all, you will have validated the JWT containing the sub hasn't been tampered with, and that it was issued by the correct user pool and application client, so you can trust its contents.
You could integrate API Gateway directly with DynamoDB
Depending on your required logic, rather than have your API method backed by a lambda function you could integrate your API Gateway method directly with DynamoDB as shown in this amazon tutorial. Again, you would use the sub from the validated JWT as the partition key to enforce the correct data access.
I have created a REST API using lambda and API gateway.
I want to give access for this API to another lambda function which is running in another AWS account.
I was thinking to create IAM based authorisation for this API. But I am not sure if this cross-AWS account based IAM authorisation is feasible?
Any better suggestions?
You can assume a role in the target account and then invoke the lambda directly using the temporary credentials. This method does not require an integration with API gateway.
You can also use IAM Authentication from anywhere if the API is publicly available. You will have to store the designated credentials.
Reference:
https://aws.amazon.com/premiumsupport/knowledge-center/lambda-function-assume-iam-role/
I would like to secure a public lambda on my website. The users will not be authenticated when they access it. Any ideas how to do this ?
Jim
Amazon Lambda functions are not available to the public without authorization. Invoking Lambda requires AWS credentials. Unauthenticated users cannot directly access Lambda. The exception is if you are using API Gateway in front of your Lambda functions.
Access to AWS Lambda requires credentials that AWS can use to authenticate your requests. Those credentials must have permissions to access AWS resources, such as an AWS Lambda function or an Amazon S3 bucket.
Authentication and Access Control for AWS Lambda
Control Access in API Gateway
This link would be helpful with screenshots given.
Fix the issue using this link
Ideally issue is that access is give with * in resources or any of the policy statement which is causing this issue.
Im building a serverless backend using the following AWS technologies:
AWS api_gateway
AWS cognito
AWS lambda
In api_gateway I have created a Cognito User Pool authorizer and Im using this authorizer for all requests to the backend.
Everything works: When a user makes a request with an invalid JWT token, the server respons accordingly. A valid JWT token executes the requested Lambda function.
Problem: I'm unable to retrieve identity information, such as accessKey, accountId, cognitoIdentityId and so forth. All these variables are null when I access them via the context object in the lambda function
Question: What do I need to do in order to get the identity variables?
The context object in the Lambda function contains the context from Lambda's perspective. The Lambda function is running with the identity of it's execution role, thus its context won't contain the identity attributes from the Cognito user pool.
API Gateway exposes the Cognito user pool identity information via $context.authorizer.claims variable within API Gateway. To access this information from within your Lambda function, you must modify your body mapping template in API Gateway to pass the desired data from $context.authorizer.claims to your Lambda function via the request body. You're Lambda function then reads this information from the request body like any other field.
Documentation on this can be found here.
Scroll down to the section titled "To enable a user pool authorizer on methods" and see step 7: "If needed, choose Integration Request to add $context.authorizer.claims ..."
When you created the Cognito User Pool you would have created two IAM Roles. You can now setup API Gateway to pass the Identity information by
Authorization set to AWS_IAM
Turn on Invoke with caller credential
In Lambda you should be able to get the information in context.
Note: In the Cognito IAM Roles you need allow invoke permission for API Gateway.
I've created a Lambda function, and connected it to an API Gateway endpoint. My users are authenticating using Facebook via Cognito (so, obviously using a Federated Identity Pool). In my iOS app, I am able to authenticate properly, receive a Cognito Id back, and also properly execute my Lambda function by way of API Gateway.
Where I'm stuck now is attempting to access the user's identity in my lambda. I understand from other threads & the AWS docs that I should use able to access the identity via context.identity.cognitoIdentityId in JS. However, the context is always null.
Further digging suggested that the context is passed in the X-Amz-Client-Context header -- when I look at packets for the API Gateway call, I do not see that header being passed through. I am using the automatically generated SDK (ObjC) from Gateway for my endpoints.
What am I missing here?
I apologize for the confusion. You have 2 options for getting this value into your lambda function:
By passing via API Gateway. If you are using the Lambda Proxy, it should already be available in event.requestContext.identity.cognitoIdentityId.
By enabling use caller credentials in your integration. If you use this method, you will also need to add lambda:Invoke permissions to your Cognito role.