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
Related
Using Serverless framework (v3) with node.js we have added a few Lambdas to use the new(-ish) feature of Function URL to use a simple HTTP request instead of lambda.invoke.
Having dependent functions (one Lambda calling another Lambda) that are deployed in AWS was not a problem using the lambda.invoke as we have keys exported locally for testing against the AWS "test" account.
However, now using Function URL with IAM I get 403 Forbidden:
htmltopdf:
handler: src/functions/htmltopdf/handler.run
timeout: 180
memorySize: 1024
ephemeralStorageSize: 2048
url:
authorizer: aws_iam
I would've thought that my "local" AWS credentials would use my IAM role (which is AdministratorAccess in the AWS test account).
Running it deploy in the Account works just fine, but how can I access it using serverless invoke local command to call the lambda in the AWS account?
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>']
})]
}]
});
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
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 have a PreTokenGenerator function which adds an additional claim to the id token.
In my serverless.yml I have the following definition.
functions:
issueAuthToken:
handler: src/handlers/cognitoPreToken.handler
events:
- cognitoUserPool:
pool: ${self:provider.stage}-user-pool
trigger: PreTokenGeneration
This runs and deploys, however does not wire up the user pool trigger in the userpool (see below)
How can I get this trigger to be setup? The documentation seems to be pretty lacking when it comes to cognito triggers
Pre Token Generation is currently not available in the UserPool LambdaConfig and hence not supported by CloudFormation (which serverless framework use). At the moment it can only be configured via console or AWS CLI.
According to Serverless documentation, you should inform the attribute existing: true and this is very critical if you don't want to create a new Cognito User Pool using-existing-pools
Also according to this forum this feature is now covered by AWS CloudFormation AWS Forum
This is a recent feature implemented by Serverless, so make sure you have the latest version installed.
Here is my Serverless configuration code:
preTokenGenerator:
name: ${self:service}-${self:provider.stage}-preTokenGenerator
description: Lambda service to list blog articles
role: LambdaRole
handler: functions/general/blog.list
events:
- cognitoUserPool:
pool: my-pool-name
trigger: PreTokenGeneration
existing: true
I literally duplicated an existing function and changed its trigger.
cognito-lambda-function