How to authenticate in websockets, using node and express-ws - node.js

I am using node 10.16.0, express 4.0.0 and express-ws module . In my normal routes that I use REST, I am using a middleware to check if there is a header with a token and validate that token.
How do I do the same thing for websockets?
I cannot just add a header to ws and pass it to the node route, even though express-ws allows to easily create express-style routes.
Currently, I am using an http server to create a ws server and then seperating different express routes
//app.js
const app = express();
const wsHttpServer = http.createServer();
wsHttpServer.listen(5001);
const expressWs = require('express-ws')(app , wsHttpServer);
app.use(cors());
app.use(express.static(path.join(__dirname,'public')));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended:false}));
app.use(express.json());
app.use(express.urlencoded({extended:false}));
app.use('/ws', require('./routes/wsroute'));
app.use('/login', require('./routes/login'));
const port = process.env.PORT || 4000;
app.listen(port, ()=>{
console.log('server runs on port ', port);
});
exports.expressWs = expressWs;
and then for the ws routes, according to the express-ws examples
//wsroute.js
router.ws('/users/:name/:date/:time', /*token.validate(),*/ (ws, req) => {
const name = req.params.name;
const date = req.params.date;
const time = req.params.time;
console.log('ws route');
console.log('ws route',name, date, time);
});
If I un-comment the token.validate() part, this will never control log because there is no header with a token.
How can I add a header to ws, like I do in REST, so it can be checked automatically? I guess that if the ws server is initialized using an http server and the ws route also includes a req, I can somehow do it?
Or is there another method?
Thanks

duplicate of Websockets token authentication using middleware and express in node.js . Follow the example there.
Listen to the wsHttpServer.on('upgrade') event and perform authentication of your choice there. If you wish you can also append a header on the upgrade eventbefore it reaches your route.

Related

How to build server.js express server around a remote github page JSON object

I have a GitHub page with one file in it: model.json. In model.json is a JSON object. I'd like to be able to access that object via a fetch request triggered in the frontend of my app.
However, I have only done mock localhost express server.jses thus far, and I don't know how to point the server.js file at a remote server.
That is, typically what I have done is something like this:
const express = require("express");
const bodyParser = require("body-parser");
const CORS = require("cors");
const app = express();
app.use(bodyParser.json());
app.use(CORS());
let modelObj = modelJSON;
// ^this "modelJSON" would be the json object in question,
// whether accessed remotely or locally.
app.get("/model.json", (req, res) => {
res.send(modelObj);
});
app.listen(5000, () => {
console.log("Server listening on port 5000");
});
I'm wondering how to make this listen to my https://<my-github-username>.github.io page instead of port 5000.
(Incidentally, will I want this server.js file in my local project/app file or in the github page root directory?)

My React front-end is unable to call my node-express backend server, the fullstack app is deployed in heroku

const port = process.env.PORT || ("http://localhost:3002")
const postUrl=`${port}/post`;
addData=()=>{
console.log(postUrl)
console.log(process.env,"AS")
Axios.post(postUrl,this.state.form)
.then((response)=>{
this.setState({errorMessage:"",successMessage:response.data})})
.catch((err)=>{
this.setState({successMessage:"",errorMessage:err.response.data.message})
})
}
When I am calling the backend in the production process.env.PORT is blank.
(process.env.NODE_EV= production which is absolutely correct exactly like in the backend)
My backend is completely fine as it getting the process.env.PORT correctly.
But my frontend is not getting the process.env.PORT that's why it keeps calling the other address("http://localhost:3002").
App will completely work fine if I keep open my local machine backend because the "http://localhost:3002"
is available to serve. But in production, Heroku keeps changing the process.env.PORT which is showing its value in the backend, not in the frontend
How can I make my frontend to call my backend server properly in production??
const express = require("express");
const app = express();
const bodyParser = require("body-parser");
const port = process.env.PORT || 3002;
const cors = require("cors");
const path=require("path");
const routing = require("./routing/route");
require('dotenv').config();
app.use(cors());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use("/",routing);
app.use(function (err, req, res, next) {
console.log("hii")
res.status(500).send({ message: err.message });
});
if(process.env.NODE_ENV ==="production"){
app.use(express.static("client/build"));
app.get("*",(req,res)=>{
res.sendFile(path.resolve((__dirname,"client","build","index.html")));
});
}
app.listen(port, () => {
console.log(` Server is started at http://localhost:${port}`);
});
server file
If your React application is served from your Node.JS application as you said, you could just use window.location. window.location is an object that stores statistics about the current page that the user is on, and you could use that to construct a URL and send the server a request, like so:
// This URL uses a template literal, which is a new feature of ES6.
// All but Internet Explorer supports it.
// This is using window.location.protocol, which is either `http:` or `https:`,
// depending on the protocol that the page was loaded with. window.location.host
// is the host that the page was loaded from, with the port number.
const postUrl =
`${window.location.protocol}//${window.location.host}/post`;
// And then requesting with the URL.
addData = () => {
console.log(postUrl);
console.log(process.env, "AS");
Axios.post(postUrl, this.state.form)
.then((response) => {
this.setState({errorMessage: "",successMessage: response.data});
})
.catch((err) => {
this.setState({successMessage: "",errorMessage: err.response.data.message});
});
}

Node Express Cors issue

I cant figure why the cors express middleware wont work. cors, express, and ejs are all saved in package.json. The app works fine if I add corsanywhere proxy on the front end but id like to work around this on the server side. any help much appreciated I've been stuck on this.
the api is in the get View/index path
the error is:
Access to fetch at 'https://api.darksky.net/forecast/' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
const express = require('express');
const app = express();
const ejs = require('ejs');
const cors = require('cors');
const PORT = process.env.PORT || 3000;
// app.use((req, res, next) => {
// res.header('Access-Control-Allow-Origin', '*')
// res.header('Access-Control-Allow-Headers', 'Origin', 'X-Requested-With')
// next();
// });
app.use(cors());
app.use(express.static(__dirname + '/Public'));
app.set('view engine', 'ejs');
app.get('/', cors(), (req, res) => {
res.render(__dirname + '/Views/index')
});
app.listen(PORT, () => {
console.log(`server is listening on ${PORT}`)
});
client side:
it works with the ${proxy} in there but id like to get rid of that
if(navigator.geolocation){
navigator.geolocation.getCurrentPosition(position => {
long = position.coords.longitude;
lat = position.coords.latitude;
var proxy = 'https://cors-anywhere.herokuapp.com/'
var api = `${proxy}https://api.darksky.net/forecast/042750f3abefefdfe2c9d43cf33ce576/${lat},${long}`;
fetch(api)
.then(response => {
return response.json();
})
.then(data => {
let {temperature, summary, icon,} = data.currently;
temperatureDegree.textContent = Math.floor(temperature);
temperatureDescription.textContent = summary;
locationTimezone.textContent = data.timezone;
setIcons(icon, document.querySelector('.icon'
w
``````
So, if you're trying to access some other service https://api.darksky.net/forecast/ (that you don't control) from your web page, then there is nothing you can do to make CORs work for that. It's up to the api.darksky.net server to decide if CORs is allowed or not. You can't change that.
You could make a request from your web page to your server to ask it to get some data from api.darksky.net for you and then return it back to your webpage (working as a simple proxy). Your server is not subject to any CORs limitations when accessing api.darksky.net. Only browsers are limited by CORs.
And, as you've found, you can also use a proxy service that enables CORs and fetches data for you.
Let's suppose you want to proxy the parts of the darksky API, you could do something simple like this:
const express = require('express');
const app = express();
const request = require('request');
const apiRouter = express.Router();
// maps /api/forecast/whatever to http://api.darksky.net/forecast/developerKey/whatever
// and pipes the response back
const apiKey = "yourAPIKeyHere";
apiRouter.get("/*", (req, res, next) => {
// parse out action and params
// from an incoming URL of /api/forecast/42.3601,-71.0589
// the /api will be the root of the router (so not in the URL here)
// "forecast" will be the action
// "42.3601,-71.0589" will be the params
let parts = req.path.slice(1).split("/"); // split into path segments, skipping leading /
let action = parts[0]; // take first path segment as the action
let params = parts.slice(1).join("/"); // take everything else for params
request({
uri: `https://api.darksky.net/${action}/${apiKey}/${params}`,
method: "get"
}).pipe(res);
});
app.use("/api", apiRouter);
app.listen(80);
Now, when you send this server, this request:
/api/forecast/42.3601,-71.0589
it will request:
https://api.darksky.net/forecast/yourAPIKeyHere/42.3601,-71.0589
and pipe the result back to the caller. I ran this test app and it worked for me. While I didn't see anything other than forecast URLs in the darksky.net API, it would work for anything of the format /api/someAction/someParams.
Note, you probably do NOT want to enable CORS on your server because you don't want other people's web pages to be able to use your proxy. And, since you're just sending requests to your own server now, you don't need CORS to be able to do that.

Allowing CORS in NodeJS

I defined my Express js app:
const express = require('express')
var history = require('connect-history-api-fallback');
const path = require('path')
const http = require('http')
const socketio = require('socket.io')
require('./db/mongoose')
const userRouter = require('./routers/user')
const publicDirPath = path.join(__dirname, '../public')
const app = express()
app.use(express.static(publicDirPath))
app.use(history({
index: '../public/index.html'
}))
app.use(express.static(publicDirPath))
app.use(express.json())
app.use(userRouter)
const server = http.createServer(app)
const io = socketio(server)
io.on('connection', socket => {
console.log('New WebSocket connection')
socket.on('join', () => {
socket.emit('message', 'Welcome to the app')
})
})
module.exports = server
Then I use it my index.js file:
const app = require('./app')
const port = process.env.PORT
app.listen(port, () => {
console.log(`Server is up on port ${port}`)
})
When I run the app and send requests from the same port - everything works just fine. But when I try to send requests from different localhost port, ie. 8080, I'm getting cross origin error.
I tried to install cors package and use it as follows:
const cors = require('cors')
app.options('*', cors())
app.use(cors());
And got the same result.
I tried to pass configuration to cors:
app.use(cors({
origin: 'http://localhost:8080'
}));
And still got the same result.
What am I doing wrong and how can I make it work?
When your frontend app tries to make the request to the express server,
The express server is blocking that request because the source of the request (i.e. frontend server) is unknown to the express server
As the request that you are trying to make it out of the domain of the express server. this is the reason where you have to tell the server please accept the request from this origin a.k.a IP Address
and you can achieve the via cors
Let's take a minute to explain what "origin" is in simple words.
When a user browses to a website, he uses an address.
Normally that address is a domain, but when we run our tests we mostly work with local IPs.
For CORS, this doesn't matter. Once you enable Allow-Origins, the server looks at the address the user used to reach the website, and defines it as the "origin" of the request.
Obviously, on a deeper level, everything comes down to IP addresses. But when it comes to CORS, it's more of a high-level security method. It's helps preventing Cross-Site Request-Forgeries for example.
In conclusion, in order for CORS to work,
make sure you allowed the address which the user is using to access the HTTP service.
So, if you're serving a VUE app that's working on http://localhost:8080, and from it calling to an HTTP service on http://localhost:5555, you'll need to do this on the said HTTP service:
app.use(cors({
origin: 'http://localhost:8080'
}));

How to access website content from onother server with express or http

How to access website content from another server with Express or HTTP
I have a website that holds all data like template website for example
and I have 3 more websites that get access this website template content HTML CSS everything inside website 2 3 and 4 the only defriend is the route like
mysite.com/template1/user1/index.html
mysite.com/template1/user2/index.html
mysite.com/template1/user3/index.html
I want to have inside website **(n)* only code that gets the HTML CSS and js content from the template server the master how I can do that?.
In PHP is something like
$url = $GET(www.masterserve.com/template1/ + user1 ) echo $url
Any example that I can do the same with node.js and express
// Get dependencies
const express = require('express');
const path = require('path');
const http = require('http');
const bodyParser = require('body-parser');
// Get our API routes
const api = require('./server/routes/api');
const app = express();
// Parsers for POST data
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
// Point static path to dist
app.use(express.static(path.join(__dirname, 'dist'))); <-- idont want static
file only a URL from the master server
// Set our api routes
app.use('/api', api);
// Catch all other routes and return the index file
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'dist/index.html'));
});
/**
* Get port from environment and store in Express.
*/
const port = process.env.PORT || '3000';
app.set('port', port);
/**
* Create HTTP server.
*/
const server = http.createServer(app);
/**
* Listen on provided port, on all network interfaces.
*/
server.listen(port, () => console.log(`API running on localhost:${port}`));
If you're trying to get HTTP content from some other server from within your nodejs app, you can use the request module.
request.get('http://somesite.com/template1/user3/index.html', function(err, response, body) {
// access data from other web site here
});
If you're trying to stream that data to some other response, you can also .pipe() the data that you requested to another response. The documentation for that module shows lots of examples of how to do that.

Resources