I have a simple application, which will login a user and based on his role will let him use a specific API (say normal/admin..view/update respectively). I am using MYSQL to Authenticate users and connection pool to manage many connections to app.
This is how i am managing session,
app.use(expressSession({
cookieName: 'session',
secret: 'mysecret',
duration: 15 * 60 * 1000,
}));
At login,
app.post('/login',function(req,res){
Authenticate(req.body.username,req.body.password,function(err,fname,user){
if(!err) {
req.session.name=user;
res.send("Welcome " + fname);
}
else{
res.send("There seems to be an issue with the username/password combination that you entered");
}
});
});
and whenever i want to access any specific API,i will check for this session name.
app.post('/updateInfo',function(req,res){
if(req.session.name){
// My logic - i ll return the role by searching in db using this req.session.name, if admin will allow him.
}
});
I will destroy the session in logout.
I have two questions since i don't understand the mechanism of the session in node js.
Now suppose i login as admin user and after authentication export
the cookie (say using edit this cookie chrome extension) and logout.
Then login as normal user and import the cookie(admin cookie), will
there be any breach (because now the session will contain admin username)?
Suppose if i want to avoid multiple
connections for a same user (user using different browser/mobile to login), can i just add this piece of code?
if(req.session.name == req.body.username)
{
req.session.destroy();
}
and then continue to authenticate? What are the drawbacks here? Why will it work or not work?
I really would love to make this stateless(don't want to persist state/session in db) because i want performance. Can i do something that is stateless and still works even when there are 100s of concurrent users?
Related
I am not exactly sure how to ask this question, so it may seem a little absurd!
I am developing an application using Node and Express (middleware). I've also used Passport (Local) for authentication.
Regarding the business of my application, I need to manage two kinds of users.
Definition
Application users: people who use the application.
Administrators: people who manage the content of application.
Now, I used passport (Local) for authenticating the application users. Therefore, I've called my passport-setup in "app". The current setup knows the application users' table and works fine. However, for administrators there is another collection which needs to be queried. Question: How can I setup tow different collections using passport? Let me show you some code to illustrate the situation.
app.js
const port = process.env.PORT || 5000;
var passport = require('passport');
var setupPassport = require('./setup-passport');
//Some code here and then
setupPassport();
setupPassport
module.exports = function () {
passport.serializeUser(function (user, done) {
done(null, user._id);
});
passport.deserializeUser(function(id,done){
User.findById(id,function(err,user){
done(err,user);
});
});
};
//And
passport.use('login',new localStrategy(function(username,password,done){
User.findOne({userName:username},function(err,user){
if(err){
return done(err);
}
if(!user){
return done(null,false,{message:'The username has not been found!'});
}
user.checkPassword(password,function(err,isMatch){
//...
As you can see, the config has been set for application users. What if I want to set another collection for administrators? Is there any solution for setup passport for different collections ? Both users are going to use the same application concurrently, so both can login and logout.
A likely but ugly solution
I have been thinking about putting all users information in a single collection, though I am sure it is not nice!
Maybe you can add a field into a schema that takes in a boolean. Something like
admin:{
type: Boolean,
default: false
}
So if a person is registered as an admin the value will be true, otherwise, it will be false.
Every time when you are logging the users in, you can put an if statement under the User.findOne() query function to check if the admin field is set to true and therefore determine the type of users.
Furthermore, you should use the world collection in MongoDB instead of a table. It's a NoSQL DB.
I've recently written a session management plugin in node.js for Muneem web framework. Here is pseudo code to create a new session;
function createSession(){
// read encrypted session-id from the request
if( sessionId ){
// decrypt it
if (decryptedSessionId ) {
//read session detail from the store
options.store.get(decryptedSessionId, (err, sessionFromStore) => {
if(err){
throw Error(err);
}else if( sessionFromStore){
if( shouldRenew(sessionFromStore) ){
//delete previous session
options.store.destroy(sessionFromStore.id, err=> {
//update the session object in memory
});
}
}else{ //session detail is not present in store
// create new session
}
});
} else { //invalid or tempered session
// throw error
}
}else{ //session-id is not presnet in request
// create new session
}
}
As you can notice, I'm renewing a session when it is valid and satisfy certain conditions by deleting the previous session. But I don't update it in the store immediately. Instead, I update the session information in the store and set the cookies when the response is being sent to the client.
Now suppose a condition, when the server receives multiple requests with the same session-id which is eligible to renew. I renew the session on the first request.
Scenarios
Session is not updated in store. So I'll renew the previous session with another new session id. A user will have multiples session-id in this case.
Session is updated in the store. Now, the previous session will not be available in the store. I'll have to ask the user to login again if it is authorized session. Or I'll create another session.
How to handle this race condition?
Here is the full code, in case we need.
This situation seems not to occur if we authenticate the main route only.
I was previously thinking to authenticate every request. A server can receive multiple requests with the same session ID only on page load which includes access to the main route along with static files like CSS, js, etc. Static files are not required to be authenticated. Hence race condition should not occur.
I am trying to implement a login system that sends a confirmation email to the user in case he logs in from a new computer/browser.
I am using Nodejs, AngularJS and PassportJS.
Any pointers to where I could find resources for this will be greatly appreciated.
The client side can detect stuff like os/browser, so you can just POST that data up to the server whenever the client loads. Other than that, you can match usernames with IP-adresses, but if you're storing that kind of information you ought to hash the information before saving it.
Could be as simple as setting a session variable (https://github.com/expressjs/session)
if(req.user){ // so that it only triggers when the user has actually logged in
if(!req.session.thisBrowser) {
req.session.thisBrowser = true || 'this computer/browser' || 'whatever';
req.user.email('You have been logged in from ...'); // do your thing
}
}
I have a node/socket.io/express server that's connected to a HTML file (like so). So visiting the web address connects you to the server. I am trying to set up a system where by, said server is being run on multiple computers at a time and by way of some sort of username and password authentication, visiting the webpage with specific credentials connects you to one of the computers with those same credentials running the server.
Ive seen mention of "Redis" from previous similar questions but they are pretty old and im wondering if there is a newer or better way of achieving this.
You won't find a lot of up-to-date documentation since Express 4 is kind of new, so let me try to remedy that here :
Authentication in Express 4.x and Socket.IO 1.x
Let's start with a confusion I think you're making:
What is Redis?
Redis is a data structure engine. It allows you to store key/values pairs, nothing more (In this context). The only thing it can do for you when building your authentication system is storing the data, user info, session ids, etc. In your case, you can share a store between multiple machines, the same way you'd share a database, or a text file.
Redis
Authenticate user to node/express server
One of the ways you can do that is by using passport. Passport is a middleware dedicated to authentication on Node.js. It is made for use with Express and relatively easy to setup. There is an excellent tutorial series on how to setup passport with your express application, so I won't detail this part, please take the time to go through the series, it's invaluable knowledge.
Here's the link to the first part, which is the one I'll focus on for the next step.
Add socket.io to the mix
Socket.io doesn't have access to the session cookies that you create in part 1. To remedy that, we will use the passport-socketio module.
Passport-socketio requires a local session store, as opposed to a memory store. This means we need some way to store the session data somewhere, does that ring a bell?
Exactly, Redis.
You can try other stores, like mongoDB or MySQL, but Redis is the fastest.
In this example, I'll assume that your express app and passport are already operational and will focus on adding socket.io to the app.
Setup :
var session = require('express-session'); //You should already have this line in your app
var passportSocketIo = require("passport.socketio");
var io = require("socket.io")(server);
var RedisStore = require('connect-redis')(session);
var sessionStore = new RedisStore({ // Create a session Store
host: 'localhost',
port: 6379,
});
app.use(session({
store: sessionStore, //tell express to store session info in the Redis store
secret: 'mysecret'
}));
io.use(passportSocketIo.authorize({ //configure socket.io
cookieParser: cookieParser,
secret: 'mysecret', // make sure it's the same than the one you gave to express
store: sessionStore,
success: onAuthorizeSuccess, // *optional* callback on success
fail: onAuthorizeFail, // *optional* callback on fail/error
}));
Connect-redis is a session store package that uses redis (in case the name isn't obvious).
Final step :
function onAuthorizeSuccess(data, accept){
console.log('successful connection to socket.io');
accept(); //Let the user through
}
function onAuthorizeFail(data, message, error, accept){
if(error) accept(new Error(message));
console.log('failed connection to socket.io:', message);
accept(null, false);
}
io.sockets.on('connection', function(socket) {
console.log(socket.request.user);
});
The user object found in socket.request will contain all the user info from the logged in user, you can pass it around, or do whatever you need with it from this point.
Note : This setup will be slightly different for Socket.IO < 1.x
I'm currently developing an app which needs users and administrators. What I do right now is, I create an admin account on the client with username 'admin' and a default password that should be changed over the accounts-ui.
I do this because creating a user like this:
Accounts.createUser({
username : 'admin',
email : 'test#test.com',
password : 'changethispasswordovertheuserinterface',
profile : { type : 'admin' }
});
doesn't work for me on server side. That means I just create the admin in my client.js and just use this code to check if the admin is logged in.
Template.admin.isAdmin = function () {
var currentUser = Meteor.user();
// Is this hackable?
if (null !== currentUser) {
if ('admin' === currentUser.username) {
return true;
}
}
};
Is this the best way to approach this? And most importantly, is my site hackable like this (Could somebody fake it)?
Yes this is hackable, one could pull up the chrome inspector and modify this quite easily. Or even faster, by typing something like Template.admin.isAdmin = function () { return true; } into Chrome's web console
The best approach would be to only provide the information to the client from the servers end if the user is an admin. So this would mean using Meteor.allow to ensure the database can only be changed by an administrative user, if peforming ops from the client end.
It also depends a bit on what you want to use 'isAdmin' for too. If its content, you could generate the html on the server's end and send it down to the client in a Meteor.methods. At the moment the templating system doesn't provide for locking down the UI on the clients end depending on what the user's document contains.
For any administrative commands, you could use a Meteor.call at which point the user is vetted on the server's and and the transaction is performed there.
The answer on this thread works too AND the top-voted answer has code for a server side, Meteor method call.