Restrict access to Node.js using Express - javascript

I do have a running node.js script located in a server.
What i want is that it doesn't get directly accessed from browser and also i want that only certain domains/IP-s can call it!
Is it possible?!

Not sure how to distinguishing between accessing something from a browser as opposed to other software, but restricting access to some domains/IPs should be doable. The following (non production) code to restrict access to the localhost loop back might serve as a starting point:
function securityCheck( req, response, next)
{ var callerIP = req.connection.remoteAddress;
(callerIP == "::ffff:127.0.0.1" || callerIP == "::1") ? next() : reply404( response);
}
function reply404( response)
{ response.writeHead(404, {"Content-Type": "text/html"});
response.statusMessage = "Not Found";
console.log("reply404: statusCode: " + response.StatusCode);
response.end('<span style="font-weight: bold; font-size:200%;">ERROR 404 – Not Found<\/span>');
}
var app = express();
app.use(securityCheck); // restrict access
... // continue with app routing
See also more detailed SO answers to Express.js: how to get remote client address and How do I get the domain originating the request in express.js?

Related

Stop execution after writing to text file with fs.writeFile

I have the following Node.JS (ran with Express) code :
let app = express();
app.use(cors());
app.get('/callback', function (req, res) {
// your application requests refresh and access tokens
// after checking the state parameter
var code = req.query.code || null;
var authOptions = {
url: 'https://accounts.spotify.com/api/token',
form: {
code: code,
redirect_uri: redirectUri,
grant_type: 'authorization_code'
},
headers: {
'Authorization': 'Basic ' + (new Buffer(clientId + ':' + clientSecret).toString('base64'))
},
json: true
};
request.post(authOptions, function (error, response, body) {
if (!error && response.statusCode === 200) {
var access_token = body.access_token,
refresh_token = body.refresh_token;
fs.writeFile('test.txt', 'HELLO', function (err) {
if (err) return console.log(err);
console.log('Hello World > helloworld.txt');
});
}
}
)
});
console.log('Listening on 8888');
app.listen(8888);
The route is used as a callback for a request to the Spotify Web API, thus I can get an access token.
Spotify then redirects to the callback function above, you can see it in the URI by looking at "redirect_uri".
If you need more information about the Spotify Authorization Flow, see here.
Here's the URI I'm using to authenticate my app to Spotify.
https://accounts.spotify.com/authorize?client_id=CLIENT_ID&response_type=code&redirect_uri=http://localhost:8888/callback&scope=user-read-private%20user-read-email%20playlist-modify-public&state=PexBrjEzISHepTp7&show_dialog=false
CLIENT_ID is replaced by my real CLIENT_ID in the request I make
My problem is located to the file writing part :
fs.writeFile('test.txt', 'HELLO', function (err) {
if (err) return console.log(err);
console.log('Hello World > helloworld.txt');
});
When the callback route is called by Spotify, I have the string "HELLO" wrote in my text file, so the file writing is functional.
But even if it has finished writing the string, the Chrome page is still running and "pending" on the server. It runs for a few minutes and then crash by saying that the page didn't sent any data. Why ?
I've looked at this page talking about the methods of writing to text files, using writeFile and writeFileAsync, but using both of them didn't solved my problem.
EDIT: I don't really want to stop the Express process! I just want to be able to process another request :)
Any idea ? Thanks in advance :)
You aren't returning anything from your route, try adding res.send({})
In your get route you are not sending response, you must send response irrespective of writing a file was successful or not.
Add below code post writing to file (as well as in if error case)
res.send({YOUR_CHOICE_RESPONSE_DATA})

Meteor Facebook Messenger Bot webhook

I'm trying to build a Facebook Messenger bot with Meteor. Setup includes looking for the Verify Token and respond with the challenge sent in the verification GET request. In Facebook's (non-Meteor) sample app, the following code is used:
app.get('/webhook', function(req, res) {
if (req.query['hub.mode'] === 'subscribe' &&
req.query['hub.verify_token'] === VALIDATION_TOKEN) {
console.log("Validating webhook");
res.status(200).send(req.query['hub.challenge']);
} else {
console.error("Failed validation. Make sure the validation tokens match.");
res.sendStatus(403);
}
});
When I try to achieve the same functionality using the following (Meteor) code, I receive the error below.
var bodyParser = Meteor.npmRequire( 'body-parser');
// Add two middleware calls. The first attempting to parse the request body as
// JSON data and the second as URL encoded data.
Picker.middleware( bodyParser.json() );
Picker.middleware( bodyParser.urlencoded( { extended: false } ) );
// ------------------------------------------------------------
// HANDLE THE INITIAL HANDSHAKE WITH FACEBOOK VIA A GET REQUEST
// ------------------------------------------------------------
var getRoutes = Picker.filter(function(req, res) {
// you can write any logic you want.
// but this callback does not run inside a fiber
// at the end, you must return either true or false
return req.method == "GET";
});
getRoutes.route('/webhook', function(params, req, res, next) {
if (params.query['hub.verify_token'] === '78750') {
console.log(params.query['hub.verify_token']);
// res.end();
res.end(params.query['hub.challenge']);
}
}); // end getRoutes
Error:
The URL couldn't be validated. Response does not match challenge, expected value = '1127215706', received='<!DOCTYPE html> <htm...
Perhaps this issue is due to it being run on the client rather than the server? If so, where should I put this code in order for it to be run on the server?
In addition, my browser console has the following error 12 times:
Mixed Content: The page at 'https://pfbe.meteorapp.com/' was loaded over HTTPS, but requested an insecure font 'http://themes.googleusercontent.com/static/fonts/inconsolata/v5/BjAYBlHtW3CJxDcjzrnZCIbN6UDyHWBl620a-IRfuBk.woff'. This request has been blocked; the content must be served over HTTPS.
What can I do to fix this problem?
Use Restivus
-You need to respond in the body with challenge and return it as a parsedInt

Can't figure out "No 'Access-Control-Allow-Origin' header" in Node Express with CORS and Mandrill

I've been reading up enough to know I'm lost on this one. The solutions on other threads don't seem to help.
I have a page at pages.samedomain.com calling the mandrill api in my Node site at apps.samedomain.com. Using ORM, I am able to write through the tables route just fine. After the table is written and the page receives confirmation, it's supposed to fire to the email route. When run locally, both work fine. When deployed, I get...
XMLHttpRequest cannot load http://apps.samedomain.com/.../.../mail/4847775376401843. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://pages.samedomain.com' is therefore not allowed access. The response had HTTP status code 502.
In my app.js I have...
var cors = require('cors');
app.use(cors());
In my routes file I have...
module.exports = function(appRouter) {
var mandrill = require('mandrill-api/mandrill');
var mandrill_client = new mandrill.Mandrill(process.env.MANDRILL_API_KEY);
appRouter.route('/.../mail/:first_list_id').post(function(req,res){
req.models.know_me_2016
.find({list_id:req.params.first_list_id})
.run(function(err, results){
if (err) {
res.send(err);
} else {
var template_content = [{
"recipient": <stuff> ,
"content": <stuff>
}];
var message = {
<mandrill message object stuff>
};
}
mandrill_client.messages.sendTemplate({
"template_name": <template-name>,
"template_content": template_content,
"message": message}, function(result) {
console.log(result);
//I tried adding header stuff but it didn't help, maybe in wrong place? I thought CORS library was going to take care of this part?
res.header("Access-Control-Allow-Origin", "http://interactives.dallasnews.com");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
//It sends successfully when run local
res.send("Email sent successfully");
}, function(e) {
// Mandrill returns the error as an object with name and message keys
console.log('A mandrill error occurred: ' + e.name + ' - ' + e.message);
// A mandrill error occurred: Unknown_Subaccount - No subaccount exists with the id 'customer-123'
});
});
});
}
My Mandrill key is set to accept all IPs.
Any insight would be appreciated.
You need to add allowed origins to the white list when initializing cors:
var whitelist = [
'http://samedomain.com',
'http://apps.samedomain.com',
'http://pages.samedomain.com'
// list whatever possible domains you have
]
var globalCorsOptions = {
origin: function(origin, callback) {
callback(null, whitelist.indexOf(origin) !== -1);
}
};
var cors = require('cors');
app.use(cors(globalCorsOptions));
In this particular case, it turns out the problem was with the lack of an updated .env file. Our private git ignores .env files so the credentials weren't being posted. Mandrill was unable to connect. Once the Mandrill credentials were inserted and the .env remote updated, it started working as expected.

JSONP call with server-side language as Javascript

I've been trying to use JSONP to get a JSON object from a server via a client-side call (on a different port). However, because my server is implemented in javascript using Node.js and Express, I haven't been able to find much on JSONP with Javascript on the server as most sites I found used php for server-side code.
I believe the issue is with how I set up the url with respect to the callback, which I'm a bit fuzzy on cause it's new to me.
On the server:
//Setting up server stuff
var express = require('express'),
app = express.createServer();
app.use(express.logger());
//Making a connection to the mongoDB to get the data that I want to display in the JSON object
new Db('prism',
new Server("127.0.0.1", 27017, {auto_reconnect: false}), {}).open(function(err, db) {
app.get('/getData', function(req, res) {
console.log('JSONPCALLBACK CALLED WITH RETURNDATA PASSED IN; SERVER SIDE');
if (typeof callback == 'function') {
console.log('callback is defined');
callback(returnData);
}
else {
console.log('callback is not defined');
}
}
});
And on the client:
$.ajaxSetup({ cache : false });
$.getJSON('http://127.0.0.1:1337/getData&callback=?', function(rtndata) {
console.log('SUCCESS');
console.log(rtndata);
});
embedded by the standard tags.
But I get the error:
GET http://127.0.0.1:1337/getData&callback=jQuery16108897686484269798_1311007334273?_=1311007334342 404 (Not Found)
The server is on port 1337 while the client is run through MAMP on localhost:8888. I'm not sure if its even a localhost related issue as I've been trying to get this setup running for a few days now.
I believe the issue has something to do with not writing this line, which is in php, into my server-side Javascript. Most of the JSONP examples I found had something like this. But I'm not sure.
if ($GET['callback'] != '')
$json = $GET['callback']."( $json )";
return $json;
Any help would be greatly appreciated. I apologize ahead of times for being super verbose.
Bests,
Cong
I think you have two problems. First is the 404. Completely separate from getting the client-side jQuery code to work, you need to make sure that you can issue a regular browser request (i.e. paste in that URL) and get back what you expect. I haven't used express, so it's hard for me to comment on why you'd be getting that, except to say that I don't see 1337 anywhere in your server-side code, just what appears to be the port number 27017.
The second problem is that you don't actually want to execute the callback on the server, just build the JSON response including the callback (string) prefixed to it.
So instead of this ...
if (typeof callback == 'function') {
console.log('callback is defined');
callback(returnData);
}
else {
console.log('callback is not defined');
}
try this:
if (callback) {
console.log('callback is defined');
res.write(callback + '(' + JSON.stringify(returnData) + ')');
}
else {
console.log('callback is not defined');
}
Hope this helps!
From http://api.jquery.com/jQuery.getJSON/ there is an example that includes 2 '?' in the URL.
you only have one, so try
$.getJSON('http://127.0.0.1:1337/getData?callback=?', function(rtndata) {
and see if that gets rid of your 404
then look #jimbojw suggestion for returning a proper jsonp formated responce.
Use this:
var express = require("express");
var server = express.createServer();
server.enable("jsonp callback");
server.get("/foo", function(req, res) {
// this is important - you must use Response.json()
res.json("hello");
});
jsonp with node.js express

Basic Ajax send/receive with node.js

So I'm trying to make a very basic node.js server that with take in a request for a string, randomly select one from an array and return the selected string. Unfortunately I'm running into a few problems.
Here's the front end:
function newGame()
{
guessCnt=0;
guess="";
server();
displayHash();
displayGuessStr();
displayGuessCnt();
}
function server()
{
xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET","server.js", true);
xmlhttp.send();
string=xmlhttp.responseText;
}
This should send the request to server.js:
var http = require('http');
var choices=["hello world", "goodbye world"];
console.log("server initialized");
http.createServer(function(request, response)
{
console.log("request recieved");
var string = choices[Math.floor(Math.random()*choices.length)];
console.log("string '" + string + "' chosen");
response.on(string);
console.log("string sent");
}).listen(8001);
So clearly there are several things going wrong here:
I get the feeling the way I am "connecting" these two files isn't correct both in the xmlhttp.open method and in using response.on to send the string back to the front end.
I'm a little confused with how I call this page on localhost. The front end is named index.html and the sever posts to 8001. What address should I be go to on localhost in order to access the initial html page after I have initialized server.js? Should I change it to .listen(index.html) or something like that?
are there other obvious problems with how I am implementing this (using .responsetext etc.)
(sorry for the long multi-question post but the various tutorials and the node.js source all assume that the user already has an understanding of these things.)
Your request should be to the server, NOT the server.js file which instantiates it. So, the request should look something like this: xmlhttp.open("GET","http://localhost:8001/", true); Also, you are trying to serve the front-end (index.html) AND serve AJAX requests at the same URI. To accomplish this, you are going to have to introduce logic to your server.js that will differentiate between your AJAX requests and a normal http access request. To do this, you'll want to either introduce GET/POST data (i.e. call http://localhost:8001/?getstring=true) or use a different path for your AJAX requests (i.e. call http://localhost:8001/getstring). On the server end then, you'll need to examine the request object to determine what to write on the response. For the latter option, you need to use the 'url' module to parse the request.
You are correctly calling listen() but incorrectly writing the response. First of all, if you wish to serve index.html when navigating to http://localhost:8001/, you need to write the contents of the file to the response using response.write() or response.end(). First, you need to include fs=require('fs') to get access to the filesystem. Then, you need to actually serve the file.
XMLHttpRequest needs a callback function specified if you use it asynchronously (third parameter = true, as you have done) AND want to do something with the response. The way you have it now, string will be undefined (or perhaps null), because that line will execute before the AJAX request is complete (i.e. the responseText is still empty). If you use it synchronously (third parameter = false), you can write inline code as you have done. This is not recommended as it locks the browser during the request. Asynchronous operation is usually used with the onreadystatechange function, which can handle the response once it is complete. You need to learn the basics of XMLHttpRequest. Start here.
Here is a simple implementation that incorporates all of the above:
server.js:
var http = require('http'),
fs = require('fs'),
url = require('url'),
choices = ["hello world", "goodbye world"];
http.createServer(function(request, response){
var path = url.parse(request.url).pathname;
if(path=="/getstring"){
console.log("request recieved");
var string = choices[Math.floor(Math.random()*choices.length)];
console.log("string '" + string + "' chosen");
response.writeHead(200, {"Content-Type": "text/plain"});
response.end(string);
console.log("string sent");
}else{
fs.readFile('./index.html', function(err, file) {
if(err) {
// write an error response or nothing here
return;
}
response.writeHead(200, { 'Content-Type': 'text/html' });
response.end(file, "utf-8");
});
}
}).listen(8001);
console.log("server initialized");
frontend (part of index.html):
function newGame()
{
guessCnt=0;
guess="";
server();
displayHash();
displayGuessStr();
displayGuessCnt();
}
function server()
{
xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET","http://localhost:8001/getstring", true);
xmlhttp.onreadystatechange=function(){
if (xmlhttp.readyState==4 && xmlhttp.status==200){
string=xmlhttp.responseText;
}
}
xmlhttp.send();
}
You will need to be comfortable with AJAX. Use the mozilla learning center to learn about XMLHttpRequest. After you can use the basic XHR object, you will most likely want to use a good AJAX library instead of manually writing cross-browser AJAX requests (for example, in IE you'll need to use an ActiveXObject instead of XHR). The AJAX in jQuery is excellent, but if you don't need everything else jQuery offers, find a good AJAX library here: http://microjs.com/. You will also need to get comfy with the node.js docs, found here. Search http://google.com for some good node.js server and static file server tutorials. http://nodetuts.com is a good place to start.
UPDATE: I have changed response.sendHeader() to the new response.writeHead() in the code above !!!
Express makes this kind of stuff really intuitive. The syntax looks like below :
var app = require('express').createServer();
app.get("/string", function(req, res) {
var strings = ["rad", "bla", "ska"]
var n = Math.floor(Math.random() * strings.length)
res.send(strings[n])
})
app.listen(8001)
https://expressjs.com
If you're using jQuery on the client side you can do something like this:
$.get("/string", function(string) {
alert(string)
})
I was facing following error with code (nodejs 0.10.13), provided by ampersand:
origin is not allowed by access-control-allow-origin
Issue was resolved changing
response.writeHead(200, {"Content-Type": "text/plain"});
to
response.writeHead(200, {
'Content-Type': 'text/html',
'Access-Control-Allow-Origin' : '*'});
Here is a fully functional example of what you are trying to accomplish. I created the example inside of hyperdev rather than jsFiddle so that you could see the server-side and client-side code.
View Code:
https://hyperdev.com/#!/project/destiny-authorization
View Working Application: https://destiny-authorization.hyperdev.space/
This code creates a handler for a get request that returns a random string:
app.get("/string", function(req, res) {
var strings = ["string1", "string2", "string3"]
var n = Math.floor(Math.random() * strings.length)
res.send(strings[n])
});
This jQuery code then makes the ajax request and receives the random string from the server.
$.get("/string", function(string) {
$('#txtString').val(string);
});
Note that this example is based on code from Jamund Ferguson's answer so if you find this useful be sure to upvote him as well. I just thought this example would help you to see how everything fits together.

Categories

Resources