user authentication using socket.io - node.js

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.

Related

Simple example of user authentication using Next.JS, Express and Passport

I really don't understand, why it is so complex to build authentication and persisting in session with Node.js.
I'm having trouble with session persistance, that is described here.
Maybe, I something don't understand...
So, in an SPA, when a browser making fetch with POST method from UI, Passport authenticates and saves session in DB (as I've setup).
What's next?
How to tell React front-end (browser, server…), that It should apply newly created cookie and use it for all subsequent requests for HMR, GraphQL and other stuff?
What I have is all subsequent requests to server referring old cookie (not created one on successful authentication) and that correct one will never looked up…
Some explanation will be greatly appreciated.
Thank You.
PS: Still looking for simple working examples of authentication with latest Next.js, Express and Passport. I'm stuck with this problem on a week…
You can make a request to the endpoint of express which is going to return you the information... for this you can use Axios, when it response you can set the cookie with something like this:
document.cookie = `id_token=${token}; expires=Thu, 18 Dec 2020 12:00:00 UTC`
In my case I set a token because I use JWT, when the cookie is set, you can request it on the server side using cookie-parser, so, when you are going to verify is the user is logged you can check if the cookie exists on the server (Next.js) and render the template, otherwise you can redirect to other view... something like this:
server.get('/profile', (req, res) => {
const actualPage = '/profile';
const logged = req.cookies['id_token']
if (logged) {
return app.render(req, res, actualPage)
}
return res.redirect('/')
})
If you want to see the complete example, check this repo

Cookies handling when client-side is also an ExpressJS app

As far as I'm concerned, for a server side application to know what clients are communicating with it, it will save a cookie in the client with the session ID.
That is indeed what express-session a popular package for session storage in ExpressJS says in the documentation
Note Session data is not saved in the cookie itself, just the session ID. Session data is stored server-side.
So I believe I can assume this is strategy used by Express to maintain user data in sessions as well.
I did something else: I'm using Redis to store the Session data in my ExpressJS server app.
So having said that, my problem is that my client application is also an Express app. I have a nodejs app with Express both for client and server. There is a SPA involved in this problem, but it communicates with the 'express client' so it appears to be a different problem.
So everytime I send a request from Express Client to Express Server, there is not cookie being passed, so it can't identify a session ID and creates a new one, generating lots of garbage in Redis and making the session solution useless.
How should I save or fake a cookie in the Express Client app, or, even better, how this kind of problem is solved?
Actually if I knew what the Express Server is expecting in the request (a cookie, a header or whatever) I think I can solve the problem.
Anyone know what to do?
#Solved
Alright, so, in my nodejs client application I did the following:
login(req,res,next){
var options = {
url : 'http://localhost:8090/user/login_handler';
};
request(options, function(error,response,body) {
var cookie_string = response['headers']['set-cookie'][0].split(';')[0];
req.session.cookie_string = cookie_string;
});
}
check(req,res,next){
var options = {
url : 'http://localhost:8090/user/check_handler',
headers: {
'cookie':req.session.cookie_string
}
};
request(options, function(error,response,body){
res.json( body);
});
}
In short, when the session is created in the server side, it will respond with headers to tell the client to create a cookie. I save the important information to pass as a cookie in a different moment. The server-side then read the headers in the middleware and load the correect data to the session.
Without knowing the details of your architecture I would guess that what you want is to either set the saveUnitialized option to false, and not save the extraneous sessions, or only apply the express-session middleware to certain routes in your "Express Server" application

Allow connection only for authorised socket in sails.js

I am trying to implement some kind of security for socket.io clients in the sails.js backend (using version 0.12.x). To achieve this, I try to either prevent successful handshake for clients without a proper cookie (no authorised session beforehand) or like for HTTP request using passport.js to see if the client is authenticated.
In Sails.js documentation I've found that this should be possible, but I could not find any hint, how to do it really. On the other hand, looking for examples on the internet, people mostly don't use security for sockets, or use some old version of sails.js (<0.10).
The closest what I found until now, is for the config/sockets.js:
beforeConnect: function(handshake, cb) {
if (handshake.headers.cookie) {
console.log(handshake);
// TODO: check session authorization
} else {
return cb(null, false);
}
return cb(null, true);
},
This should check the cookie sent with the handshake, if it has a proper session. I have a hard time figuring out, how can I map the sid from the cookie to current sessions in sails.js, for deciding if the connection should be allowed.
Questions:
What is the best security practice for socket.io, if only a small number of clients is allowed (some 40-50 dynamic generated users should be allowed for connection), and nobody else?
How can I map the sails.sid from the cookie to active sessions?
What other configs could be a shortcut to my goal (e.g. setting some policies, that socket.io request use the same middleware as http)?
Thanks for any hint, link or suggestions.
What is the best security practice for socket.io, if only a small
number of clients is allowed (some 40-50 dynamic generated users
should be allowed for connection), and nobody else?
I don't know what is best. But there are two common approaches: token- and cookie based authentication.
Here is a nice visualization of both taken from https://auth0.com/blog/auth-with-socket-io/
I really like the token approach because there is no need for a session store. Hence the server application is decoupled from the client and also stateless.
How can I map the sails.sid from the cookie to active sessions?
Token approach: check out jsonwebtoken. When a user signs in you generate a token and send it to the client:
res.json({
user: user,
token: jwToken.issue({id : user.id })
});
Further you need a policy that checks if a token exists and validate it:
jwToken.verify(token, function (err, token) {
if (err) return res.json(401, {err: 'Invalid Token!'});
req.token = token;
next();
});
I found a complete tutorial that might help you: https://thesabbir.com/how-to-use-json-web-token-authentication-with-sails-js/
How to configure it with sails: you basically just send the token with each socket.io request and check the token inside a policy:
SailsJS - using sails.io.js with JWT

How should I implement a token based authorization API in Node.js and Redis?

I'm working in a web app which handle resources from a Mongo database, for such resources I'd like to offer an API, so a future mobile application can seize it or consume it from a raw client.
However I'd like to have web app consuming same API, here is where I get a bit confused about how to properly implement this.
Here is what I've done so far:
API Auth:
app.route('/api/auth/')
.post(function (request,response) {
var email = request.body.email;
var password = request.body.password;
var login = new Account({"local.email":email,"local.password":password});
Account.findOne({"local.email":email}, function (err,user) {
if (err) {
response.send(500);
}
if (!user) {
response.send(404);
}
else {
user.validPassword(password, function (err,matched) {
if (err) {
response.send(500);
}
if (matched) {
var uuidToken = uuid.v4();
redisClient.set(uuidToken,user._id,redis.print);
redisClient.expire(user._id,100);
response.send(uuid);
}
else {
response.send(403);
}
});
}
});
});
So basically I receive consumers username and password, I authenticate it against database, If it matches I reply a token, (actually an UUID). That token gets stored at Redis paired with the user id in databse. Every future request to any API route will verify for such token existance.
Here I wonder:
How should I manage the token TTL, and renewal upon future requests?
How can I control requests per time windows limits?
Is there any security caveat in the approach I'm taking?
Website Auth:
Basically I perform SAME username-password authentication against database and I then:
1. Start a new server session.
2. Naturally, offer back a cookie with session ID.
3. I create then the Redis UUID and user ID record, which API will check. I guess this is OK as there's any sense in requesting POST /api/auth authenticating again.
Here I wonder:
Is this a best approach?
Should I include any token salt to distinguish a pure API consuming request from a request from web app?
Is there any security caveat in the approach I'm taking?
Should I include more tokens?
This is example of POST /login:
app.route('/login')
.post(function (request,response,next) {
var email = request.body.email;
var password = request.body.password;
var login = new Account({"local.email":email,"local.password":password});
Account.findOne({"local.email":email}, function (err,user) {
if (err) {
response.redirect('/error');
}
if (!user) {
var cookie = request.cookies.userAttempts;
if (cookie === undefined) {
response.cookie('userAttempts',1);
}
else {
response.cookie('userAttempts',(++cookie));
}
response.redirect('/');
}
else {
user.validPassword(password, function (err,matched) {
if (err) {
// Redirect error site or show err message.
response.redirect('/error');
}
if (matched) {
var session = request.session;
session.userid = user._id;
var uuidToken = uuid.v4();
redisClient.set(uuidToken,user._id,redis.print);
redisClient.expire(uuidToken,900);
response.cookie('email',email);
response.redirect('/start');
}
else {
var cookie = request.cookies.passwordAttemps;
if (cookie === undefined)
response.cookie('passwordAttemps',1);
else {
var attemps = ++request.cookies.attemps
response.cookie('passwordAttemps', attemps)
}
response.redirect('/');
}
});
}
});
})
I think I could get rid of using and writing a typical session implementation and depend somehow on the similar token based auth the API has.
What you have there is on the right track and basically replaces some of the functionality of cookies. There are a few things to consider though, and you've touched on some of them already.
While using a UUID (v4 I'm guessing?) is good in that it's nondeterministic and "random", on its own the token is worthless. Should redis lose data the token no longer has any context. Nor can you enforce expirations without help from redis. Compare this to a JWT which can carry context on its own, can be decrypted by anybody with the correct key, can handle expirations, and can enforce further common application level constraints (issuer, audience, etc).
Rate limiting. There are a number of ways to handle this and few of them are tied directly to your choice of token scheme aside from the fact that you'd probably use the token as the key to identify a user across requests in the rate limiter.
Transparently passing the token in both a web app and on other clients (mobile app, desktop app, etc) can be a huge pain. In order to access private resources the user will need to pass the token in the request somewhere, likely the headers, and in the case of a web app this means manual intervention on your part to include the token in each request. This means hand coded ajax requests for all authenticated requests. While this can be annoying, at least it's possible to do, and if you're writing a single page app it's likely you'd do that anyways. The same can be said for any mobile or desktop client. Since you already have to make the HTTP request directly in code anyways, why does it matter? Now imagine the scenario where an HTTP GET endpoint, which returns an html page, can only be accessed with proper authentication. In the case of a web app the user is very likely going to access this via a browser redirect or by typing it directly into the URL bar. How is the token added to the request? Other than using cookies, which you're explicitly not using because mobile and desktop clients do not implement them, this is not really possible. However, if your API clients can always modify the HTTP request structure this isn't really a problem.
Now for a shameless plug, our team has a library we use for this. It's mostly used internally and as such is pretty opinionated on its dependencies (express, redis), but hopefully it can help you here. In fact, that library is pretty much just a JWT wrapper around what you have in place. If you decide to use it and notice any issues or deficiencies feel free to file any issues on github. Otherwise there are a whole bunch of other JWT based session management modules on npm that look promising. I would check those out regardless as there are very likely better modules out there than ours. Again, ours is used internally and came about from a pretty specific set of use cases so the chances that it captures all of yours are pretty slim. On the other hand, it sounds like you're using a similar stack so maybe the shoe fits.
If you do use ours it may seem odd that there's a split in the API surface on that module in that you can choose to store data directly in the JWT claims or in redis. This was deliberate and I think your examples illustrate a good use case for both sides. Typically what we do is store the user's email and name in the JWT claims, then store more dynamic session data in redis on their session. For example, upon logging in you'd add the issuer, audience, and user's email to the JWT claims but leave off anything related to "userAttempts". Then upon failed attempts you would add or modify the "userAttempts" on the session data stored in redis related to that JWT. Once a JWT is set it's not possible to modify its contents without generating a new one, so be aware that if you decide to keep relatively dynamic data in the JWT you'll have a constant exchange of old and new JWT's between the server and client.

What's the better way of implementing security with MEAN.js

I'm working with mean.js, and I have a little doubt about authentication and authorization here...
MEAN.js come with a out of the box passport.js implementation that seems to be working good enough for me just to know when a user is logged in. But at the moment of authorization some question pop up in my mind.. doing my research I reach some answers and I don’t know what is the best way of implementing security API calls in my app.
So far, I'm taking this solution:
Using express.all() function to set in one file all my authorization functions ( I guess it is a good practice right ? ).. creating a file with the following code example:
'use strict';
var passport = require('passport');
module.exports = function(app) {
app.route('/private/p/*').all(function(req, res, next){
if(!req.isAuthenticated()){
res.send(401);
}else{
next();
}
});
app.route('/private/byRoles/*').all(function(req, res, next){
if(!req.isAuthenticated()){
res.send(401);
}else{
var urlRoles = ['admin', 'godlike'];
// ROLE LOGICS THAT ARE GOING TO BE ADDED TO MY USER
// GETTING MY USER ID BY THE DE-SERIALIZE PASSPORT FUNCTION AND GETTING MY
// MONGO MODEL FOR MY USER, WITH THE INFO OF ROLES IN THERE AND DOING
// SOME LOGICS HERE ABOUT THE ROLES AND URL PATTERN.
if ( hasRole(urlRoles, user.roles)){
next();
}else{
res.send(401);
}
}
});
};
So far this is the solution that I'm planning to implement, but I would like to be sure of what I'm doing here... is there a better way of implementing authorization in mean.js ? Is this authorization middle-ware wrong implemented with passport? I don't sure if is necessary to implement another strategy to this.. or if this implementation has a security lack ( sure it has to ).. is better to use Oauth or using api token ??? what should be the architecture to secure an app made in MEAN.js supporting roles and permissions ?? also in the future I would need to secure my socket.. I was looking at passport-socketio.. but not sure if is there a better solution.
I use JWT's for my angular apps. There are many articles out there about the benefits for using tokens instead of sessions or cookies Cookies vs Tokens. Getting auth right with Angular.JS.
You can do everything you want with JWT, roles for backend and frontend, securing sockets is also possible and there are packages for this functionality. You do not need passport if you using tokens. You check the the credentials one time and store the token in the browsers local storage. There are many packages for express and JWT Express-JWT
For a closer look at JWT jwt.io

Resources