I have an aws ApiGateway which verify my token and pass request to lambda.
When I have an error from lambda, APIGateway response is
{
"statusCode": 500,
"error": "Internal Server Error",
"message": "..."
}
But if I don't pass my token, then APIGateway will return me
{
"message": "Unauthorized"
}
And in postman I have statusCode: 401.
How I want it to be:
{
"statusCode": 401,
"error": "Unauthorized"
}
I use serverless.yml to deploy:
functions:
index:
handler: dist/index.handler
events:
- http:
cors: true
path: '/'
method: any
authorizer:
type: COGNITO_USER_POOLS
authorizerId:
Ref: ApiGatewayAuthorizer
Please, tell me how I have to change my serverless.yml to change the 'Unauthorized' error to be as at third code example.
try to implement this:
https://github.com/SeptiyanAndika/serverless-custom-authorizer:
Allows to get reponses like:
{
"success":false,
"message":"Custom Deny Message"
}
Adding to #svladimirrc, it will work even if you don't have a custom authorizer in place, just make sure you have the proper name of your API Gateway configured to link to:
resources:
Resources:
ApiGatewayRestApi:
Type: AWS::ApiGateway::RestApi
Properties:
Name: ${self:provider.stage}-${self:service}
InvalidApiKeyGatewayResponse:
Type: 'AWS::ApiGateway::GatewayResponse'
Properties:
RestApiId:
Ref: 'ApiGatewayRestApi'
ResponseType: INVALID_API_KEY
ResponseTemplates:
application/json: "{\"success\":false,\"message\":\"Invalid API key\"}"
StatusCode: '401'
You can achieve this by modifying Gateway Responses.
Go to API Gateway in AWS Management Console.
Select your API.
Click "Gateway Responses" which can be seen on the left side.
Select "Unauthorized" in the list of Gateway Responses.
Select "application/json" in Response Templates and click "Edit".
Update the response template body based on your requirements.
Click "Save".
Re-deploy your API.
Related
Iam trying to send a post request to aws lambda function throuth api gateway.
but when i send it by postman it return a error "Could not parse payload into json".
Unrecognized token
like the picture below(i remove some useless info):
next i try to send the same request through "Test Method" on the webpage of api gateway:
it successed! (iam pretty sure use the same request body)
extra info:
not choose "Use Lambda Proxy integration" in Api gateway Integration Request.
try to set "Mapping Templates" but not solved my problem.
i create that reource for api through serverless freamwork.
request header Content-Type have been set to "application/json".
i have check the CloudWatch of my lambda function, and nothing find ,it proved that the problem is limited in api gateway.
here is the code of my lambda:
def handle(event, context):
res = {'code': 200}
bad_res = {'code': 400}
if event['operation'] == 'JoinCluster':
# verify user by aws cognito
is_allowed, user = is_joining_allowed(event)
if is_allowed:
# add a item in dynamodb
client_info = client_table.add_requested_client(user, event["metadata"])
# get a available token from another table
token = token_table.get_valid_token()
res = {'owner': user, 'id': client_info['id'], **token, **res}
# Send a message to SQS to indicate that a node is ready to join
aws.send_message({'id': client_info['id']}, queue_name="netmind-node-join-queue")
return res
else:
bad_res["message"] = "You are not allowed to join this cluster, check your access token before retrying."
return bad_res
bad_res["message"] = "unknown operation."
return bad_res
I try your lambda with api-gateway from postman site, it works well.
Something you not mentioned above cause the problem.
In my postman page, Body tab with green dot at right hand.
I also choose raw and JSON follow your screen capture.
But for Params and Authorization tab,
I dont know what to set, and keep them without green lights.
I guess something there make things wrong.
Following is my test api-gateway definition which works in my site.
You shoud change ARN_FOR_YOUR_LAMBDA to your real lambda-arn before you can import into your api-gateway.
---
swagger: "2.0"
info:
description: "test"
version: "2022-04-02T15:18:02Z"
title: "PetStore"
host: "HOST_OF_YOUR_API"
basePath: "/test"
schemes:
- "https"
paths:
/mao:
post:
produces:
- "application/json"
responses:
"200":
description: "200 response"
schema:
$ref: "#/definitions/Empty"
x-amazon-apigateway-integration:
httpMethod: "POST"
uri: "arn:aws:apigateway:ap-northeast-1:lambda:path/2015-03-31/functions/ARN_FOR_YOUR_LAMBDA/invocations"
responses:
default:
statusCode: "200"
passthroughBehavior: "when_no_match"
contentHandling: "CONVERT_TO_TEXT"
type: "aws"
options:
consumes:
- "application/json"
produces:
- "application/json"
responses:
"200":
description: "200 response"
schema:
$ref: "#/definitions/Empty"
headers:
Access-Control-Allow-Origin:
type: "string"
Access-Control-Allow-Methods:
type: "string"
Access-Control-Allow-Headers:
type: "string"
x-amazon-apigateway-integration:
responses:
default:
statusCode: "200"
responseParameters:
method.response.header.Access-Control-Allow-Methods: "'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT'"
method.response.header.Access-Control-Allow-Headers: "'Content-Type,Authorization,X-Amz-Date,X-Api-Key,X-Amz-Security-Token'"
method.response.header.Access-Control-Allow-Origin: "'*'"
requestTemplates:
application/json: "{\"statusCode\": 200}"
passthroughBehavior: "when_no_match"
type: "mock"
definitions:
Empty:
type: "object"
Wish this help.
Im using Serverless framework to deploy a set of API's running on API Gateway using cognito as authorizer. Everything seemed to work but i found an issue that when lambda crashes for some reason (maybe time out or some unhandled exception), API Gateway returns 401 Unauthorized.
I added some gateway responses and I can handle some of the errors but even if I get the initial error, I keep receiving the 401 Unauthorized in Frontend.
This is part of my serverless.yml file:
getSimulationStatus:
handler: getSimulationStatus.handler
events:
- http:
path: /simulation/status
method: post
cors: true
authorizer:
arn: arn:aws:cognito-idp:us-east-1:${self:custom.settings.COGNITO_ARN}
resources:
Resources:
GatewayResponseDefault5XX:
Type: 'AWS::ApiGateway::GatewayResponse'
Properties:
ResponseParameters:
gatewayresponse.header.Access-Control-Allow-Origin: "'*'"
gatewayresponse.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
gatewayresponse.header.Access-Control-Allow-Methods: "'*'"
ResponseType: DEFAULT_5XX
RestApiId:
Ref: 'ApiGatewayRestApi'
GatewayResponseDefault4XX:
Type: 'AWS::ApiGateway::GatewayResponse'
Properties:
ResponseParameters:
gatewayresponse.header.Access-Control-Allow-Origin: "'*'"
gatewayresponse.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
gatewayresponse.header.Access-Control-Allow-Methods: "'*'"
ResponseType: DEFAULT_4XX
RestApiId:
Ref: 'ApiGatewayRestApi'
For the frontend im using Angular and I have an error interceptor that captures all this events. Currently im forcing a 504 Request time out and Im able to see that, but also the 401 Unauthorized appears.
This is the code for the interceptor:
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(request).pipe(catchError(err => {
console.log("error captured", err);
return throwError(err.error.message);
}));
}
The idea is to sign out if I receive 401 from backend but currently if any of my lambdas fails, Im immediately logged out.
Any Idea on what could be the problem?
Edit:
This is a capture from web console:
I have a better solution.Why you don't verify if the token it's valid before send the request to api gateway?
you can install this:
npm i jwt-decode
In my case, I always have a fuction that return me the headers and that allows me to validate in that function the token on every call to the API.
Here a function that validate the token (you only need to pass the token of cognito if you are ussing amplify or something like that).
import jwt_decode from "jwt-decode";
....
some code
....
tokenValidator(){
if(localStorage.getItem('access_token') == null){
//call signout cognito and go to login
}
const decoded = jwt_decode(localStorage.getItem('access_token'));
if (decoded.exp === undefined) return null;
const date = new Date(0);
date.setUTCSeconds(decoded.exp);
if(new Date() > date){
//call signout cognito and go to login
}
}
This solution prevents your problem.
I have an OpenAPI spec for an api which I am deploying via CDK. The spec looks like:
openapi: 3.0.1
info:
title: My API
description: My REST API with CORS enabled
version: 0.1.0
x-amazon-apigateway-cors:
allowOrigins:
- "*"
allowCredentials: true
exposeHeaders:
- "x-apigateway-header"
- "x-amz-date"
- "content-type"
maxAge: 3600
allowMethods:
- "*"
allowHeaders":
- "x-apigateway-header"
- "x-amz-date"
- "content-type"
- "Authorization"
components:
securitySchemes:
lambda:
type: "apiKey"
name: "Authorization"
in: "header"
x-amazon-apigateway-authtype: "custom"
x-amazon-apigateway-authorizer:
authorizerUri: "{{my-lambda-authorizer}}"
authorizerResultTtlInSeconds: 300
type: "token"
paths:
/user/{id}:
get:
summary: Get info of specified user.
parameters:
- in: path
name: id
required: true
schema:
type: string
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/User'
security:
- lambda: []
x-amazon-apigateway-integration:
uri: "{{my-lambda}}"
passthroughBehavior: "when_no_match"
httpMethod: "POST"
type: "aws_proxy"
When I try to access this via fetch(), I get an error Failed to load resource: Origin http://localhost:8000 is not allowed by Access-Control-Allow-Origin.
fetch('https://api.example.com/user/1')
.then(response => response.json())
.then((user: User) => {
// do something
})
.catch((err) => {
console.log("Error: " + err);
});
The API is accessible at api.example.com and I am running the website locally using Gatsby at localhost:8000.
The AWS docs seem to state that CORS is enabled when I put x-amazon-apigateway-cors at the root of the spec, but CORS doesn't seem to be enabled (or working) when I try to access the API. How do I enable CORS for my API without needing to configure it in the console?
Amazon API Gateway offers two types of APIs: REST APIs and HTTP APIs. REST APIs were the kind of APIs originally introduced with Amazon API Gateway, while HTTP APIs got announced at the end of 2019.
Based on the description of your OpenAPI specification I assume you're trying to deploy a REST API. The x-amazon-apigateway-cors OpenAPI extension however only works for HTTP APIs.
You have two choices now: You either switch to use a HTTP API or you configure CORS manually. As long as you don't need features only supported by REST APIs, I suggest you switch to use a HTTP API, as that's the more modern kind of API Amazon API Gateway offers.
If you want to use a REST API, enabling CORS requires more manual configuration. That's documented by AWS in Enabling CORS for a REST API resource. Essentially you have to ensure your integration returns proper CORS headers. For a NodeJS AWS Lambda function that could look like:
exports.handler = async (event) => {
const response = {
statusCode: 200,
headers: {
"Access-Control-Allow-Headers" : "Content-Type",
"Access-Control-Allow-Origin": "https://www.example.com",
"Access-Control-Allow-Methods": "OPTIONS,POST,GET"
},
body: JSON.stringify('Hello from Lambda!'),
};
return response;
};
For CORS pre-flight requests to work you'd also have to ensure that OPTIONS-requests also return the correct headers. The easiest way to achieve that is to add a mock-integration for the OPTIONS-request to your OpenAPI specification. That would look like:
options:
responses:
'200':
description: Default response
headers:
Access-Control-Allow-Headers:
schema:
type: string
Access-Control-Allow-Methods:
schema:
type: string
Access-Control-Allow-Origin:
schema:
type: string
x-amazon-apigateway-integration:
type: mock
requestTemplates:
application/json: |
{"statusCode" : 200}
responses:
default:
statusCode: 200
responseParameters:
method.response.header.Access-Control-Allow-Headers: "'*'"
method.response.header.Access-Control-Allow-Methods: "'OPTIONS,POST'"
method.response.header.Access-Control-Allow-Origin: "'https://example.com/'"
Please also mind that modern browsers don't support localhost as origin for CORS, so you might need to work around that as well.
I'am using node serverless and python to deploy service on AWS.
Services used: S3, API-Gateway, Cloudformation, Lambda.
The problem is... I am getting empty response body:
{
"statusCode": 200,
"isBase64Encoded": false,
"headers": {
"Content-Type": "application/json"
},
"body": "{}"
}
The field "body" is empty. It is same when I test with POSTMAN.
but when I test on lambda it works fine:
{
"statusCode": 200,
"isBase64Encoded": false,
"headers": {
"Content-Type": "application/json"
},
"body": "{\"55b7badc-75af-41c0-9877-af308264cb33\":\"0.4666666666666667\",\"4694e172-322e-4a51-930e-d3b9bfd3c2e6\":\"0.36363636363636365\",\"c5447cc5-936d-4aa6-97c4-3f51a7e7c283\":\"0.3\",\"6abf0893-5d32-4a43-942f-aaef4395d91d\":\"0.2727272727272727\",\"c0bf1214-fb41-48eb-b07d-f81b71ba0061\":\"0.25\"}"
}
Here is the yml file:
service: collaborative
provider:
name: aws
runtime: python3.6
region: eu-west-1
defaults:
stage: dev1
region: eu-west-1
package:
include:
- collaborative
exclude:
- .git
- .idea
- .col_ser.txt
custom:
integration: lambda
functions:
collaborative:
name: lambda-collaborative
handler: handler.lambda_handler
events:
- http:
path: recommend_user
method: post
integration: lambda
cors: true
request:
template:
text/xhtml: '{ "stage" : "$context.stage" }'
application/json: '{ "httpMethod" : "$context.httpMethod" }'
response:
headers:
Access-Control-Allow-Origin: "'*'"
statusCodes:
400:
pattern: '.*wrong.*'
template:
application/json: >
#set ($errorMessageObj = $input.path('$.errorMessage'))
$errorMessageObj
Resources:
ApiGatewayMethodRecommenduserPost:
Type: AWS::ApiGateway::Method
Properties:
Integration:
IntegrationHttpMethod: POST
Type: lambda
If you have invoked your lambda function asynchronously from API Gateway then it's normal to receive an empty response because in this case, API Gateway does not wait for lambda to response back and simply return an empty response. However, you can create your own integration response template from API gateway depending upon the status code.
If you have invoked synchronously and still haven't received the response then you can also invoke your lambda function from API gateway and observe if your lambda function is working properly or not.
I have a Lambda proxy integration with API Gateway that is working fine.
CORS is handled directly in the lambda code with checks against lists of authorized domains.
But the issue now is with unexpected errors during Lambda execution.
API Gateway returns the following message in such a case:
{
message: "Internal server error"
}
with a 502 HTTP status code. Unfortunately for me, the Access-Control-Allow-Origin header is missing in that response, which is causing errors on client side.
The same happens also with timeouts for example. The HTTP status code is then 504 but the response content and the lack of Access-Control-Allow-Origin is the same.
The same issue occurs also in case of permission issue: if the API Gateway does not have sufficient permissions to call the Lambda, then a 500 error is returned but, once again, without any header.
A fixed value of '*' would be OK in the case of Lambda errors but how and where can this be configured?
Late to the game, but you can add these to your SAM / CloudFormation template to fix this:
Resources:
GatewayResponseDefault4XX:
Type: 'AWS::ApiGateway::GatewayResponse'
Properties:
ResponseParameters:
gatewayresponse.header.Access-Control-Allow-Origin: "'*'"
gatewayresponse.header.Access-Control-Allow-Headers: "'*'"
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: "'*'"
ResponseType: DEFAULT_5XX
RestApiId:
Ref: 'ApiGatewayRestApi'
Taken from here: [https://serverless-stack.com/chapters/handle-api-gateway-cors-errors.html#create-a-resource][1]
Add in your handler function this callback with these values, you can add your proper response in the body
callback(null, {
statusCode: 200,
headers: {
'Content-Type': 'application/json',
"Access-Control-Allow-Headers": "*",
"Access-Control-Allow-Origin": "*",
},
body: JSON.stringify({ "response": true })
});