How to prevent "other" domains from accessing URL - javascript

I have a Node/React application that pulls data from an API.
On the server side (server.js) I have an “internal” URL that grabs the data from the API and then returns it as JSON. Like this:
server.get('/api/data', (req, res) => {
Promise.resolve(getAPIData()).then(data => {
res.writeHead(200, {'Content-Type': 'application/json'})
return res.end(serialize(data))
}).catch((error) => {
console.log(error)
res.writeHead(200, {'Content-Type': 'application/json'})
return res.end(serialize({}))
})
})
Then on the front-end in React (Next.js) I fetch this JSON with the example.com/api/data URL and display it on the page.
My problem is I want to restrict the example.com/api/data URL to be accessed only from my domain (which in this case let's say is example.com). I don’t want someotherdomain.com to be able to access the same data from the url.
I installed helmet, but that didn’t quite do what I wanted. CORS didn’t seem to be the answer either.
Is there a standard way to do this?
Update:
Here's what I tried to do with CORS. Using this package:
https://github.com/expressjs/cors
const cors = require('cors')
...
const corsOptions = {
origin: (process.env.NODE_ENV === 'development') ? 'http://localhost:3000' : 'https://example.com',
optionsSuccessStatus: 200 // some legacy browsers (IE11, various SmartTVs) choke on 204
}
And then on the route:
server.get('/api/data', cors(corsOptions), (req, res) => {
Promise.resolve(getAPIData()).then(data => {
res.writeHead(200, {'Content-Type': 'application/json'})
return res.end(serialize(data))
}).catch((error) => {
console.log(error)
res.writeHead(200, {'Content-Type': 'application/json'})
return res.end(serialize({}))
})
})

Related

EventSource gets all chunks at once when streaming via Nextjs API Routes

Problem
I am trying to stream data to my React Frontend via EventSources, which kind of works. The problem is that I receive all chunks of data at once instead of time after time. Which kind of defeats the purpose of the method as it is basically now a GET request.
Code
I am using Next.js api routes and this is my code on this route:
res.writeHead(200, {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
Connection: "keep-alive",
});
answerStream.data
.on("data", (chunk: string) => res.write(chunk))
.on("error", (error: Error) => {
console.error(error);
res.end();
})
.on("end", () => res.end());
This is the code in the Frontend:
const eventSource = new EventSource(url);
eventSource.addEventListener("message", e => {
try {
if (e.data == "[DONE]") eventSource.close();
else {
const messageObject = JSON.parse(e.data);
setArticle(state => (state += messageObject?.choices[0]?.text));
}
} catch (error) {
console.log(error);
}
});
eventSource.addEventListener("close", e => {
console.log("Connection closed with the server");
setSubmitting(false);
});
eventSource.addEventListener("error", e => {
setError(e?.message || "Leider ist ein Fehler aufgetreten");
setSubmitting(false);
eventSource.close();
});
Info
Is there anything in my implementation wrong? Or does Next.js have a strange handling of EventSource?
Solution
Ok, I have found the solution in this Github discussion:
It seems the issue is that the middleware adds a gzip encoding which the browser has negotiated using the header
In order to fix this, it is needed to overwrite that behavior by adding 'Content-Encoding': 'none' to the headers on the server:
res.writeHead(200, {
Connection: 'keep-alive',
'Content-Encoding': 'none',
'Cache-Control': 'no-cache',
'Content-Type': 'text/event-stream',
});
Alternatively, one could use a custom server.js.

Is it possible to pass a request header on a link with EJS template? Express/Node.js

I'm a trying to make a route accessible only if you are authenticated with JWT using below middleware but I can't seem to find a way to pass the token in the get request from client side ?
It works fine on postman and I if using a fetch from client side it won't redirect me to the page I want to go
auth.js
async function (req, res, next) {
const token = req.header('x-auth-token');
if (!token) {
return res.status(401).json({ msg: 'Forbidden' });
}
try {
const decoded = jwt.verify(token, process.env.TOKEN_SECRET);
req.user = decoded.user;
next();
} catch (e) {
return res.status(401).json({ err: 'fail' });
}
};
server side
router.get('/', auth, function (req, res, next) {
res.render('pages/person');
});
You can simply attach your token to the headers in the request and sent it with get or even 'delete` method.
for example, in the fetch method you can attach your token in this way in your client side:
fetch('URL_GOES_HERE', {
method: 'post',
headers: new Headers({
'Authorization': YOUR_TOKEN,
'Content-Type': 'application/x-www-form-urlencoded'
}),
});
Now, you can retrieve the token in the node app:
const token = req.headers.authorization || "";

I didn't get response from the nodejs server. It showing undefine after request to server

I hosted website on two different platforms like Firebase and Heroku
I Have some issues with that
Firstly, It showing cors errors when I post data from firebase hosted URL to the server which is hosted on Heroku
Then after resolving cors errors data couldn't from the server it showing undefined in console
Here is my server-side code which is hosted on Heroku
const express = require('express')
const path = require('path')
const PORT = process.env.PORT || 3000
const app = express()
const bodyParser = require('body-parser')
app.use(bodyParser.urlencoded({extended:false}))
app.use(bodyParser.json())
app.use(express.json({limit:'1mb'}))
app.use(function (req, res, next) {
res.header('Access-Control-Allow-Origin', 'https://sample-377b8.web.app');
res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With,content-type,Accept,Authorization',);
res.header('Access-Control-Allow-Credentials', true);
if(req.method=="OPTIONS"){
res.header('Access-Control-Allow-Methods','PUT,POST,DELETE,PATCH')
return res.status(200).json({})
}
// Pass to next layer of middleware
next();
});
let data;
app.get('/',(req,res)=>{
res.send("hello world")
})
app.post('/',(req,res)=>{
data = req.body
console.log(data)
res.status(200).json({
"success":"200 response",
"res":"You are now just talked with server"
})
})
app.listen(PORT, () => console.log(`Listening on ${ PORT }`))
This is my client side code
document.getElementById('send').addEventListener('click',async()=>{
let data = {lat,lon}
await fetch('https://demoserver-app.herokuapp.com/',{mode:"no-cors"},{
method: 'POST', // or 'PUT'
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
}).then( async (dat) =>{
console.log(res.json())
}).then(res =>{
console.log(res)
})
})
It is giving the error on a console like
console error image
Headers information in the network tab
Header information of request image
I hope I can help you,
one issue that I see that can make this kind of output
is that you console.log(res) but .then referring to (dat)
And I don''t think you need async inside .then(it's already async function)
try this:
document.getElementById('send').addEventListener('click',async()=>{
let data = {lat,lon}
await fetch('https://demoserver-app.herokuapp.com/',{mode:"no-cors"},{
method: 'POST', // or 'PUT'
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
}).then( data =>{
data.json()
}).then(res =>{
console.log(res)
})
})
ok, so for the server side:
1)you need to destructure data from req.body, what you made is just adjust req.body to data bar.(see my solution)
2) for the post method you need to make a directory and not try it in the root directory.
try this code you will see the response you want
server:
app.post('/getmessage', (req,res) => {
const {data} = req.body;
console.log(data);
res.status(200).json({
"success":"200 response",
"res":"You are now just talked with server"
})
})
client:
fetch('http://localhost:3000/getmessage', {
method : 'post',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
data:"this is massage from client"
})
})
.then(response => response.json())
.then(data => console.log(data))
.catch(err => console.log(err))

My webserver sends 404 when i make the request through react app, but works fine when I access it directly through chrome or postman

My web server is working fine when I call it through chrome. However when I am using fetch or axiom in my react-app to call the same url, it returns 404 Not Found. The react-app also sends a options request to same url which returns status 200. I have even set this header to allow the origin.
app.use(async (ctx, next) => {
var origin = ctx.headers.origin;
ctx.set({
'Access-Control-Allow-Headers': 'authorization,Content-Type,refresh',
'Access-Control-Allow-Methods': "GET,HEAD,POST,PUT,DELETE",
'Access-Control-Allow-Origin': '*',
'Connection': 'keep-alive',
'Access-Control-Allow-Credentials': true
});
await next();
console.log('______________')
});
require('./routes')(app); //2
my ./routes.js file contain
//router.post('/signin' , signinController.signin);
router.get('/signin' , signinController.signin);
can you please tell me what I am missing here. My axios call
axios.get('http://localhost:3002/signin')
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
Alright I tried your code out and it works for me if I use app.use(router.allowedMethods()) middleware to pass preflights, OR declare separate option-routes for each route; Or instead of using router.get() there is router.all('/signin', ...) which will also catch options-requests.
This is my complete server code:
const app = new Koa();
const router = new Router();
router.get('/signin' , (ctx, next) => {
ctx.body = '{"status":"hello"}';
});
// Adding a options-route works (instead of router.allowedMethods())
router.options('/signin' , (ctx, next) => {
ctx.body = '';
});
app.use(async (ctx, next) => {
var origin = ctx.headers.origin;
ctx.set({
'Access-Control-Allow-Headers': 'authorization,Content-Type,refresh',
'Access-Control-Allow-Methods': "GET,HEAD,POST,PUT,DELETE",
'Access-Control-Allow-Origin': '*',
'Connection': 'keep-alive',
'Access-Control-Allow-Credentials': true
});
await next();
console.log('______________')
});
app.use(router.routes());
// app.use(router.allowedMethods()); // This works instead of separate option-routes.
app.use((ctx, next) => {
console.log('here');
next();
});
app.listen(9787);
And this is my call:
axios.defaults.headers.common['Authorization'] = "sometoken";
axios.get('http://localhost:9787/signin').then(response =>{
console.log(response); // {data: {…}, status: 200, ... }
});
(I had to add the header or the CORS won't trigger.)
I've tried setting various origin and headers and it does obey your middleware.
But I did notice that if you choose to use allowedMethods(), that will override your Access-Control-Allow-Methods (and make it useless).
It has to do with cross origin support
You can use this package #koa/cors#2 to add cors support
const Koa = require('koa');
const cors = require('#koa/cors');
const app = new Koa();
app.use(cors());
Heres the link to the package
https://github.com/koajs/cors

Angular 5 node js express POST JSON Obj. to Amazon Aws [External API] (formerly CORS issue)

SOLVED, see my answer below
My server runs on localhost:3000
I develop on localhost:4200
I am creating something and trying to post it on an Amazon API
Angular side code:
sendSomething(something) {
const body = JSON.stringify(something);
// const headers = new Headers({'Content-Type': 'application/json'});
const headers = new Headers({'Access-Control-Allow-Origin': '*'});
return this.http.post('http://Amazon-API:port/send', body, {headers: headers})
.map((response: Response) => response.json())
.catch((error: Response) => {
this.error.handleError(error.json());
return Observable.throw(error.json());
});
}
Server side:
//////////////app.js//////////////
app.use(cors());
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", '*'); //<-- you can change this with a specific url like http://localhost:4200
// res.header("Access-Control-Allow-Credentials", true);
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
res.header("Access-Control-Allow-Headers", 'Origin,X-Requested-With,Content-Type,Accept,content-type,application/json');
next();
});
app.use('http://Amazon-API:port', engineRoutes);
//////////////routes/engine.js//////////////
router.post('/send', engine_controller.send_something);
//////////////controllers/engine.controller.js//////////////
exports.send_something = function (req, res, next) {
const somethingID = req.body.something;
Something.findById(somethingID, function(err, something) {
if (err) {
res.status(404).json({
title: 'Something not found',
error: {message: 'Something went wrong'}
});
}
console.log(something);
if (something) {
res.status(200).json({
message: 'Something successfully sent',
something: something
});
}
})
};
I have tried posting to that API with cors, without cors and with the res.headers appended, and every other variation I could think of
I still get this error which I've seen so common around here, but still, their solutions don't seem to work for me. Still getting this error...
Failed to load http://Amazon-API:port/send: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:4200' is therefore not allowed access. The response had HTTP status code 403.
That's from NETWORK tab:
Accept:*/*
Accept-Encoding:gzip, deflate
Accept-Language:he-IL,he;q=0.9,en-US;q=0.8,en;q=0.7
Access-Control-Request-Headers:access-control-allow-origin
Access-Control-Request-Method:POST
Connection:keep-alive
Host:Amazon-API:port
Origin:http://localhost:4200
Any kind of help would be so much appreciated
I see you added this code but I can't post comment yet, you may try to add this code before other routes
app.use(function (req, res, next) {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With,
Content-Type, Accept');
res.setHeader('Access-Control-Allow-Methods', 'POST, GET, PATCH, DELETE,
OPTIONS');
next();
});
Solved,
What I did was routing back to the server on the front:
sendSomething(something) {
const body = JSON.stringify(something);
const headers = new Headers({'Content-Type': 'application/json'});
return this.http.post('http://localhost:3000/api/somethings/send-something', body, {headers: headers})
.map((response: Response) => response.json())
.catch((error: Response) => {
this.error.handleError(error.json());
return Observable.throw(error.json());
});
}
And then accepting this route as it is on the back:
//////////////app.js//////////////////
app.use('/api/somethings/send-something', engineRoutes);
/////////////routes/engine.js/////////
router.post('/', engine_controller.send_something);
And most importantly, in the controller itself I used the newly downloaded request lib to send my json data to my external API:
////////////controlllers/engine.controller.js////////////
const request = require('request');
exports.send_something = function (req, res, next) {
const SomethingID = req.body.something;
Something.findById(SomethingID, function(err, something) {
if (err) {
res.status(404).json({
title: 'Something not found',
error: {message: 'Something went wrong'}
});
}
request({
url: app.get('amazon-API:port/method'),
method: "POST",
json: something
}, function (error, response, body) {
// console.log(error) <--- returns null or error
// console.log(response.statusCode <--- returns 200 / 403 / w.e
// console.log(body) <--- returns response pure html
res.status(200).json({
message: 'Something successfully sent',
response: body,
status: response.statusCode
});
});
})
};
Now as a response I'm getting what the server which I posted to sends me back, which is exactly what I need.
Ultimately I figured my way thanks to many other questions posted here
So thank you SOF once again!

Categories

Resources