Cloudfront as a reverse proxy for backend - aws-lambda

I have tried to put in place a CloudFront distribution that would forward requests using a CloudFront function to our external API GW (not the AWS one). However this creates an issue with CORS. I can make the CORS request working, however what I am trying to replace is a backend for frontend pattern that we have in place currently using an Apache server and single origin cookie.
function handler(event) {
var request = event.request;
var headers = request.headers;
var apigwurl = 'https://gatewayendpoint/'
if (request.uri.startsWith('/api')) {
request.uri = request.uri.replace('/gw/', '');
var response = {
statusCode: 302,
statusDescription: 'Found',
headers: {
"location": { "value": apigwurl+request.uri}
}
}
return response;
}
return request;
}
In essence what I am trying to do is replace in the most effective way the following rewrite function
RewriteRule ^/api/(.*)$ https://api.backend.com/$1 [P,L]
Once the first request is done, the single cookie is set and used for authentication purposes to make calls to the BFF layer (Backend for Frontend).

NGINX would be a good fit for a reverse proxy for backend and can be used as an API gateway.
I would suggest to use a solution/technology combination which is widely used. That way, you will not make your life difficult with rare technology combination and find better technical support and resources online.

Related

How to provide own follow redirect strategy?

I'm interacting with a webservice which on POST request answers with 302 containing address to created resource in the location header. To access the created resource I've to make a GET request to the provided location.
I want reactor.netty.http.client.HttpClient to handle the redirect flow for me.
This is my configuration:
import reactor.netty.http.client.HttpClient;
...
...
var nettyHttpClient = HttpClient.create()
.compress(true)
.followRedirect(true);
With the above configuration, the client will use same HTTP method for the redirected request as it did for the first request.
Given my use-case, is there a way to provide my own redirect strategy to the client for 3xx responses?
You could handle raw response and handle http status 302 with custom logic
var response = nettyHttpClient.post()
.uri("/test")
.response()
.flatMap(res -> {
if (res.status().equals(HttpResponseStatus.FOUND)) {
return nettyHttpClient.get()
.uri(res.responseHeaders().get(HttpHeaders.LOCATION))
.response();
}
return Mono.just(res);
});

SignalR and OpenId Connect

I have a server which uses ASP.NET Core Web Api and OpenIddict as authorization framework. Now I've added an SignalR host and want to add authorisation to it.
From different sources I found that SignalR (JS Client) wants that you send the access token in the querystring or by cookie as websockets don't support headers.
As the authentication middleware doesn't check the querystring or cookie container for an authorization entry I need to implement such an provider/retriever/resolver which reads this value by myself.
I've found a solution for IdentityServer but nothing about OpenIddict.
Where/How do I implement such an token resolver with OpenIddict?
If you use JwtBearerAuthentication then you can use OnMessageReceived to set token:
Events = new JwtBearerEvents()
{
OnMessageReceived = async (ctx) =>
{
ctx.Token = ctx.Request.Query["<qs-name>"];
}
}
Or if you use IdentityServerAuthentication then you can use TokenRetriever(not tested but it should be something like this):
TokenRetriever = (ctx) =>
{
return ctx.Request.Query["<qs-name>"];
}
Just like #adem-caglin mentioned, in IdentityserverAuthentication you use TokenRetriever and can go with the built-in functions if what you're after is the standard bearer header or a query string
TokenRetriever = (request) =>
{
// by default calls TokenRetrieval.FromAuthorizationHeader()(request);
// check if request is to signalr endpoint and only then apply FromQueryString
return TokenRetrieval.FromQueryString()(request);
}

Amazon CORS-enabled api returns no 'Access-Control_allow_Origin' header

After setting up Amazon API Gateway CORS as instructed, I still get the following error when send an Ajax POST request.
XMLHttpRequest cannot load https://-------.execute-api.us-west-2.amazonaws.com/--------. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://------.s3-website-us-west-2.amazonaws.com' is therefore not allowed access. The response had HTTP status code 400.
I'm using Amazon S3 to host the website, which does not support web script so I can't use python or php to fix this.
I'd really appreciate any help.
Could it be that you're using Lambda-proxy integration and your Lambda is not returning those headers? If that's the case, you have to add those headers yourself.
This is how I use to create the response that I return using callback(null, response).
function createResponse(statusCode, body) {
const headers = {
'Access-Control-Allow-Origin': '*',
}
return {
headers,
statusCode,
body: body ? JSON.stringify(body) : undefined,
}
}

SailsJS - using sails.io.js with JWT

I have implemented an AngularJS app, communicating with Sails backend through websockets, using sails.io.js.
Since the backend is basically a pure API and will be connected to from other apps as well, I'm trying to disable sessions completely and use JWT.
I have set up express-jwt and can use regular HTTP requests quite nicely, but when I send a request through sails.io.js, nothing happens at all - websocket request keeps pending on the client, and there's nothing happening on the server (with "silly" log level).
I've tried patching sails.io.js to support the query parameter, and when connecting, I send the token from Angular, but in the best case, I get a response with error message coming from express-jwt saying credentials are missing...
I've also seen some hints that socket.js in sails needs to be modified with beforeConnect, I've seen socketio-jwt, but have no idea where and how to plug that in, in Sails.
Has anyone implemented this and is using JWT with Sails and sockets? I'd appreciate any kind of hint in what direction to go :)
I realised that policy I've put in place and that was using express-jwt abstracted too much away from me, so I didn't figure out what exactly was happening. Once I looked at other examples, I've figured out that I only needed to check what's different for websocket requests than regular, and I quickly found a way around the problem.
So:
set up token signing and sending on login
Angular takes the token and saves to local storage
Create an interceptor for HTTP requests to add authorization header and token
Fix up sails.io.js to forward query parameters provided through options (as mentioned in the question)
When connecting using sails.io.js, send token as query parameter, i.e. url + '?token=' + token
In sails policy, check all combinations for token, including req.socket.handshake.query, as below:
module.exports = function (req, res, next) {
var token;
if (req.headers && req.headers.authorization) {
var parts = req.headers.authorization.split(' ');
if (parts.length == 2) {
var scheme = parts[0],
credentials = parts[1];
if (/^Bearer$/i.test(scheme)) {
token = credentials;
}
} else {
return res.json(401, {err: 'Format is Authorization: Bearer [token]'});
}
} else if (req.param('token')) {
token = req.param('token');
// We delete the token from param to not mess with blueprints
delete req.query.token;
}
// If connection from socket
else if (req.socket && req.socket.handshake && req.socket.handshake.query && req.socket.handshake.query.token) {
token = req.socket.handshake.query.token;
} else {
sails.log(req.socket.handshake);
return res.json(401, {err: 'No Authorization header was found'});
}
JWTService.verifyToken(token, function (err, token) {
if (err) {
return res.json(401, {err: 'The token is not valid'});
}
sails.log('Token valid');
req.token = token;
return next();
});
};
It works well! :)

Call Play 2 REST API with AngularJS (CORS Problems)

I am developing an AngularJS application calling a REST API developed with Play Framework 2.2.0.
I have a problem related to Cross-domain ajax calls as the Angular application and the Play one will not be hosted on the same domain.
Here is the JS call in my Angular service :
$http
.post("http://localhost:9001/category/list", { langCode: 'fr-FR' })
.success(function(data, status, headers, config) {
callback(data.items);
})
.error(function(data, status, headers, config) {
console.log("Error Data : " + data);
console.log("Error Status : " + status);
});
Here is the route in my Play app :
POST /category/list controllers.catalog.ProductCategoryController.list()
If I don't send any data in the request, everything works fine
If I send data, I have Ajax errors concerning ACCESS_CONTROL_ALLOW_ORIGIN, ACCESS_CONTROL_ALLOW_HEADERS
The only workaround I have is the following :
Intercept all requests in Global class and add the headers
#Override
public Action onRequest(Request request, Method method) {
return new Action.Simple() {
#Override
public Promise<SimpleResult> call(Context ctx) throws Throwable {
Logger.debug("Intercepting request and applying CORS headers...");
ctx.response().setHeader(Controller.ACCESS_CONTROL_ALLOW_ORIGIN, "*");
ctx.response().setHeader(Controller.ACCESS_CONTROL_ALLOW_HEADERS, "Content-Type");
return delegate.call(ctx);
}
};
}
Add another route with OPTIONS in routes
OPTIONS /category/list controllers.catalog.ProductCategoryController.list()
Is there a way of making the integration simpler than that ?
There's no CORS support out of the box in play; that's a situation I'd like to see changed, but for now you've identified a wart.
The good news is that you can manage a global workaround if you are OK having one CORS setting for all of your resources. It can be done in a couple of ways, one of which you identified. My inclination would be to go with a low level OPTIONS route.
Something like:
OPTIONS /*path controllers.Application.options()
From there, your handler definition can be something like:
Ok("").withHeaders(
"ACCESS_CONTROL_ALLOW_METHODS" -> "GET, POST, PUT, PATCH",
"ACCESS_CONTROL_ALLOW_HEADERS"->"Content-Type",
"ACCESS_CONTROL_ALLOW_ORIGIN" -> "*"
)
It's not super clean, but until Play adds something a bit more workable, I think it's your best option over making tons of OPTIONS routes (again, assuming you're comfortable with a global CORS setting)
You have to enable CORS support to your Play web server. The following url do have plenty of how-to for configurating server enabling the cross origin support:
http://enable-cors.org/server.html

Resources