NodeJS admin backend authentication strategy - node.js

I built a simple e-commerce site for a customer based in NodeJS/Express3/MongoDB and I have some doubts about what kind of authentication strategy to implement for the backend of the site.
I have little to no knowledge in authentication/authorization and I'm a little bit lost, so I'll appreciate if you help me clear the fog a little.
Only one person, the owner is going to access it. I implemented HTTP Simple authentication in the past, but as long as I know, the credentials are sent in plain text. I would need to implement HTTPS but I'm trying to avoid that.
I though about OAuth2 but it seems overkill for a single login.
Maybe Digest fits my needs. It doesn't send the credentials in plain text as long as I know.
I also found this library that look very well:
http://passportjs.org/
What do you recommend for this scenario?

I think Passport with local strategy is good enough.
Use HTTPS is always good, and not hard in Node.js.
Read The Web Application Hacker's Handbook if you wanna go deeper.

I suggest to use HTTP Basic or Digest authentication with HTTPS, you can do that using http-auth module.
// HTTPS module
var https = require('https');
// File system module.
var fs = require('fs');
// Authentication module.
var auth = require('http-auth');
var basic = auth.basic({
realm: "Simon Area.",
file: __dirname + "/../data/users.htpasswd" // gevorg:gpass, Sarah:testpass ...
});
// HTTPS server options.
var options = {
key: fs.readFileSync(__dirname + "/../data/server.key"),
cert: fs.readFileSync(__dirname + "/../data/server.crt")
};
// Starting server.
https.createServer(basic, options, function (req, res) {
res.end("Welcome to private area - " + req.user + "!");
}).listen(1337);

Related

Ubuntu | Nodejs | Secure location for SSL certificates?

I have a nodeStatic app on an ubuntu server.
A domain links to the server.
Now I need to add SSL certificates:
var fs = require('fs');
var options = {
key: fs.readFileSync('ssl/privatekey.pem').toString(),
cert: fs.readFileSync('ssl/certificate.pem').toString()
};
var fileServer = new(nodeStatic.Server)();
var app = http.createServer(options, function(req, res) {
fileServer.serve(req, res);
}).listen(80);
The thing is they are not secure in the folder with the app, or am I wrong?
Here is my folder structure:
The app is in var/www/myapp
Anyone could open the contents of my certificates in the webbrowser with example.com/ssl/privatekey.pem
What is a good practise to serve the certificates and keep everything secure? Put them on the same level as index.js, or even further above? Is this secure?
Bonus question:
Kind of a general question, but the server is fresh, what shall I configure to keep everything secure? A good link on the topic would do it.

Meteor: Endpoint for images fetched from S3 - How to authenticate connection?

We have encrypted images stored in S3 that we need to serve to clients, meaning that we cannot give clients S3 URLs for the img src. The files are also potentially large, so idealy we would like to not go through js.
With no serverside routing available we were going down the route of having a separate express setup in Meteor, and this works, since the router on the client side doesn't interfere with
We could add the Auth token to the src url and poke the DB, but we're wary of doing so as it would expose the token in the DOM and in copy pasta cooked up by the users.
Is there a good way of getting this to work properly? Is it posible to configure other routers to serve up the angular app on a specific URL maybe?
Any input welcome :)
app = Express();
app.get('/order/:orderID/image/:UUID', function(mainReq, mainRes) {
// TODO: security check, but not getting current loggedin user info
// There are no cookies, only the DDP connection is authenticated (?)
console.log(Meteor.userId()); // fails
// S3 fetch and decrypt here
});
The answer is:
It's not possible using out of the box Meteor. If you wanna restrict HTTP requests, you're on your own.
Meteor doesn't use cookies (on purpose and for good reason; https://blog.meteor.com/why-meteor-doesnt-use-session-cookies-e988544f52c9), but instead only ever authenticate the DDP websocket connection, and hence any HTTP request to the server is not authed in any way. There are a few packages that tries to handle these things, and this article explains a way of putting the auth token (and the user ID) into the url: https://blog.kayla.com.au/server-side-route-authentication-in-meteor/
The problem with this is that you then expose the token in the DOM, and any browser extension would be able to read it, and the user would be able to copy/paste the url and send it to others. This could end up in session hijacking.
If you wanna authenticate HTTP requests, you will have to wither find a package that write a cookie (and prevents CSRF attacks if you're doing actions) or have the user supply the username/password each time.
For my situation it is sufficient to have the client side write a cookie with the auth token on login on the client. It will then be sent with the request and can be checked server side. Since all I'm doing is send back a picture, it's not nessaccary to prevent CSRF for me, so beware of that while reading the snippets below og how to have the client send a cookie to the server:
Accounts.onLogin(() => {
removeExistingCookie(cookieName);
document.cookie = "loginToken=" + localStorage['Meteor.loginToken'] + "; domain=" + document.location.hostname + "; path=/; expires=" + expiryTime + ";secure"
});
Then you'll have to parse the cookie header on the server and auth the request on the server using something like this
let cookieParser = Npm.require('cookie-parser');
app = Express();
app.use(cookieParser());
app.get('/order/:orderID/image/:UUID', function(mainReq, mainRes) {
let loginToken = mainReq.cookies["loginToken"];
if (!loginToken) {
mainRes.status(404).send();
return;
}
let hashedToken = Accounts._hashLoginToken(loginToken),
sessionBelongsToUser = Meteor.users.findOne(
{
'services.resume.loginTokens.hashedToken': hashedToken,
});
if (!sessionBelongsToUser) {
mainRes.status(404).send();
return;
}

Retrieving data from Pocket API (oAuth)

I need to retrieve my saved reading list from my Pocket account
and it seems that I need to acquire access token through their oAuth to make a request.
I've got consumer key for the access token and as per Pocket API documentation, the request will resemble something like this.
POST /v3/oauth/request HTTP/1.1
Host: getpocket.com
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Accept: application/x-www-form-urlencoded
consumer_key=1234-abcd1234abcd1234abcd1234&
redirect_uri=pocketapp1234:authorizationFinished
My question is... isn't oAuth for 3rd party apps to enable authentication via Google, Facebook account? I don't see how this idea is relevant for my website that will only require access to my own data from Pocket to share on my site.
I understand I will need to authenticate somehow so I can access my data but is oAuth the process I will need to go through to get what I need?
It seems that they support only 3 legged OAuth flow. You can use Grant in your NodeJS app, or just get access token from here.
Grant
save the following example to a file
set your key here: key:'...'
install the needed dependencies
run the file with node.js
navigate to http://localhost:3000/connect/getpocket
follow the instructions on screen
At the end you'll see your access_token.
var express = require('express')
, session = require('express-session')
var options = {
server: {protocol:'http', host:'localhost:3000'},
getpocket: {key:'...', callback:'/getpocket_callback'}
}
var Grant = require('grant-express')
, grant = new Grant(options)
var app = express()
app.use(session({secret:'very secret'}))
app.use(grant)
app.get('/getpocket_callback', function (req, res) {
console.log(req.query)
res.end(JSON.stringify(req.query, null, 2))
})
app.listen(3000, function () {
console.log('Express server listening on port ' + 3000)
})
}
Purest
Then you can use Purest to make requests to the Pocket's REST API.
var getpocket = new Purest({provider: 'getpocket'})
getpocket.query()
.post('get')
.auth('[API_KEY]', '[ACCESS_TOKEN]')
.request(function (err, res, body) {
// body is the parsed JSON response
})
For anyone reading this in 2021 or later, wanting to make a simple script to add articles to their pocket, I came up with this:
1: get your consumer key, via pocket's site.
2: get you access token, using this tool it's very simple. If you want to make an app or something that'll work without it, I guess the above (old) answer might work, didn't test it.
3: Use the following code to add an article:
var request = require('request');
request.post({
url: 'https://getpocket.com/v3/add',
form: {
url: 'https://articleToAdd.com',
consumer_key: '123456-12abcd1234a1ab12a12abc12',
access_token: '12345a1a-1ab1-1a12-12a1-1a1234'
}
},
function(err, httpResponse, body) { console.log(httpResponse.body) }
)
Hope it helps someone that is looking to do the same. Retrieving/modifying articles is similar, look here for the specifics.

user authentication using socket.io

I've red this tutorial: http://howtonode.org/socket-io-auth.
It shows how to authenticate users using express and socket.io.
But is there a way to authenticate users using only socket.io without the need for express?
edit:
For session handling I use RedisStore (https://github.com/LearnBoost/Socket.IO/wiki/Configuring-Socket.IO).
Whats left is a module to create authentication cookies.
Does anyone know of a socket.io implementation I can use to create an authentication cookie like you can do with session handling?
I know this is bit old, but for future readers in addition to the approach of parsing cookie and retrieving the session from the storage (eg. passport.socketio ) you might also consider a token based approach.
In this example I use JSON Web Tokens which are pretty standard. You have to give to the client page the token, in this example imagine an authentication endpoint that returns JWT:
var jwt = require('jsonwebtoken');
// other requires
app.post('/login', function (req, res) {
// TODO: validate the actual user user
var profile = {
first_name: 'John',
last_name: 'Doe',
email: 'john#doe.com',
id: 123
};
// we are sending the profile in the token
var token = jwt.sign(profile, jwtSecret, { expiresInMinutes: 60*5 });
res.json({token: token});
});
Now, your socket.io server can be configured as follows:
var socketioJwt = require('socketio-jwt');
var sio = socketIo.listen(server);
sio.set('authorization', socketioJwt.authorize({
secret: jwtSecret,
handshake: true
}));
sio.sockets
.on('connection', function (socket) {
console.log(socket.handshake.decoded_token.email, 'has joined');
//socket.on('event');
});
The socket.io-jwt middleware expects the token in a query string, so from the client you only have to attach it when connecting:
var socket = io.connect('', {
query: 'token=' + token
});
I wrote a more detailed explanation about this method and cookies here.
Instead or wiring up authentication and session handling code manually, I'd recommend to go with a dedicated module, such as session.socket.io (but please note that this is a module that requires Express as well).
I guess (but don't know) that there were downvotes because you need some sort of session handling, and you most probably do not want to do this manually as well ;-). Hence it's a quite good idea to stick with Express here.
Nevertheless, it's an interesting question, although I can not answer on how to do it without Express.
I am quite new to node.js, just started a few days ago. and i only can answer to the first part to the question, which is user authentication without the use of express. and i also got no session-style handling yet.
the reason I am still answering to this question is to help out other people who are new to node with a more simple alternative solution for the beginning.
the solution i am currently using in my learning project (a socket.io - based chat, what else?) is using the http server for authentication.
if you can't get a valid authentication on the http server, you'll never get access to the page with the socket.io interface.
the user authentication on the http server is handled by reading out some POST data. only if the POST data is valid user data the user is allowed to move on to the chat where the socket.io interface is.

Make a NodeJs application private on Heroku

I'm trying to make a NodeJS application on Heroku private so that only developers can see it. Is there a simply way to do that, like basic auth? (All of the solutions I keep finding are specific to Ruby apps).
If you want to leverage basic authentication, here are two options: http-auth and Passport. http-auth is a very simple module and Passport is a powerful module with alternatives for authentication. Both modules provide code examples ranging from basic code to Express framework integration.
I have the same problem. I managed to get one solution working that may work for you but wasn't suitable for me as it seems to interfere with the built in user login from angular-fullstack.
I just wanted a quick way to password protect the app so that only developers and stakeholders could see it. https://www.npmjs.org/package/http-auth seems to do the trick.
This involves add http-auth to your project (npm install http-auth --save). Then you'll need to find the file where your createServer is defined and the code there.
If you're using Express you can do something like this
// HTTP Authentication
var preAuth = require('http-auth');
var basic = preAuth.basic({
realm: "Restricted Access! Please login to proceed"
}, function (username, password, callback) {
callback( (username === "user" && password === "password"));
}
);
// Setup server
var app = express();
app.use(preAuth.connect(basic));
var server = require('http').createServer(app);
If not then you can try one of the options from the http-auth documentation e.g.
// Authentication module.
var auth = require('http-auth');
var basic = auth.basic({
realm: "Simon Area."
}, function (username, password, callback) { // Custom authentication method.
callback(username === "Tina" && password === "Bullock");
}
);
// Creating new HTTP server.
http.createServer(basic, function(req, res) {
res.end("Welcome to private area - " + req.user + "!");
}).listen(1337);
Here are also a couple of related threads with somewhat similar approaches.
express.basicAuth throwing error
Basic HTTP authentication in Node.JS?

Resources