I'm trying to learn Express session and authentication handling.
For example:
app.post('/login', authCredentials, function(req, res) {
console.log("second")
});
function authCredentials(req, res, next) {
//this happens first
console.log(req.body) // => { username: etc, password: etc }
next();
}
My question is just how much should my authCredentials function do?
For example if the credentials are correct, I can do something like
res.redirect('/index'). Once I do that, however, what purpose does the second function have?
Other questions:
How would I handle invalid credentials?
If I make authCredentials just return true or false depending on the credentials, doesn't that break the middleware flow because it would never invoke next()?
Is it possible to access anything in authCredentials in the anonymous callback after it? Basically in the function(req, res) { }?
The answer depends on your authentication strategy i.e. are you using session identifiers, access tokens, etc.
In either case I suggest that you break out the credential exchange (aka login) from the authentication.
function usernamePasswordExchange(req,res,next){
var username = req.body.username;
var password = req.body.password;
callToAuthService(username,password,function(err,user){
if(err){
next(err); // bad password, user doesn’t exist, etc
}else{
/*
this part depends on your application. do you use
sessions or access tokens? you need to send the user
something that they can use for authentication on
subsequent requests
*/
res.end(/* send something */);
}
});
}
function authenticate(req,res,next){
/*
read the cookie, access token, etc.
verify that it is legit and then find
the user that it’s associated with
*/
validateRequestAndGetUser(req,function(err,user){
if(err){
next(err); // session expired, tampered, revoked
}else{
req.user = user;
next();
}
});
}
app.post('/login',usernamePasswordExchange);
app.get('/protected-resource',authenticate,function(req,res,next){
/*
If we are here we know the user is authenticated and we
can know who the user is by referencing req.user
*/
});
Disclaimer: I work at Stormpath and we spend a lot of time writing
authentication code :) I just wrote our newest library, stormpath-sdk-express,
which has a concrete implementation of my suggestions
You want to add your authCredentials middleware to every end point that needs authentication. app.post('/login') usually does not need any as you want to access this end point to actually get credentials in the first place.
When credentials are correct/valid you simply invoke next() and the workflow will jump to the next middleware or the actual end point. If there was an error, invoke next() with an error object like next(new Error('could not authenticate'); for instance. Add an error route to your general routing and the error will be handled there:
app.use(function(err, req, res, next) {
res.render('error', err);
});
Should be answered by now.
A middleware does not return a value. It either calls next() or ends the process differently by calling res.send().
There are different approaches to pass variables from one middleware to another. The most common is probably to attach the desired value to the req parameter.
authenticate is an asychronous function in the following example:
function authCredentials(req, res, next) {
authenticate(req.body, function(err, user) {
if (err) {
return next(err);
}
req.user = user;
next();
});
}
Related
I need to check if a user is an administrator OR if the user is the same as the one requested. I tried but in one case it doesn't work.
router.get('/:id', isSigned, isSameUser || isAdmin, getUser)
Is there a way to do what I want without creating a dedicated function?
You could write a different middleware to avoid unnecessary middleware chaining, which you can call Auth or AuthMiddleware, the name you want is up to you. In this middleware you could do the logic for checking the user's authentication status, something similar to this:
function AuthMiddleware(request, response, next) {
if(isSigned) {
// User is signed in, so we can proceed doing the next check
if(isSameUser || isAdmin || getUser) {
// Validation checks out, go to next middleware without warnings.
return next();
} else {
// User failed to show some ID and cannot access the page.
return next(new Error("Unauthorized."));
}
} else {
// The user has not signed in to your application
return next(new Error("Unauthenticated"));
}
}
router.get('/:id', AuthMiddleWare, (request, response) => {
// DO LOGIC
});
Place this middleware inside a new file, and every time you need it, you can just import it rather than copy and paste the same code over and over.
! Note that this is a rough sketch code just to put you on the right track, so you most likely have to adjust this snippet to your own needs and logic.
The benefits of having a single authentication middleware, is so you can use it on every route, so you don't have to use so many chaining in your requests.
I have a login route that if a user signs in successfully, it attaches the id of the user from the database to the req.session.user property of the request object:
router.route('/za/login')
.get(onlyGuest, (req, res) => {
res.render('login', { layout: false });
})
.post(onlyGuest, async(req, res) => {
try {
const user = await User.findByCredentials(req.body.email, req.body.password);
req.user = user;
req.session.user = req.user._id;
res.redirect('/');
} catch (err) {
console.log(err);
res.status(400).render('login', {
layout: false,
message: {
error: 'login failed!',
email: req.body.email
}
});
}
});
After this property(req.session.user) has been set, a user is directed to the home route. But there is a middleware that allows access to a certain route upon authenticated, otherwise it redirects to another page. Here is the code for the middleware:
let regex = /^\/za(\/.*)?$/; /*a route prefixed with '/za', followed by zero or /anything_else*/
app.use(async (req, res, next) => {
let url = req.originalUrl;
if(!req.session.user && !regex.test(url)) {
console.log('You are trying to access an authenticated route. Login first!');
console.log(req.session.user);
if(url === '/') {
res.redirect('/za');
return;
}
res.redirect('/za/login?forbidden');
return;
}
/*req.session.user property is true or has a value set*/
if (req.session.user) {
try {
const user = await User.findById(req.session.user);
if(!user) {
return res.redirect('/za/login?forbidden');
}
req.user = user;
return next();
} catch (err) {
console.log(err);
return res.render('errors/500');
}
}
});
After successfully logging in via the login route(providing the correct credentials), i wonder why the request.session.user is still undefined and therefore redirecting me to a page accessible by unauthenticated users.
I have a feeling that i am missing the timing of order of execution. But this is just a feeling.Actually i don't know where i may have errored.
Short answer: You have to set the session or token or whatever you use for authentication on the client side.
Long answer:
You have the req object and the res object available in your handlers. The req object is the request and data sent by the client (web browser). The res object is what is sent back to the client as a response. As soon, as you call any terminating method on the res object like res.render or res.send, the handling of the specific client request is done and the linked objects are "thrown away". Therefore, setting any value on the req object does never affect the client, as the req object never gets sent back to the client. Modifying the req object would only be of use if you want to access some values on the req object in another middleware dealing with the same request.
So, you have to send the user id in the response object back to the client upon successful authentication and on the client side, take this id and set it e.g. as permanent header to be sent along with every request sent from the client from now on.
Hint: This way of authentication is not very secure. Usually, you would generate a token - e.g. a JSON Web Token (JWT-token) on the server-side upon successful authentication, send this token to the client and verify this token on each subsequent request.
Hint 2: Search (on Stackoverflow) for e.g. "node.js authentication" to get more input.
I am relatively new to node.js and trying to write a route which will check the authorization of all the users if the mentioned endpoint starts with /api.
I read that for optional value ? can be used {_id?} like this but is it possible to use it like {?}.
and How do I call this route into another route which actually does the job of GET method?
Currently the method looks like this
server.route({
method: 'GET',
path: '/api/something/nothing/{_id?}',
handler: function (request, reply) {
Controller.vcontroller.get(request.headers.authorization, request.params, function (err, success) {
console.log(request.headers);
console.log(request.headers.authorization);
if (err) {
reply(func.sendError(err));
} else {
reply(func.sendSuccess(APP_CONSTANTS.STATUS_MSG.SUCCESS.DEFAULT, success)).code(200);
}
});
},
config: {
description: 'desc',
tags: ['api', 'order'],
validate: {
headers: func.authorizationHeaderObj,
params: {
order_id: Joi.string().required().trim(),
_id: Joi.string().optional().trim()
},
failAction: func.failActionFunction
},
plugins: {
'hapi-swagger': {
responseMessages: APP_CONSTANTS.swaggerDefaultResponseMessages
}
}
}
}
});
when I pass endpoint /api it should check the authorization for all the users for GET PUT POST & DELETE respectively.
In other words I want global authorisation which is route specific
Can you suggest a way to implement it?
To check if all requests to endpoints starting with /api are authorised you can use middleware, i.e.
app.use('/api', (req, res, next) => {
// this uses passport.js for auth, you could use something else
if(req.isAuthenticated()) {
return next();
}else{
return res.status(401).send();
}
});
I'm not entirely clear that I'm understanding your question, so I'll restate what I think you're asking and then answer. If I am not understanding, please clarify.
What I think you're asking is whether you can run a check anytime someone hits a URL path that starts with /api, probably for user authorization purposes.
The answer is, yes, you can just incorporate that into a middleware (though I strongly recommend Passport for all your authentication needs.
That said, if you're using Express, then you can just add middleware at the start of your routing like so:
app.use('/api', (req, res, next) => {
// trivial implementation, not for production
const users = ['bob', 'mari', 'hasan'];
if (users.indexOf(req.user) >= 0) {
// user is authorized
return next();
}
// Forbidden
return res.sendStatus(403);
});
Put that before your other routes, and any route starting with /api will pass through this first. Obviously, I'm assuming you have populated req.user here somehow, but if you're using Passport as I suggested that will be done for you.
I'm an node.js and express.js noob. This question may seems silly but I'm really in confusion.
I'm trying to configure Local Strategry authentication by using passport. As shown in the official documentation, we can figure this Local Strategy by the following code,
passport.use(new LocalStrategy(
function(username, password, done) {
User.findOne({ username: username }, function (err, user) {
if (err) { return done(err); }
if (!user) { return done(null, false); }
if (!user.verifyPassword(password)) { return done(null, false); }
return done(null, user);
});
}
));
My confusion is about the done callback function. When the official docs show this local strategy using as a middleware in the route handler, there is no need to pass the function parameter for this done callback.
app.post('/login',
passport.authenticate('local'),
function(req, res) {
res.redirect('/');
});
So, isn't this done callback function will be null if we don't provide the function parameter? If not, what is that done callback function and what processes will be happening in this done callback function?
done is a method called internally by the strategy implementation.
Then it navigates you, as you can see, to one of the success / error / fail methods (again, by the implementation. there are more options).
Each of these options may calls to the next, where in your snippet code is the following:
function(req, res) {
res.redirect('/');
});
When success is called, it can attach the user to the request or do other things, depending on your needs (it looks for the options you pass to passport.authenticate). If you want to determine when next will be called, you should use custom callback which gives you more flexibility.
I strongly recommend that you read the source.
It's now 2022 and I had the same question. The passport documentation has improved and it describes the done method (also called cb) here: https://www.passportjs.org/concepts/authentication/strategies/#verify-function. You will need to call this yourself in your strategy's verify function.
A verify function yields under one of three conditions: success, failure, or an error.
If the verify function finds a user to which the credential belongs, and that credential is valid, it calls the callback with the authenticating user:
return cb(null, user);
If the credential does not belong to a known user, or is not valid, the verify function calls the callback with false to indicate an authentication failure:
return cb(null, false);
If an error occurs, such as the database not being available, the callback is called with an error, in idiomatic Node.js style:
return cb(err);
I'm using Express JS and Passport JS for my app.
I want to give a new user the opportunity to automatically login, once, by a specific URL. I can get the user from the database with the information from the URL, so I have an User object (with id, email, hashed password etc.) but I don't know how I can use passport to authenticate the user and login.
I tried executing below function with the user object I got from the database:
req.login(user, function(err) {
if (err) { return next(err); }
return res.redirect('/users/' + req.user.username);
});
source: http://passportjs.org/guide/login/
But that didn't work. Guess it's just because the user object contains the hashed password...
Anyone who ever tried this before and can tell me how it works?
Maybe https://github.com/yarax/passport-url strategy will be useful for you
Base logic is getting argument from url
UrlStrategy.prototype.authenticate = function(req, options) {
var self = this;
function verified(err, user, info) {
if (err) { return self.redirect(self.failRedirect); } // redirect in fail
self.success(user, info); // done callback
}
this._verify(req.query[this.varName], verified);
};
Full example here https://github.com/yarax/passport-url/blob/master/index.js
Heyo, so while #Rax Wunter is totally right, I just saw this question and wanted to say it is NOT A GOOD IDEA to do what you're doing here. You should never be passing a hashed password in a URL string ever. This is a really bad security concern.
What you should do instead is use something like a JSON Web Token (JWT). There are lots of libraries to help with this, but the basic flow goes something like this:
Wherever you are generating your URL, you'll instead generate a JWT that contains the user ID in it.
You'll then build a URL that looks like: https://somesite.com/?token=
On your https://somesite.com endpoint, you'll read in the token, validate it using the JWT library (and a shared secret variable), and this will confirm this token was unmodified (eg: you KNOW this user is who they claim to be).
This strategy above is really great because it means you can safely log someone in, in a trusted way, without compromising security or leaking a password hash at all.
There is not need of any additional module or passport-strategy for this. Adjust below code according to your use case;
router.get('/url/:token', (req, res) => {
User.findOne({token: req.params.token}, (err, user) => {
req.login(user, {}, function(err) {
if (err) { console.error(err); }
else return res.redirect("/home.html");
});
});
});