I have multiple Cognito user pools which I use to separate users for different applications. I also have a set of APIs which are defined in API Gateway. These APIs are common and multiple applications can use them. I am trying to control which applications have access to which APIs using a Cognito resource server and custom scopes.
This is the guide I've been following: https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-enable-cognito-user-pool.html
The problem I'm having is that I have to specify a user pool when creating an API Gateway authorizer. I can create multiple authorizers but I only seem to be able to select one when attaching an authorizer to an API Gateway method.
This set up means than only one user pool can ever have access to an API in API Gateway when using the Cognito authorizer. Is this correct or have I missed something?
My only option seems to be using a Lambda authorizer and doing all this manually. However, this means that I have to store a mapping between API endpoints/methods and custom scopes. If I were to take this approach, how would I verify that an access token has access to the endpoint in the incoming request?
I've done something similar but not exactly what you are doing, but maybe it'll lead you in the right direction.
So I think you're right in that you'll need to do a Lambda authorizer, but then assume the role of the cognito user. Then if that user doesn't have access to do something IAM will blow it up.
client = boto3.client('sts')
role=event['requestContext']['authorizer']['claims']['cognito:preferred_role']
assumed_role_object = client.assume_role(
RoleArn=role,
RoleSessionName='APIrole'
)
credentials=assumed_role_object['Credentials']
dynamo_resource=boto3.resource(
'dynamodb',
aws_access_key_id=credentials['AccessKeyId'],
aws_secret_access_key=credentials['SecretAccessKey'],
aws_session_token=credentials['SessionToken'],
)
There is a solution posted here:
How to use multiple Cognito user pools for a single endpoint with AWS API Gateway?
I found Abhay Nayak answer useful, it helped me to achieve my scenario:
Allowing authorization for a single endpoint, using JWTs provided by different Cognitos, from different aws accounts. Using cognito user pool authorizer, not custom lambda authorizer.
Here is the authorizer and endpoint from my serverless .yml template:
functions:
service:
handler: service.service
events:
- http:
path: service
method: get
authorizer:
type: COGNITO_USER_POOLS
authorizerId:
Ref: ApiGatewayAuthorizer
resources:
Resources:
ApiGatewayAuthorizer:
Type: AWS::ApiGateway::Authorizer
Properties:
AuthorizerResultTtlInSeconds: 300
Name: API_AUTH_cognito_authorizer
IdentitySource: method.request.header.Authorization
RestApiId:
Ref: ApiGatewayRestApi
Type: COGNITO_USER_POOLS
ProviderARNs:
- arn:aws:cognito-idp:us-east-1:account1:userpool/userpool1
- arn:aws:cognito-idp:us-east-1:account1:userpool/userpool2
- arn:aws:cognito-idp:us-east-1:account2:userpool/userpool3
- arn:aws:cognito-idp:us-east-1:account2:userpool/userpool4
Related
I am currently running an API through APIGW, the endpoints are protected by a Lambda Authorizer. In order to improve performance, I enabled caching in APIGW for the Policy generated by the Authorizer.
But, I'm unsure how to effectively test if the cache is working properly. Is there some standard series of calls I could use, or some way to definitively check that the Policy is being cached?
Part of SAM Template:
EndpointConfiguration:
Type: PRIVATE
VPCEndpointIds: !Ref VPCEndpoints
Auth:
DefaultAuthorizer: DefAuthorizer
Authorizers:
DefAuthorizer:
FunctionPayloadType: TOKEN
FunctionInvokeRole: !Sub arn:aws:iam::${AWS::AccountId}:role/RoleName
FunctionArn: !ImportValue AuthorizerArn
Identity:
Headers:
- Authorization
ReauthorizeEvery: 3600
We can check two things:
Custom Authorizer Lambda will not be invoked at all when it is using the policy from cache.
You can enable Api Gateway Stage Logs to cloudwatch. you will see difference in logging.
When it is cached, you will see only one entry Using valid authorizer policy for principal
When it is not cached, you should see additional entires
Sending request to https://lambda.us-east-1.amazonaws.com/2015-03-31/functions/arn:aws:lambda....
Authorizer result body before parsing: ... full policy that was returned by lambda
Using CDK, I have a gateway rest api lambda which I want to be accessible to;
Members of a cognito user pool
Other lambdas in my stack
As far as I know there is no way to add 2 authorizers to the same gateway resource method?
So this means I have 2 options;
Create custom Lambda authoriser which verifies tokens for cognito and IAM
Add 2 separate recourses to my api and call a different endpoint from my client vs my lambdas. E.g:
/external/route1 with cognito authorizer
/internal/route1 with iam authorizer
Which of these approaches is better? I imagine option 2 is easier to setup. Are there any CDK examples or constructs that could help me with this?
You can run Cognito authorizers down the whole stack and then check the scopes per internal service. At a gateway, Cognito authorizers will validate the token and check the scopes against what you've specified. I don't know about CDK, but if you're using Pulumi Crosswalk, it's really trivial to setup Cognito Authorizers in APIGW, something like this:
const api = new awsx.apigateway.API(<NAME>, {
routes: [{
path: <PATH>,
method: <METHOD>,
eventHandler: <LAMBDA>,
authorizers: [awsx.apigateway.getCognitoAuthorizer({
providerARNs: [<USERPOOL>],
methodsToAuthorize: ['<RESOURCE>/<SCOPE>']
})]
}]
});
In my AWS project, I use API Gateway to create APIs that call lambda functions. The APIs are called by an Android application. I use a Cognito user pool and a Cognito Identity pool to manage my users (authenticated and unauthenticated).
In my lambda functions, I need to get the identity ID of the users. After some research, I saw that to achieve that, I need to check Invoke with caller credentials in the Integration Request of my API.
Unfortunately, when I call my API, I got an error 500, with the following log: Execution failed due to configuration error: Invalid permissions on Lambda function. Apparently, it's because the Cognito identity pool role doesn't have the lambda invoke permissions for the backend lambda.
So in order to be able to get the identity id in my lambda function, how can I add those permissions and, if possible, what is the CloudFormation syntax to add those permissions?
Thanks for your help.
Select the AWS Lambda (in AWS UI) which you are trying to fix and scroll down to the Execution role part. You need to create a new role that has permission to access the Cognito Identity pool and sett that role for Lambda and save it.
In AWS API, have you added the authorizer which sets permission (allow/deny) to access the API.
If your users are in Cognito User Pool, there is no need of API gateway, Lambda, etc. Simply use AWS Amplify and everything is baked into the framework.
https://aws-amplify.github.io/docs/js/authentication
Robin
Here is what was missing in my CloudFormation template, just add it in the Cognito Identity Pool policy (or policies if you're dealing with unauthenticated users):
- Effect: 'Allow'
Action:
- 'lambda:InvokeFunction'
Resource:
Fn::Join:
- ''
-
- 'arn:aws:lambda:'
- Ref: AWS::Region
- ':'
- Ref: AWS::AccountId
- ':function:*'
I'm trying to limit access on a service endpoints to a specific user pool (ie admin).
From my understanding of the documentation (https://serverless.com/framework/docs/providers/aws/events/apigateway/) this should be possible by setting
get:
handler: functions/get.main
events:
- http:
path: /
method: get
cors: true
authorizer: "arn:aws:cognito-idp:eu-west-2:####:userpool/eu-west-2_xET8A8Kui"
Deploys fine and appears correctly in the API gateway for this endpoint
Problem is any calls via AWS Amplify (React integration) to the API result in a 401 error! If I put the authorizer back to aws_iam it works fine.
Where can I even begin to debug this? I can't find any specific logs or reason why this is failing.
How you have things setup should work (I have that working - just a User Pool).
You will need to pass the idToken you received from your Amplify auth call in the Authorization header:
Authorization: Bearer eyJraWQiOiJ4T2NLWG5GakljWE...
Make sure to use the idToken, not the accessToken.
Last time I checked amplify only supported IAM Auth not user pool auth. You can use an identity pool with your user pool and still use IAM via your user pool.
How to setup cross account custom authorizer with serverless framework? Custom authorizer configured with sls framework works fine if it is in the same AWS account as function that needs authorization.
What I have now is organization root account where authorizer function has been deployed. On the second account, which is organization member, I have a serverless service deployed with the endpoints that needs to be authorized from the root account.
Is it possible to configure something like this inside serverless.yml that will be deployed on the member account (111111111111 is root account number):
hello:
handler: api/hello.handler
events:
- http:
path: hello
method: get
cors: true
authorizer: arn:aws:lambda:eu-west-1:111111111111:function:authorizer
I have tried this and received following error:
An error occurred: AuthorizerApiGatewayAuthorizer - The policy of
Lambda function must explicitly authorize the method or custom
authorizer with a SourceArn condition for cross account integration
(Service: AmazonApiGateway; Status Code: 400; Error Code:
BadRequestException;
... which makes sense according to the AWS docs. These docs explains how to manually do it using API Gateway console which is exactly what I did for now (authorizer in the root, authorizer in the member account - manually connected through API gateway, same as described in the docs).
I need a better solution as the number of services and organization member accounts is going to grow.
Is it possible to configure and make this work with serverless framework?
As with a lot of the Serverless Framework, there's a plug-in for those times that CloudFormation hasn't yet offered an option:
https://github.com/rschick/serverless-plugin-lambda-account-access
The custom authorizer's serverless.yml should then include:
plugins:
- serverless-plugin-lambda-account-access
provider:
allowAccess:
- 111111111111 # account id of invoking account