Using AWS Lambda and API Gateway to serve static javascript? - node.js

I am writing a third party widget that executes javascript client side to add a button to the users site. Essentially, the user will include a tag on their site that includes a path to my widget. The URL for that path will include the app_id for the particular user calling the widget. For example
<script src="www.widget.com/widget/{USER_ID}">
I want to be able to use AWS Lambda and API Gateway to do some quick authentication that the user is allowed to download the widget and serve the javascript content. This is super simple with something like res.sendFile in Express.js, but API Gateway doesn't seem to support sending a file. Is there any way that I can use API Gateway to serve the javascript quickly, without having to stringify the entire file?

You can setup api gateway to proxy all requests to s3 which hosts the file (S3 proxy example). Otherwise you can setup a Http Proxy integration and then run a backend http server that serves the entire file.
To perform authentication on the request you can execute a lambda function as a custom authorizer
If you have a lambda integration type, the only was is to return the entire file in the response using callback lambda documentation for callback.

First, you can respond something like this in your Lambda function:
{
statusCode: 200,
headers: {
"content-type": "text/javascript"
},
body: buildMyJavascriptFileInTextFormat(event.user_id),
isBase64Encoded: false,
}
Then go to your API Gateway resource and set up the integration following these steps:
Method Request:
Add a query string called user_id.
Integration Request:
{
"user_id": "$input.params('user_id')"
}
Integration Response
Go to Status code: 200, create a Mapping template called: text/javascript and set up this:
$input.path('body')
Method Response
In the box of Response Body for 200 change the Content-Type to text/javascript.

Related

Nodejs server - how can I intercept a 3rd party request and forward it the request to a proxy (rewrite request)?

Scenario
I have nodejs app is deployed on our infrastructure at work
At some point in the login / auth process of my app, my app will make a POST request to https://login.microsoftonline.com for authenticating the user (remix-auth-microsoft library is the one making the POST request)
My POST request to login.microsoft.com fail because our servers at work are not allowed to make external calls to the internet, unless we make our requests through a proxy (lets call it: example-proxy.com)
Question
How would I intercept the HTTP POST call that the 3rd party library is making in my nodejs app and proxy that request to example.proxy.com. Essentially I want login.microsoft.com to think the POST request is coming from example-proxy.com. Then I want the proxy server to return that data to my app
Are there specific headers I need to pass to make this work?
Some pseudo code of what I'm trying to do:
someInterceptor.on(request, (request) => {
if(outgoingRequest === 'https://login.microsoftonline.com') {
// somehow re-write the request so login.microsoftonline.com thinks the POST is coming from example-proxy.com
}
})

API Gateway doesn't pass headers to Lambda

I am trying to make a multipart http query through API Gateway to a Lambda. I have set the binary media type to 'multipart/form-data'. The issue is that I need to access the Content-Type header, so that I can parse the body correctly in my lambda logic.
But when I console log the lambda event, I can see that the Content-Type header is missing, even though I can see that it is well sent by my client.
Also, everything works perfectly in local with serverless-offline, without going through API Gateway.
So I'm guessing API Gateway does its own processing of my request and send a different version of it to my Lambda, with different headers.
How can I force API Gateway to pass this header to my Lambda ?
Thank you very much!
In the integration request try adding an HTTP Header Content-Type mapped from application/x-www-form-urlencoded.
Then add a mapping template for multipart/form-data
{
"Content-Type" : "$input.params('Content-Type')",
"body_base64" : "$util.base64Encode($input.body)"
}
If you are using an OpenAPI template to create the api with terraform or serverless then configure the x-amazon-apigateway-integration: node
requestTemplates:
multipart/form-data: "{\n \"Content-Type\"\
\ : \"$input.params('Content-Type')\",\n \"body_base64\" : \"$util.base64Encode($input.body)\"\
\n}"
passthroughBehavior: "never"
type: "aws"

How to avoid headers passed back to API Gateway from Lambda via Serverless framework being remapped?

I'm hitting an issue when I try to respond in a Serverless Lambda function with the WWW-Authenticate challenge header.
I am setting in my response
{
statusCode: 401,
headers: {
'WWW-Authenticate': 'Basic realm="My realm"',
},
body: "",
}
The function runs successfully but in the response I don't have a WWW-Authenticate header; instead, I have an x-amzn-Remapped-WWW-Authenticate header.
How can I have this header passed through verbatim?
I read the docs about passing custom response headers, but it seems that these need to be set in serverless.yml. Since these response headers need to be dynamic based on the request headers I don't think that will help me.
The function is the default lambda-proxy type.
By default The Serverless Framework uses the Lambda Proxy Integration method. This does lots of the heavy lifting for you, but also gets in the way.
If you want a more transparent experience you need to use Lambda Integration, and handle the response yourself. Though you'll need to get more hands on, including CORS response headers.
integration: lambda
You can read more about the two methods here: https://serverless.com/framework/docs/providers/aws/events/apigateway/#lambda-integration

Request method not recognized after promisification

I built an API using AWS API Gateway and Lambda, now I am writing end to end tests, I am using Promise from bluebird and request, so I promisified request like this:
Promise.promisifyAll(require('request'));
Promise.promisifyAll(request);
Now when I make requests (POST, PUT, GET), using request.methodAsync, the method is not recognized by the API Gateway !
I launched Jasmine with :
NODE_DEBUG=request jasmine
I can see the method = 'POST' or whatever, but the API still not recognize the method of the requests I am making with the promisified request ! any one run into this situation ?
Hi I'm from the Api Gateway team. As long as the request is sent to a valid resource path / HTTP method pair on a deployed API, Api Gateway will accept it. Please note you'll need to put the stage name as the first path part in the URI (see example in the Api Gateway console on the Stages page).
If you're invoking the right API resource, the issue sounds like a client-side bug.
Jack

Gzipped response in AWS Lambda -> API Gateway

I can't seem to get a gzipped response from Lambda through the API Gateway.
I'm gzipping my response in Lambda and setting the "Content-Encoding" header in API Gateway.
I'm not sure which part is the problem.
Here's the final return from Lambda to API Gateway:
zlib.gzip(myJsonString, function (err, buffer) {
if ( err ) { return handleError(err, context) }
return context.succeed(buffer.toString('binary'));
});
I've tried just passing the buffer, base64 encoding it, etc.
Making a GET request from Chrome:
If I remove the Content-Encoding header from the gateway I get binary/base64/buffer array as a string response in the browser.
If I set the header, the GET request fails entirely with no response but testing in the AWS console returns the payload with quotes around it.
I don't know what's going on here but If Amazon actually wants people to use this thing we need to be able to compress our responses. Seems like it should just be a checkbox in API Gateway and then I could simply return a JSON string from Lambda and have it zipped up automatically.
As for Nov 17, 2016 - Binary Data Now Supported by API Gateway
Let me know if you figured that out!

Resources