Creating a expressjs proxy server between webpack and API - api

Hello i'm creating a web application using webpack, which makes REST api call to a backend server. The problem I have is CORS issues, so I will need to use a proxy.
Which leads me to how do I connect wepback-dev-server which runs on port(8080) to my api server which runs on port (7000)? Would my proxy server run same as port(8080)?
I read up on expressjs, npm node-http-proxy and webpack, but struggling to tie it all together.
I'm new to proxying.

Below a sample config for webpack-dev-server, see the proxy option
var config = {
// webpack stuff here ...
//dev server configuration
devServer: {
// ...
// every request made to 'locahost:8080/api/xxxx' will be proxyfied to 'http://localhost:7000/api/xxxx'
proxy: {
"/api/*": {
target: "http://localhost:7000",
secure: false,
rewrite: function(req, options) {
//you can handle rewrite here if you need to
}
},
}
},
//
};
module.exports = config;
As described here https://webpack.github.io/docs/webpack-dev-server.html#proxy
Hope it helps,
EDIT as for webpack-dev-server v 1.14.1 'rewrite' is still implemented

Related

Swashbuckle using port 80 for https when run behind reverse proxy

I have a .net core api documented with swagger/swashbuckle.
When running the swagger ui on localhost on url https://localhost:44390/ the "Try it out" works fine.
We have the same solution in an App service in Azure with an Azure Front Door acting as reverse proxy. Front Door only accepts https traffic and only forwards https traffic. Front door domain is widget.example.com and App service is widget-test-app.azurewebsites.net. When running the swagger ui in Azure using the url https://widget.example.com/api/index.html there are two differences compared to running in localhost:
The swagger ui is showing a Servers -heading and a dropdown
The swagger ui is showing the server url as https://widget.example.com:80
I added an endpoint in the api with the following code
return $"Host {HttpContext.Request.Host.Host} Port {HttpContext.Request.Host.Port} Https {HttpContext.Request.IsHttps}";
When requesting https://widget.example.com/api/v1/test/url it returns
Host widget-test-app.azurewebsites.net Port Https True
This is completely ok since Front door is changing the host header. Port is empty, though.
Summary: Swagger ui is showing the correct domain in the Servers -dropdown but the port number is wrong. How can I get it to either omit the port number if it's 80 or 443, or add it correctly?
Update: The problem is in the swagger.json file which behind the reverse proxy includes a servers element
"servers": [{
"url": "https://widget.example.com:80"
}]
Startup.ConfigureServices
services.AddApiVersioning(options => {
options.Conventions.Add(new VersionByNamespaceConvention());
});
services.AddVersionedApiExplorer(o => {
o.GroupNameFormat = "'v'VVV";
o.SubstituteApiVersionInUrl = true;
});
services.AddSwaggerGen(c => {
c.SwaggerDoc("v1", new OpenApiInfo {
Title = "Widget backend v1", Version = "v1"
});
c.SwaggerDoc("v2", new OpenApiInfo {
Title = "Widget backend v2", Version = "v2"
});
c.EnableAnnotations();
c.AddEnumsWithValuesFixFilters();
var xmlFile = $ "{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
c.IncludeXmlComments(xmlPath);
});
Startup.Configure
app.UseSwagger(options => {
options.RouteTemplate = "/api/swagger/{documentname}/swagger.json";
});
app.UseSwaggerUI(options => {
foreach(var description in provider.ApiVersionDescriptions) {
options.SwaggerEndpoint($ "/api/swagger/{description.GroupName}/swagger.json", "widget backend " + description.GroupName);
}
options.RoutePrefix = "api";
});
To fix this I cleared the Servers -list. Here is my code:
app.UseSwagger(options =>
{
options.RouteTemplate = "/api/swagger/{documentname}/swagger.json";
options.PreSerializeFilters.Add((swagger, httpReq) =>
{
//Clear servers -element in swagger.json because it got the wrong port when hosted behind reverse proxy
swagger.Servers.Clear();
});
});
The solution (ok, a - mine - solution :)) is to configure forward headers in Startup.
services.Configure<ForwardHeadersOptions>(options =>
{
options.ForwardHeaders = ForwardHeaders.All; // For, Proto and Host
options.KnownNetworks.Clear();
options.KnownProxies.Clear();
});
Doing this, any URL generation in the app (behind reverse proxy) should respect the port-forwarding value. According to documentation known networks should be specified (taken from docs):
Only allow trusted proxies and networks to forward headers. Otherwise, IP spoofing attacks are possible.
See ASP.NET documentation for more details.

How to handle JWT authentication with RxDB?

I have a local RxDB database and I want to connect it with CouchDB. Everything seems to works fine except for authentication. I have no idea how to add it differently then inserting credentials in database url:
database.tasks.sync({
remote: `http://${username}:${pass}#127.0.0.1:5984/tododb`,
});
I would like to use JWT auth but can't find how to add a token to sync request. I found only some solutions for PouchDB (pouchdb-authentication plugin) but can't get it working with RxDB.
RxDB is tightly coupled with PouchDB and uses its sync implementation under the hood. To my understanding, the only way to add custom headers to a remote PouchDB instance (which is what is created for you when you pass a url as the remote argument in sync), is to intercept the HTTP request:
var db = new PouchDB('http://example.com/dbname', {
fetch: function (url, opts) {
opts.headers.set('X-Some-Special-Header', 'foo');
return PouchDB.fetch(url, opts);
}
});
PouchDB replication documentation (sync) also states that:
The remoteDB can either be a string or a PouchDB object. If you have a fetch override on a remote database, you will want to use PouchDB objects instead of strings, so that the options are used.
Luckily, RxDB's Rx.Collection.sync does not only accept an server url as the remote argument, but also another RxCollection or a PouchDB-instance.
RxDB even reexport the internally used PouchDB module, so you do not have to install PouchDB as a direct dependency.
import { ..., PouchDB } from 'rxdb';
// ...
const remotePouch = new PouchDB('http://27.0.0.1:5984/tododb', {
fetch: function (url, opts) {
opts.headers.set('Authorization', `Bearer ${getYourJWTToken()}`)
return PouchDB.fetch(url, opts);
}
})
database.tasks.sync({
remote: remotePouch,
});

.Net core2 CORS - How SetIsOriginAllowed works?

Want to allow my API to be accessed from different sites. For this had:
services
.AddCors(options =>
{
options.AddPolicy(PolicyName, builder =>
{
builder
.SetIsOriginAllowedToAllowWildcardSubdomains()
.WithOrigins(
"http://*.my-api.com",
"http://*.my-api.service"
)
...
This doesn't seem to allow httpS or when I specify the port in the request.
Ex.:
https://www.my-api.com:3000
Thought could replace the WithOrigins with SetIsOriginAllowed()
services
.AddCors(options =>
{
options.AddPolicy(PolicyName, builder =>
{
builder
.SetIsOriginAllowed(IsOriginAllowed)
where IsOriginAllowed function is defined as:
private static bool IsOriginAllowed(string host)
{
var corsOriginAllowed = new[] { "my-api.com", "my-api.service" };
return corsOriginAllowed.Any(origin =>
Regex.IsMatch(host, $#"^http(s)?://.*{origin}(:[0-9]+)?$", RegexOptions.IgnoreCase));
}
but this doesn't work at all, even the regular expression is returning true when I want.
Does anyone know why this doesn't work and can show me the right way to allow httpS (besides duplicating all the domains in WithOrigins() with httpS and different ports.
Thanks
SetIsOriginAllowed() does work. Was testing with Postman and as was told, Postman doesn't care about headers returned from the server. It's the browser who enforces the Cors headers.
To test properly created a little html page under a test site with below javascript
<html>
<script>
fetch('http://test.com:5000/v2/campaign/hallo3').then(function(response) {
return response.json();
}).then(function(j) {
alert(JSON.stringify(j));
});
</script>
</html>
when domain is NOT included in the Cors allowed list browser doesn't display the returned values from API
After adding test domain to allowed domains list browser display the data and get the content Cors headers
Another problem was that with just the SetIsOriginAllowed() server was not sending the 'Vary' header. Had to set both:
.SetIsOriginAllowed(IsOriginAllowed)
.WithOrigins(corsOriginAllowed)
23/12/2022
For anyone struggling with this in NET CORE 7 try this on Program.cs:
Add the variable:
...
var MyHosts = "myHosts";
var builder = WebApplication.CreateBuilder(args);
...
Add the new CORS policy:
if (builder.Environment.IsDevelopment())
{
///Add a CORS policy to allow certain hosts
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyHosts,
policy =>
{
policy.AllowAnyOrigin().WithOrigins("http://localhost:59028").AllowAnyHeader().AllowAnyMethod();
});
});
}
U have to add the AllowAnyHeader and AllowAnyMethod or u'll get another pre-flight error.
Don't forget to add the new policy below:
app.UseCors(MyHosts);

Apache + Symfony2 + HTTPS + Node.js + Socket.io: socket.emit not firing

I've been spending hours upon hours on this problem, but to no avail.
EDIT: solution found (see my answer)
Project background
I'm building a project in Symfony2, which requires a module for uploading large files. I've opted for Node.js and Socket.IO (I had to learn it from scratch, so I might be missing something basic).
I'm combining these with the HTML5 File and FileReader API's to send the file in slices from the client to the server.
Preliminary tests showed this approach working great as a standalone app, where everything was handled and served by Node.js, but integration with Apache and Symfony2 seems problematic.
The application has an unsecured and secured section. My goal is to use Apache on ports 80 and 443 for serving the bulk of the app built in Symfony2, and Node.js with Socket.io on port 8080 for file uploads. The client-side page connecting to the socket will be served by Apache, but the socket will run via Node.js. The upload module has to run over HTTPS, as the page resides in a secured environment with an authenticated user.
The problem is events using socket.emit or socket.send don't seem to work. Client to server, or server to client, it makes no difference. Nothing happens and there are no errors.
The code
The code shown is a simplified version of my code, without the clutter and sensitive data.
Server
var httpsModule = require('https'),
fs = require('fs'),
io = require('socket.io');
var httpsOptions =
{
key: fs.readFileSync('path/to/key'),
cert: fs.readFileSync('/path/to/cert'),
passphrase: "1234lol"
}
var httpsServer = httpsModule.createServer(httpsOptions);
var ioServer = io.listen(httpsServer);
httpsServer.listen(8080);
ioServer.sockets.on('connection', function(socket)
{
// This event gets bound, but never fires
socket.on('NewFile', function(data)
{
// To make sure something is happening
console.log(data);
// Process the new file...
});
// Oddly, this one does fire
socket.on('disconnect', function()
{
console.log("Disconnected");
});
});
Client
// This is a Twig template, so I'll give an excerpt
{% block javascripts %}
{{ parent() }}
<script type="text/javascript" src="https://my.server:8080/socket.io/socket.io.js"></script>
<script type="text/javascript">
var socket = io.connect("my.server:8080",
{
secure: true,
port: 8080
});
// Imagine this is the function initiating the file upload
// File is an object containing metadata about the file like, filename, size, etc.
function uploadNewFile(file)
{
socket.emit("NewFile", item);
}
</script>
{% endblock %}
So the problem is...
Of course there's much more to the application than this, but this is where I'm stuck. The page loads perfectly without errors, but the emit events never fire or reach the server (except for the disconnect event). I've tried with the message event on both client and server to check if it was a problem with only custom events, but that didn't work either. I'm guessing something is blocking client-server communication (it isn't the firewall, I've checked).
I'm completely at a loss here, so please help.
EDIT: solution found (see my answer)
After some painstaking debugging, I've found what was wrong with my setup. Might as well share my findings, although they are (I think) unrelated to Node.js, Socket.IO or Apache.
As I mentioned, my question had simplified code to show you my setup without clutter. I was, however, setting up the client through an object, using the properties to configure the socket connection. Like so:
var MyProject = {};
MyProject.Uploader =
{
location: 'my.server:8080',
socket: io.connect(location,
{
secure: true,
port: 8080,
query: "token=blabla"
}),
// ...lots of extra properties and methods
}
The problem lay in the use of location as a property name. It is a reserved word in Javascript and makes for some strange behaviour in this case. I found it strange that an object's property name can't be a reserved word, so I decided to test. I had also noticed I was referencing the property incorrectly, I forgot to use this.location when connection to the socket. So I changed it to this, just as a test.
var MyProject = {};
MyProject.Uploader =
{
location: 'my.server:8080',
socket: io.connect(this.location,
{
secure: true,
port: 8080,
query: "token=blabla"
}),
// ...lots of extra properties and methods
}
But to no avail. I was still not getting data over the socket. So the next step seemed logical in my frustration-driven debugging rage. Changing up the property name fixed everything!
var MyProject = {};
MyProject.Uploader =
{
socketLocation: 'my.server:8080',
socket: io.connect(this.socketLocation,
{
secure: true,
port: 8080,
query: "token=blabla"
}),
// ...lots of extra properties and methods
}
This approach worked perfectly, I was getting loads of debug messages. SUCCESS!!
Whether it is expected behaviour in Javascript to override (or whatever is happening here, "to misuse" feels like a better way of putting it to me right now) object properties if you happen to use a reserved word, I don't know. I only know I'm steering clear of them from now on!
Hope it helps anyone out there!

Detecting if someone is logged into Meteor from a regular Node.js application

Is there a way for me to check to see if someone is logged into Meteor from outside of Meteor; for example, from an Express.js application? I would like to know from the Express app who the currently logged in user is on a particular client so that if the API were called, we would know who to apply the results of the API call to.
So this is best done it two parts.
A method to check whether the user is online in meteor
You can probably do it with a meteor smart package (community package repo) : https://github.com/erundook/meteor-profile-online
Make sure you have meteorite, installed via npm install meteorite -g
In your package repo use : mrt add profile-online
Accessing meteor's data using Express
To access the stuff in Express you would need a DDP client, I know this one works with pre1 (The version of DDP with Meteor 0.57+): https://github.com/EventedMind/node-ddp-client
You can have a method that checks for you in meteor
Server js (Meteor)
Meteor.methods({
'isonline: function(id) {
return Meteor.users.find(id).profile.online;
}
}
Express:
var client = new DDPClient({
host: "localhost",
port: 3000
});
userid = '1' //The user _id of the person you want to check
client.connect(function () {
console.log("Connected to Meteor at localhost:3000");
client.call("isonline", [userid], function(err,result) {
client.close();
if(!err) {
if(result) {
console.log("User " + userid + " is online");
}
else
{
console.log("That user isn't online");
}
}
else
{
console.log(err)
}
});
});