Open josoroma-zz opened 4 years ago
Here, you are creating cloudfront distribution with lambda-edge event. One of the limitation for lambda-edge functions is unable to use env variables and more like memory limit, executon time limit etc. To handle env variables you cant use serverless-dotenv-plugin, instead of this you can use serverless-plugin-embedded-env-in-code.
I don't know what you want to achive but my advice is to use authorizer insted of cf-event. https://serverless.com/framework/docs/providers/aws/events/apigateway/#http-endpoints-with-custom-authorizers
@FanFataL thanks for your insights. This is what I am trying to achieve:
Call a Lambda@Edge function before each API request. Invoke the "request" interceptor every time a request is sent to the origin.
I am creating the resource but I just can't make the edge function run before calling any other api-function.
I am getting a Status Code: 403 Forbidden
response using the new Api Distribution URL as the new endpoint URL within my current conf/amplify.js
file.
...
plugins:
...
- '@silvermine/serverless-plugin-cloudfront-lambda-edge' # Associate a Lambda with a CloudFront distribution
...
functions:
# Call a Lambda@Edge function before each API request.
# Invoke the "request" interceptor every time a request is sent to the origin.
request:
name: 'scb-courses-api-${self:custom.stage}-request-api-gateway-distribution'
handler: course/interceptors/request.main
memorySize: 128
timeout: 1
lambdaAtEdge:
distribution: 'ApiGatewayDistribution'
eventType: 'origin-request'
# Defines an HTTP API endpoint that calls the main function in create.js
# - path: url path is /courses
# - method: POST request
# - cors: enabled CORS (Cross-Origin Resource Sharing) for browser cross
# domain api call
# - authorizer: authenticate using the AWS IAM role
create:
handler: course/create.main
events:
- http:
path: courses
method: post
cors: true
authorizer: aws_iam
...
resources:
...
# cloudfront invoke lambda@edge before API Gateway request
- ${file(resources/api-gateway-distribution.yml)}
...
Resources:
ApiGatewayDistribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Comment: Api Gateway Distribution
DefaultCacheBehavior:
TargetOriginId: ApiGatewayOrigin
ViewerProtocolPolicy: 'redirect-to-https'
DefaultTTL: 30
ForwardedValues:
QueryString: false
Enabled: true
Origins:
- Id: ApiGatewayOrigin
DomainName:
Fn::Join:
- "."
- - Ref: ApiGatewayRestApi
- execute-api.us-east-1.amazonaws.com
OriginPath: /development
CustomOriginConfig:
HTTPPort: 80
HTTPSPort: 443
OriginProtocolPolicy: https-only
course/interceptors/request.js
is:/**
* https://serverless.com/framework/docs/providers/aws/events/cloudfront
* https://serverless.com/blog/lambda-at-edge-support-added
*
* Add support for associating a Lambda function with a CloudFront distribution
* to take advantage of the Lambda@Edge features of CloudFront:
*
* - https://github.com/silvermine/serverless-plugin-cloudfront-lambda-edge
*
* Instead of calling the URL we did earlier (the API Gateway URL),
* we are going to use a different URL which points to the CloudFront
* Distribution:
*
* - https://www.yld.io/blog/caching-in-with-cloudfront-using-serverless
*/
export const main = (event, context, callback) => {
console.log('*====> event: ', event);
const request = event.Records[0].cf.request;
console.log('*====> request: ', request);
callback(null, request);
};
conf/amplify.js
file, the one used by the frontend side:export const amplifyConf = {
Auth: {
identityPoolId: '...',
region: 'us-east-1',
userPoolId: '...',
userPoolWebClientId: '...',
},
API: {
endpoints: [
{
name: 'ApiGatewayRestApi',
endpoint: 'https://{ApiGatewayDistributionURL}.cloudfront.net',
},
],
},
};
export const storageSourceConf = {
Storage: {
bucket: '...',
region: 'us-east-1',
identityPoolId: '...',
},
};
Hi @josoroma
Try to add Authorization header to forwarded values to the behavior option in the cloudfront distribution settings. Optionaly you can add access controll headers if you want use xhr requests with the api.
ForwardedValues:
QueryString: false
Headers:
- "Authorization"
- "Access-Control-Request-Headers"
- "Access-Control-Request-Method"
@FanFataL thanks!
No luck yet!
Access to XMLHttpRequest at 'https://{ApiGateWayRestApi}.cloudfront.net/courses' from origin
'http://localhost:3002' has been blocked by CORS policy: Response to preflight request doesn't pass
access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
functions:
authreq:
name: 'scb-courses-api-${self:custom.stage}-authreq'
handler: course/interceptor/authreq.main
memorySize: 128
timeout: 1
lambdaAtEdge:
distribution: 'ApiGatewayDistribution'
eventType: 'origin-request'
...
resources:
...
- ${file(resources/api-gateway-distribution.yml)}
Resources:
ApiGatewayDistribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Comment: Api Gateway Distribution
DefaultCacheBehavior:
TargetOriginId: ApiGatewayOrigin
ViewerProtocolPolicy: 'redirect-to-https'
DefaultTTL: 10
ForwardedValues:
QueryString: false
Headers:
- "Authorization"
- "Access-Control-Request-Headers"
- "Access-Control-Request-Method"
Enabled: true
Origins:
- Id: ApiGatewayOrigin
DomainName:
Fn::Join:
- "."
- - Ref: ApiGatewayRestApi
- execute-api.us-east-1.amazonaws.com
OriginPath: /development
CustomOriginConfig:
HTTPPort: 80
HTTPSPort: 443
OriginProtocolPolicy: https-only
Outputs:
ApiGatewayDistributionDomain:
Value:
'Fn::GetAtt': [ ApiGatewayDistribution, DomainName ]
Resources:
GatewayResponseDefault4XX:
Type: 'AWS::ApiGateway::GatewayResponse'
Properties:
ResponseParameters:
gatewayresponse.header.Access-Control-Allow-Origin: "'*'"
gatewayresponse.header.Access-Control-Allow-Headers: "'*'"
gatewayresponse.header.Access-Control-Allow-Credentials: true
ResponseType: DEFAULT_4XX
RestApiId:
Ref: 'ApiGatewayRestApi'
GatewayResponseDefault5XX:
Type: 'AWS::ApiGateway::GatewayResponse'
Properties:
ResponseParameters:
gatewayresponse.header.Access-Control-Allow-Origin: "'*'"
gatewayresponse.header.Access-Control-Allow-Headers: "'*'"
gatewayresponse.header.Access-Control-Allow-Credentials: true
ResponseType: DEFAULT_5XX
RestApiId:
Ref: 'ApiGatewayRestApi'
Outputs:
ServiceEndpointDomain:
Description: "API Gateway Domain"
Value:
Fn::Join:
- "."
- - Ref: ApiGatewayRestApi
- "execute-api"
- ${self:provider.region}
- "amazonaws.com/${self:provider.stage}"
Export:
Name: ServiceEndpointDomain
Default methods handled by the Cloudfront are HEAD and GET, you need to add OPTIONS or all of them. https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-defaultcachebehavior.html#cfn-cloudfront-distribution-defaultcachebehavior-allowedmethods
DefaultCacheBehavior:
AllowedMethods:
- "GET"
- "HEAD"
- "OPTIONS"
- "PUT"
- "PATCH"
- "POST"
- "DELETE"
CachedMethods:
- "GET"
- "HEAD"
- "OPTIONS"
Additionaly you need to enable cors in the lambda function sls configuration (if cors: *
is not enough for you try this:)
cors:
origin: '*' # <-- Specify allowed origin
headers: # <-- Specify allowed headers
- Content-Type
- X-Amz-Date
- Authorization
- X-Api-Key
- X-Amz-Security-Token
- X-Amz-User-Agent
allowCredentials: false
@FanFataL thanks, all your hints have been so very helpful to me. Just one last thing,
one thing I'm still struggling with, do you know what should I do in order to make event.requestContext.identity.cognitoIdentityId
work, now the lambda edge function is successfully executed but I am getting a sort of null
error message associated to the lack of the event.requestContext.identity.cognitoIdentityId
value in the create.js
lambda@endpoint.
export const main = async(event, context) => {
const data = JSON.parse(event.body);
const now = new Date().toISOString();
const params = {
TableName: process.env.tableName,
Item: {
courseId: data.courseId || uuid.v1(),
userId: event.requestContext.identity.cognitoIdentityId,
title: data.title
}
};
try {
await dynamoDbLib.call('put', params);
return success(params.Item);
} catch (error) {
return failure({ error });
}
};
functions:
# Defines an HTTP API endpoint that calls the main function in create.js
# - path: url path is /courses
# - method: POST request
# - cors: enabled CORS (Cross-Origin Resource Sharing) for browser cross
# domain api call
# - authorizer: authenticate using the AWS IAM role
create:
handler: course/create.main
events:
- http:
path: courses
method: post
cors:
origin: '*'
headers:
- Content-Type
- X-Amz-Date
- Authorization
- X-Api-Key
- X-Amz-Security-Token
- X-Amz-User-Agent
allowCredentials: false
# Call a Lambda@Edge function before each API request.
# Invoke the "request" interceptor every time a request is sent to the origin.
authreq:
name: 'scb-courses-api-${self:custom.stage}-authreq'
handler: course/interceptor/authreq.main
memorySize: 128
timeout: 1
lambdaAtEdge:
distribution: 'ApiGatewayDistribution'
eventType: 'origin-request'
resources:
...
# API Gateway Errors
- ${file(resources/api-gateway-errors.yml)}
# cloudfront invoke lambda@edge before API Gateway request
- ${file(resources/api-gateway-distribution.yml)}
Resources:
GatewayResponseDefault4XX:
Type: 'AWS::ApiGateway::GatewayResponse'
Properties:
ResponseParameters:
gatewayresponse.header.Access-Control-Allow-Origin: "'*'"
gatewayresponse.header.Access-Control-Allow-Headers: "'*'"
gatewayresponse.header.Access-Control-Allow-Credentials: true
ResponseType: DEFAULT_4XX
RestApiId:
Ref: 'ApiGatewayRestApi'
GatewayResponseDefault5XX:
Type: 'AWS::ApiGateway::GatewayResponse'
Properties:
ResponseParameters:
gatewayresponse.header.Access-Control-Allow-Origin: "'*'"
gatewayresponse.header.Access-Control-Allow-Headers: "'*'"
gatewayresponse.header.Access-Control-Allow-Credentials: true
ResponseType: DEFAULT_5XX
RestApiId:
Ref: 'ApiGatewayRestApi'
Outputs:
ServiceEndpointDomain:
Description: "API Gateway Domain"
Value:
Fn::Join:
- "."
- - Ref: ApiGatewayRestApi
- "execute-api"
- ${self:provider.region}
- "amazonaws.com/${self:provider.stage}"
Export:
Name: ServiceEndpointDomain
Hi,
Its because event.requestContext.identity.cognitoIdentityId
is availalbe only when you are using aws_iam authorizers.
If you want to have information abourt user simply modify request headers and it will be available in the gateway lambda.
const request = event.Records[0].cf.request;
const headers = request.headers || {};
headers['x-cognito-identity-id'] = [{
key: 'X-Cognito-Identity-Id',
value: 666
}];
@FanFataL so... according to this lambda edger code:
headers['x-cognito-identity-id'] = [{
key: 'X-Cognito-Identity-Id',
value: 666
}];
should the value
be sent by the client request itself. I found no way to get identity id whitin the lambda edge function?
Hi @josoroma, Can you please share how you solved the 403:Forbidden issue? I am facing this issue while invoking a lambda edge function before any apiGatewayRestApi function request
I was wondering... How to invoke a lambda edge function before any apiGatewayRestApi function request?
I was using:
I am hitting the following error:
"Current API gateway endpoint" or "execute-api URL" is:
note: {0123456789}.execute-api.us-east-1.amazonaws.com/development
Thanks!