How to go about creating temporary authentication for a website? - javascript

I'm new to authentication with websites, and I've been doing a lot of reading on the different kinds of user authentication (for example session vs token authentication) you can use. However, a lot of it seems more than what I need, and I'm not sure which ones will be suitable for my cause.
My idea is to generate temporary user accounts and passwords that will expire after the first use. I want this integrated with my website, so they have one chance to view restricted pages, after which they will not allowed access to those parts again (unless provided with new credentials).
Any direction in the right step will be appreciated.
Update: I'm using Javascript(Node) as my server side language

Session-based authentication is actually incredibly lightweight if you're using a Node backend, due to most (if not all) webserver libraries supporting "middleware", which modify requests before they hit your route functions. The Express-compatable middleware client-sessions is fantastic for this, and I used it previously in a project with great success. It adds a cookie on the first request a user makes to your site which identifies them, and if at some point they log in, you can flag that session as authenticated, store session information, and other data related to them specifically.
Assuming you want both login & logout, the simplest way would to be to use POSTs over HTTPS to login & logout routes. Inside of the resolution for the login route, you would simply "mark for deletion" inside whatever database you're working with.
An example might look like this:
var app = express();
function authenticate(user, pw){
//do your application specific login verification here
}
function deleteAccount(user){
//do your application specific user removal here
}
app.use(require("express-session")({
secret : "YOUR-SECRET-KEY-HERE"
cookieName : "Session"
//any other desired config options go here
})
app.post("/login", function(req, res){
var user = req.body.user;
var pw = req.body.pw;
req.Session.isAuthenticated = authenticate(user, pw)
if(req.Session.isAuthenticated){
markForDeletion(user, pw);
}
res.write("logged in as: " + user);
res.end();
});
app.post("/logout", function(req, res){
deleteAccount(req.Session.username);
req.Session.username = "";
req.Session.isAuthenticated = false;
res.write("logged out!");
res.end();
});

Related

Node basic auth with loading credentials from Database

I am using NodeJs on server and I need to add some basic auth function, which asks the user for the credentials before any file (like html or js files) is downloaded to the user's browser. When I tried to view the cource code when I am not log in, I can't be able to access it like in this.
This works fine with basic-auth module, but only for one username and password.
app.use(basicAuth('username', 'password'));
And there is the problem - I need to load this credentions from Database and check if the user is admin and compare his encrypted password.
I tryed this:
// ****
var basicAuth = require('basic-auth');
// ****
app.use(function (req, res, next) {
var user = basicAuth(req);
if (user) {
var c = mysql.createConnection(db);
var q = "SELECT * FROM user WHERE email = '" + user.name + "' AND admin = 1;";
c.query(q, function (error, result) {
c.end();
if (error) {
console.log(error);
res.set('WWW-Authenticate', 'Basic realm="example"');
return res.status(401).send();
} else {
if (result.length === 1 &&
bcrypt.compareSync(user.pass, result[0].password)) {
return next();
} else {
res.set('WWW-Authenticate', 'Basic realm="example"');
return res.status(401).send();
}
}
});
} else {
res.set('WWW-Authenticate', 'Basic realm="example"');
return res.status(401).send();
}
});
This works fine, but this function is called every time, when the browser communicates with the server, which is about 70 times on loading simple page (It is not asking the user for the credentials again, "only" run this function). This function lasts about 200ms on every request (because of the query and password comparing), in sum the loading lasts about 15 seconds, which is not acceptable.
Can you someone please help me, how to figured out this problem or recommend me some other NodeJs library, which can help me with that?
Thank you.
So if you store you hash in the DB and the Admin Flag in your DB, why don't you do the following Select request :
var q = "SELECT * FROM user WHERE email = '" + user.name + "' AND password = '" + bcrypt.hashSync(user.pass) + "' AND admin = '1';";
If you get only one result it is OK, otherwise it is refused...
If I understand your question, every request to the server is running your authentication, causing page loads to be slow. I don't understand how someone can log in if all the routes are behind the basic auth check you are doing, but that is besides the point.
The way I set my node apps up are to create an array of functions for auth and pass these in to the specific routes where I require authentication, this way it is being run for each resource I may be serving. For example(psuedocode-ish):
// this is the authentication function array, each function in the array
// will be run when this array is called on a route, require next()
// after successful auth to continue on through the array/route handlers
// for your example, adminAuth would be the anonymous
// function in your app.use method
var auth = [adminAuth];
// some random route examples
// this is an unprotected route
app.get('/login', loginHandler);
// these are protected routes and will have auth called
app.get('/api/users', auth, getUserHandler);
app.post('/api/user', auth, postUserHandler);
So in conclusion, you can put the auth call only on protected resources, this way images, html, js, css, etc can be served without running the auth check everytime.
There are a few issues with your code. I recognize that you need to do the password check every request, but you don't necessarily need to hit the database every time. Assuming that you must do HTTP Basic (not recommended anymore, and I hope if you are, that you're doing it over HTTPS), then one major improvement you can make is if you create some kind of fast data cache. The easiest form of this would be in-memory (assuming you're OK with each instance of your app keeping its own copy), but you could also use something like memcached or redis for it instead depending on your use case.
A really trivial implementation would be just keeping an in-memory JS object with a key equal to the username and the value equal to the data returned from the db. Something like:
var userCache = {}; // something local to the module is the easiest way
Then, when you are going to query the db, you just check the userCache first:
if(userCache[user.name]) // check password
else
// do your query and insert a successful result into the userCache for later.
Just doing that should significantly reduce your response times per resource, though again for a production solution you'll probably want multi-tier caching (e.g. an in-memory cache and a redis or memcached cache to help performance across multiple instances of your app).
Second, you should use the callback version of the bcrypt check. It won't be faster on a given request, but it may give your application more overall request capacity, which should help.
Third, your actual SQL query is a bad idea for three reasons. First, in most databases, you'll have worse performance for using a SELECT * vs selecting the specific fields you want. This is both on the DB side (query cache optimization) as well as on the Node side (time to deal with fields getting converted / cast between a string response and a JS object for example). Second, the string concatenation you're doing is an invitation for SQL Injection attacks. Look into using parameterized queries instead. Lastly, if you know your users will be unique, you should also add a LIMIT 1 to the query, since you're only expecting one result and again the DB can do some optimization around that.

Parse Server Node.js SDK: Alternative to Parse.User.become?

I want to completely dissociate my client app from Parse server, to ease the switch to other Baas/custom backend in the future. As such, all client request will point to a node.js server who will make the request to Parse on behalf of the user.
Client <--> Node.js Server <--> Parse Server
As such, I need the node.js server to be able to switch between users so I can keep the context of their authentification.
I know how to authentificate, then keep the sessionToken of the user, and I ve seen during my research than the "accepted" solution to this problem was to call Parse.User.disableUnsafeCurrentUser, then using Parse.User.become() to switch the current user to the one making a request.
But that feels hackish, and I m pretty sure it will, sooner or later, lead to a race condition where the current user is switched before the request is made to Parse.
Another solution I found was to not care about Parse.User, and use the masterKey to save everything by the server, but that would make the server responsible of the ACL.
Is there a way to make request from different user other than thoses two?
Any request to the backend (query.find(), object.save(), etc) takes an optional options parameter as the final argument. This lets you specify extra permissions levels, such as forcing the master key or using a specific session token.
If you have the session token, your server code can make a request on behalf of that user, preserving ACL permissions.
Let's assume you have a table of Item objects, where we rely on ACLs to ensure that a user can only retrieve his own Items. The following code would use an explicit session token and only return the Items the user can see:
// fetch items visible to the user associate with `token`
fetchItems(token) {
new Parse.Query('Item')
.find({ sessionToken: token })
.then((results) => {
// do something with the items
});
}
become() was really designed for the Parse Cloud Code environment, where each request lives in a sandbox, and you can rely on a global current user for each request. It doesn't really make sense in a Node.js app, and we'll probably deprecate it.
I recently wrote a NodeJS application and had the same problem. I found that the combination of Parse.User.disableUnsafeCurrentUser and Parse.User.become() was not only hackish, but also caused several other problems I wasn't able to anticipate.
So here's what I did: I used
Parse.Cloud.useMasterKey(); and then loaded the current user by session ID as if it was a regular user object. It looked something like this:
module.exports = function(req, res, next) {
var Parse = req.app.locals.parse, query;
res.locals.parse = Parse;
if (req.session.userid === undefined) {
res.locals.user = undefined;
return next();
}
Parse.Cloud.useMasterKey();
query = new Parse.Query(Parse.User);
query.equalTo("objectId", req.session.userid);
query.first().then(function(result) {
res.locals.user = result;
return next();
}, function(err) {
res.locals.user = undefined;
console.error("error recovering user " + req.session.userid);
return next();
});
};
This code can obviously be optimized, but you can see the general idea. Upside: It works! Downside: No more use of Parse.User.current(), and the need to take special care in the backend that no conditions occur where someone overwrites data without permission.

How can I impersonate another user with Passport.js in Node?

Using Passport.js in Node, is there a way for me to allow one user to impersonate another? eg. as an Administrator in the application, I want to be able to log in as another user, without knowing their password.
Most simply, I would be satisfied if I could change the serialized user data (user ID) so when deserializeUser is called it will just assume the identity of the alternate user. I've tried replacing the value at req._passport.session.user and the value at req.session.passport.user but the net effect is just that my session seems to become invalid and Passport logs me out.
Passport provides a req.logIn method in case you want to do the authentication manually. You can use it to login any user even regardless of authentication.
Here's how you can use it. Have the Admin login normally, who'll have an isAdmin flag set.
Then place a middleware before passport.authenticate in your login route. This will login the new user based only on their username, if the currently logged in user isAdmin.
app.post('/login',
function forceLogin(req, res, next) {
if (!req.user.isAdmin) return next(); // skip if not admin
User.findOne({
username: req.body.username // < entered username
}, function(err, user) {
// no checking for password
req.logIn(user);
res.redirect('/users/' + user.username);
});
},
passport.authenticate('local'),
function(req, res) {
res.redirect('/users/' + req.user.username);
}
);
I have another way to impersonate, because:
I didn't want to mess with internals of authentication/passport like
session storage / logIn / etc. You must understand them really well
and also they are prone to change so I'd say it's not an option for
me.
Also, I'd like to still be able to tell if action is made from
superuser (impersonated) or normal user (not impersonated).
What I do is:
Have a route for user with superadmin role to impersonate, like /superadmin/impersonate?username=normaluser1 which sets req.user.impersonated.userid = normaluser1.userid
Then I have a middleware, which checks if user is superadmin and is impersonated:
if (req.user.isAdmin && req.user.impersonated) {
req.user.userid = req.user.impersonated.userid;
}
Also, I have found this to be a good article about user impersonation. Similar to my approach, and good for inspiration for building something similar.
The answer to your question is basically: no. The reason is this: the sessions library that is being used 99% of the time is signing the cookies, so if you tamper with the data the web server will reject it.
The way around this is to write your own passport authentication strategy that obviously doesn't do this, but I'm assuming you're talking about working with the built-in strategies here.

Access Meteor.userId from outside a method/publish

I'm currently writing a server-centric package for Meteor, and the relevant code looks something like this:
__meteor_bootstrap__.app.stack.unshift({
route: route_final,
handle: function (req,res, next) {
res.writeHead(200, {'Content-Type': 'text/json'});
res.end("Print current user here");
return;
}.future ()
});
This is obviously a relatively hacky way of doing things, but I need to create a RESTful API.
How can I access Meteor.userId() from here? The docs say it can only be accessed from inside a method or publish. Is there any way around that?
Things I've tried:
Capture it from a publish using Meteor.publish("user", function() { user = this.userId() });
Get the token + user id from the cookies and authenticate it myself using something like Meteor.users.findOne({_id:userId,"services.resume.loginTokens.token":logintoken});
Create a method called get_user_id and call it from inside my code below.
The thing that you need to target first is that to get something that can identify the user from headers (especially because you want to get the username at a point where no javascript can run).
Meteor stores session data for logins in localStorage, which can only be accessed via javascript. So it can't check who is logged in until the page has loaded and the headers have been passed.
To do this you need to also store the user data as a cookie as well as on localStorage:
client side js - using cookie setCookie and getCookie functions from w3schools.com
Deps.autorun(function() {
if(Accounts.loginServicesConfigured() && Meteor.userId()) {
setCookie("meteor_userid",Meteor.userId(),30);
setCookie("meteor_logintoken",localStorage.getItem("Meteor.loginToken"),30);
}
});
server side route
handle: function (req,res, next) {
//Parse cookies using get_cookies function from : http://stackoverflow.com/questions/3393854/get-and-set-a-single-cookie-with-node-js-http-server
var userId = get_cookies(req)['meteor_usserid'];
var loginToken = get_cookies(req)['meteor_logintoken'];
var user = Meteor.users.findOne({_id:userId, "services.resume.loginTokens.token":loginToken});
var loggedInUser = (user)?user.username : "Not logged in";
res.writeHead(200, {'Content-Type': 'text/json'});
res.end("Print current user here - " + loggedInUser)
return;
}.future ()
The cookie allows the server to check who is logged in before the page is rendered. It is set as soon as the user is logged in, reactively using Deps.autorun
My solution was inspired by the server part of #Akshat's method. Since I'm making a RESTful API, I just pass the userId/loginToken in every time (either as a param, cookie or header).
For anyone interested, I bundled it as a package: https://github.com/gkoberger/meteor-reststop

Node.js preresponse with Express

So I'm doing a web page with Node.js and Express framework. I already have registration and login (I'm holding users id in a session). Next step is to render different page whenever a user is authenticated.
Some of those pages require a User object which is just a mapping of a user from my db. So whenever an authenticated request comes I need to retrieve the user from my db. But writing this code every time seems to be a bad way to do this. So here's the question: is it possible (and if it, then how?) to do, say preresponse, so I can automaticaly retrieve User object whenever I know that the user is authenticated and THEN do the main response?
Middleware is what you are referring to. Middleware is just a function that gets called sequentially when the route is triggered. So to have a loadUser function:
function loadUser(req, res, next) {
// You would fetch your user from the db
var user = users[req.params.id];
if (user) {
req.user = user;
next();
} else {
next(new Error('Failed to load user ' + req.params.id));
}
}
app.get('/user/:id', loadUser, function(req, res){
res.send('Viewing user ' + req.user.name);
});
You can define as many middleware functions as your need. Just be sure to call next() at the end to pass the route handling on to the next function.
This EXACT example is covered in the express.js route middleware docs. Go read it and you'll see the pattern of using middleware to factor out common functionality that you need at many route paths in your app.

Categories

Resources