Delete DynamoDB entries with AWS API - amazon-web-services

I am learning AWS API Gateway to manage the api in the cloud and not through express.js. I created the method DELETE under a resource with the following Integration Request.
Integration Request
Integration Type: AWS Service
AWS Region: us-east-1
AWS Service: DynamoDB
HTTP method: DELETE
Action: deleteItem
Execution role: XXXXXX
Content Handling: Passthrough
the execution role is working because i can read/write to DynamoDB. a Body Mapping Template that has a Content-Type: application/json as follows:
{
"TableName": "comments",
"Key": {
"commentId": {
"S": "$context.requestId"
},
"pageId": {
"S": "$input.path('$.pageId')"
},
"userName": {
"S": "$input.path('$.userName')"
}
}
}
when i try to run the test with a Request Body as follows:
{
"pageId": "breaking-news-story-01-18-2016",
"userName": "cacevedo"
}
I get the following information:
Request: /comments
Status: 200
Latency: 80 ms
Response Body
Response Body:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html
xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head>
<title>Page Not Found</title> </head> <body>Page Not Found</body>
</html>
Can someone help me out with this, because i am not able to delete the item. yet it seems that the integration request is working. Am i missing something or doing something wrong?

Just in case anyone runs into the same problem. the HTTP verb needs to be a post and not delete and also is case sensitive so instead of using deleteItem. use DeleteItem

Related

HTML returned from Lambda#Edge and CloudFront throws JSON is not parsable

I'm trying to set up a reverse proxy on a subfolder of our domain (example.com/sub) to show content from a site sitting on a different domain/server. I would like to access that site on the same domain subfolder for SEO purposes.
I have a CloudFront distribution set up for the whole domain. On the same distribution, I have an additional behavior /sub* Viewer request linked to a Lambda#Edge function.
In that lambda function, I'm returning some HTML. For testing, I'm using code straight from AWS docs.
'use strict';
const content = `
<\!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Simple Lambda#Edge Static Content Response</title>
</head>
<body>
<p>Hello from Lambda#Edge!</p>
</body>
</html>
`;
exports.handler = (event, context, callback) => {
/*
* Generate HTTP OK response using 200 status code with HTML body.
*/
const response = {
status: '200',
statusDescription: 'OK',
headers: {
'cache-control': [{
key: 'Cache-Control',
value: 'max-age=100'
}],
'content-type': [{
key: 'Content-Type',
value: 'text/html'
}]
},
body: content,
};
callback(null, response);
};
This works as expected. But when I try to return a more complex HTML for example
exports.handler = async (event, context, callback) => {
console.log(event);
const response = await fetch('https://www.nbcnewyork.com');
const content = await response.text();
const res = {
status: '200',
statusDescription: 'OK',
headers: {
'cache-control': [{
key: 'Cache-Control',
value: 'max-age=100'
}],
'content-type': [{
key: 'Content-Type',
value: 'text/html'
}]
},
body: content,
};
callback(null, res);
};
When I GET the example.com/sub I'm getting the error below.
502 ERROR
The request could not be satisfied.
The Lambda function returned invalid JSON: The JSON output is not parsable. We can't connect to the server for this app or website at this time. There might be too much traffic or a configuration error. Try again later, or contact the app or website owner.
If you provide content to customers through CloudFront, you can find steps to troubleshoot and help prevent this error by reviewing the CloudFront documentation.
Generated by cloudfront (CloudFront)
Request ID: *
In CloudWatch for CloudFront/LambdaEdge I'm also seeing these events:
2022-04-08T12:31:50.000+02:00 (2k-5BGOaTxD_Ll51dVhJbBRN0rQY4yjju4k9TEPQcpgzl1MZa-vYAA==) INFO START processing EventType: viewer-request LambdaFunctionARN: arn:aws:lambda:eu-central-1:*:1 Method: GET URI: /sub
2022-04-08T12:31:50.000+02:00 (2k-5BGOaTxD_Ll51dVhJbBRN0rQY4yjju4k9TEPQcpgzl1MZa-vYAA==) INFO Invoking LambdaFunctionARN: arn:aws:lambda:eu-central-1:*:1 Region: eu-central-1
2022-04-08T12:31:50.000+02:00 (2k-5BGOaTxD_Ll51dVhJbBRN0rQY4yjju4k9TEPQcpgzl1MZa-vYAA==) INFO Response received from Lambda: RequestId: *
2022-04-08T12:31:50.000+02:00 (2k-5BGOaTxD_Ll51dVhJbBRN0rQY4yjju4k9TEPQcpgzl1MZa-vYAA==) ERROR Validation error: The Lambda function returned an invalid json output, json is not parsable.
2022-04-08T12:31:50.000+02:00 (2k-5BGOaTxD_Ll51dVhJbBRN0rQY4yjju4k9TEPQcpgzl1MZa-vYAA==) INFO END processing EventType: viewer-request LambdaFunctionARN: arn:aws:lambda:eu-central-1:*:f
Any ideas about what I'm doing wrong?
Regards, Primoz

Create MappingTemplate for DynamoDb GetItem to retrieve by primary key through Api Gateway

I am getting an http 404 from dynamodb, and page not found web page when I make this call.
I am using API gateway to interface directly with DynamoDB.
{
"TableName": "mytable",
"Key" :{
"IntakeId" :{
"S": "$input.params('intakeId')"
}
}
}
I run my test and I get this in the logs:
Mon Sep 27 00:45:44 UTC 2021 : Endpoint request body after transformations: {
"TableName": "mytable",
"Key" :{
"IntakeId" :{
"S": "BA22df4"
}
}
}
I am able to search this way:
this is my actual response:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>Page Not Found</title>
</head>
<body>Page Not Found</body>
</html>
I am able to save items, just not retrieve them.
I get the same result when I add the sort key:
{
"TableName": "mytable",
"Key": {
"IntakeId": {
"S": "BA22df4"
},
"Email" : {
"S": ""
}
},
"ReturnConsumedCapacity": "TOTAL"
}

Google Cloud Run Authentication Service-to-Service

I have two services (APIs) deployed on GCP Cloud Run. Call them service-one.myDomain.com and service-two.myDomain.com. I would like service-one to be authenticated in calling service-two independently of what any user is doing.
I've read and implemented the instructions from GCP Cloud Run docs on Authenticating service-to-service (https://cloud.google.com/run/docs/authenticating/service-to-service) but service-one.myDomain.com is unsuccessful in calling service-two.myDomain.com receiving a 401:Unauthorized response.
Any thoughts on how to get service-one to successfully call service-two?
Here's my setup:
IAM and service accounts:
On google IAM, I created two service accounts and granted them both the "Cloud Run Invoker" (roles/run.invoker) role:
service-one#myproject.iam.gserviceaccount.com
service-two#myproject.iam.gserviceaccount.com
Inside Cloud Run I changed the service account from the "Default compute service account" to the service accounts I created. I assigned service-one#myproject.iam.gserviceaccount.com for service-one.myDomain.com and service-two#myproject.iam.gserviceaccount.com for service-two.myDomain.com
OIDC Auth token:
In service-one.myDomain.com I make a call to the metadata server to get a token (jwt) from the following url:
http://metadata/computeMetadata/v1/instance/service-accounts/default/identity?audience=https://service-two.myDomain.com with a request header set as {'Metadata-Flavor': 'Google'} The request is successful and the token I receive is decoded to have the following payload:
{
"alg": "RS256",
"kid": "9cef5340642b157fa8a4f0d874fe7543872d82db",
"typ": "JWT"
}
{
"aud": "https://service-two.mydomain.com",
"azp": "100959068407876085761",
"email": "service-one#myproject.iam.gserviceaccount.com",
"email_verified": true,
"exp": 1572806540,
"iat": 1572802940,
"iss": "https://accounts.google.com",
"sub": "100953168404568085761"
}
Http request:
Using the token I make a request from service-one.myDomain.com to an http endpoint on service-two.myDomain.com. I set the request header with {'Authorization': 'Bearer {token}'} ({token} is value of token).
Http Response:
The response is a 401 Unauthorized and my logs show the response headers to include:
{'WWW-Authenticate': 'Bearer error="invalid_token" error_description="The access token could not be verified"'}
With a content of:
"
<html><head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title>401 Unauthorized</title>
</head>
<body text=#000000 bgcolor=#ffffff>
<h1>Error: Unauthorized</h1>
<h2>Your client does not have permission to the requested URL <code>/health</code>.</h2>
<h2></h2>
</body></html>
"
I'm stumped.... any ideas on what I'm missing to get service-one to authenticate to service-two?
The answer was to use the gcp cloud run generated Url as the audience in the OIDC token request. And relatedly the "aud" field in the jwt.
My discovery was that Service-to-Service authentication in cloud run does not support custom domains (myDomain.com). I was using my custom domain.
(I feel like a bonehead) thanks #guillaumeblaquiere

AWS GET request with body rejected by CloudFront

I'm build an API using AWS SAM (Lambda & API Gateway) whose contract is defined by a 3rd party.
The 3rd party calls my API with a GET request that contains JSON in the body. However, when a request is sent to the API with a body it gets rejected by CloudFront.
This is the request:
curl -X GET -H "Content-Type: application/json" --data '{"hello":"world"}' https://my-api.execute-api.us-east-2.amazonaws.com/Prod/my-api
This is the response:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
<TITLE>ERROR: The request could not be satisfied</TITLE>
</HEAD>
<BODY>
<H1>403 ERROR</H1>
<H2>The request could not be satisfied.</H2>
<HR noshade size="1px">
Bad request.
<BR clear="all">
<HR noshade size="1px">
<PRE>
Generated by cloudfront (CloudFront)
Request ID: 1p0St_-e3noQL-2uMxeB_2I6lkMr1mg5afvxJRmVpCdnG67Vgnhj9w==
</PRE>
<ADDRESS></ADDRESS>
</BODY>
</HTML>
Checking the logs, the request never hits API Gateway or the Lambda function. However, if I remove the body from the request, then it hits the Lambda function and I get the appropriate error message from the API (telling the caller that the expected body is missing.)
curl -X GET -H "Content-Type: application/json" https://my-api.execute-api.us-east-2.amazonaws.com/Prod/my-api
I'm using basic configuration of API Gateway via a SAM template. This is the relevant section:
MyApiFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: bin/main.zip
Handler: main
Runtime: go1.x
Tracing: Active
Role: !Sub ${MyApiLambdaExecutorRole.Arn}
Events:
CatchAll:
Type: Api
Properties:
Path: /my-api
Method: GET
GET requests cannot contain a request body on CloudFront. Try using POST instead.
If you want to send limited data in a GET request, you can use query parameters.
You can see in the AWS Docs that this isn't possible here:
https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/RequestAndResponseBehaviorCustomOrigin.html#RequestCustom-get-body
If a viewer GET request includes a body, CloudFront returns an HTTP status code 403 (Forbidden) to the viewer.
You could perhaps use an EC2 instance or other service that doesn't use API Gateway to handle the request.

Can GET but not POST to a Lambda via API Gateway. Why?

Below is the request the client makes. The GET request is a success, but the POST request gives an error.
fetch('api/public/libraries/sign-out-discourse', {
method: 'GET', // or 'POST'
headers: new Headers([
['Accept', 'application/json'],
['Content-Type', 'application/json'],
['Authorization', jwtToken],
]),
})
Here is the error from the POST request:
HTTP/1.1 403 Forbidden
Server: CloudFront
Date: Fri, 05 Oct 2018 08:50:14 GMT
Content-Type: text/html
Content-Length: 694
Connection: keep-alive
X-Cache: Error from cloudfront
Via: redacted
X-Amz-Cf-Id: redacted
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<HTML><HEAD><META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
<TITLE>ERROR: The request could not be satisfied</TITLE>
</HEAD><BODY>
<H1>403 ERROR</H1>
<H2>The request could not be satisfied.</H2>
<HR noshade size="1px">
This distribution is not configured to allow the HTTP request method that was used for this request. The distribution supports only cachable requests.
<BR clear="all">
<HR noshade size="1px">
<PRE>
Generated by cloudfront (CloudFront)
Request ID: redacted
</PRE>
<ADDRESS>
</ADDRESS>
</BODY></HTML>
What I have tried
In CloudFront I have chosen behaviors -> edit -> Allowed HTTP Methods: GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE. I have invalidated caches since making this chance in the unlikely case that AWS stores settings in the CloudFront cache somehow.
In API Gateway graphical, I have enabled CORS for the resource. I have redeployed the API since making this change.
Other debugging
curl -X POST API_ENDPOINT returns the same error as calling the endpoint with POST from my application.
If you are using Lambda Proxy integration, you need to provide the CORS header in the response, API Gateway will not do that for you for a POST request.
var response = {
statusCode: 200,
headers: {
"Access-Control-Allow-Origin" : "*",
"Access-Control-Allow-Credentials" : true
},
body: JSON.stringify({"message":"Success"})
}
callback(null, response);