I'm using sessions and cookies to authenticate the users. I would like to check for users having a cookie and if so i will set the sessions variables.
So basicly what i do is :
Check if sessions variables exist
If not, check if user has cookie
If he has a cookie, I compare the value in my database.
If everything's ok, I set up the session.
Now i'd like to have that process into a module so i don't have to paste that code into each routes of my site.
Let's say I've put all that code in a middleware route located at routes/middleware/check_auth.js.
How do I export this module so I can check in my route page if the user has auth or not, something like :
//routes/index.js
var check_auth = require('./middleware/check_auth');
module.exports = function(app){
app.get('/', check_auth, function(req, res){
if(variable_from_check_auth == true){
res.render('index_with_auth');
}else{
res.render('index_without_auth');
}
});
};
Btw, I'm not sure if it's the right way to do or if I simply have to :
Call the module on each routes.
Check for some sessions variables before rendering.
If someone could help me!
You can just export your middleware as simple as this(assuming you are using express session handler and cookie parser):
var userModel = require('./user');
module.exports = function check_auth(res, req, next) {
if (!res.session) {
req.send(401);
return;
}
userModel.isAuthenticated(req.session.id, function (result) {
if (!result) {
req.send(401);
return;
});
next();
});
};
Related
What is the best way to call a function on many but not all requests in a node express app? (An example would be a function which checks if the user is currently logged in)
What I did is to define a module exporting a checkLogin(...) function and to call this function on each corresponding api-request. E.g.:
Module auth:
module.exports = {
checkLogin: function(req, res, next) {
if (req.session.hasOwnProperty('user')) {
//if the user is logged in we pass through
next();
} else if (req.cookies.user == undefined || req.cookies.pass == undefined) {
res.render('login', { title: 'Login' });
} else {
User.checkLogin(req.cookies.user, req.cookies.pass, true, function(o) {
if (o != null) {
req.session.user = o;
next();
} else {
res.render('login', { title: 'Login' });
return;
}
});
}
}
};
Routes for /index:
//...
var auth = require('../middlewares/auth.js');
//...
router.get('/index', auth.checkLogin, function(req, res) {
//if we passed the auth.checkLogin step we render the index page
res.render('index', {
title: 'Index',
udata: req.session.user
});
});
In another route file:
//...
var auth = require('../middlewares/auth.js');
//...
router.get('/user/someAPICall', auth.checkLogin, function(req, res) {
...
});
Is this the way to go or are there better ways to do that? I could define a middleware function which I could include using app.use(function(){..}) in each route. The problem is that every request for this route would go through this function which is not what I want.
Routers (http://expressjs.com/en/guide/routing.html) are a great way to design your application. You could think of your URL paths as namespaces, and create a router for the namespace that requires user authentication.
Most likely your main /index page won't require immediate redirecting to login, since it's used for presentation purposes; but if required, then just include the auth.checkLogin as you did above.
For everything else where you need your user to be authenticated (e.g. everything under /user/*), you'd better create a scoped router
const router = express.Router();
router.use(auth.checkLogin);
router.get('/someAPICall', fn1, fn2);
router.get('/someOtherAPICall', fn3, fn4);
and then in your parent router or main app, just include the router:
app.use('/user', router);
which is just like defining:
app.use('/user/someAPICall', [auth.checkLogin, fn1, fn2]);
app.use('/user/someOtherAPICall', [auth.checkLogin, fn3, fn3]);
This gives you the advantage of creating modular route handlers - which makes them easier to adjust, reuse, etc. - and at the same time will keep auth.checkLogin, although always executed when the router is entered, just for the paths defined by the router.
In short, the approach would be: "execute function on all routes inside the router, but not on all the app requests".
If you cannot redesign your routes in this way, then yes, you'll always need to include auth.checkLogin in handlers list for the paths you only want to use.
I've just made an Node.js app modular by splitting up data models and routes into separate files.
My routes are exported by express.Router(). In these routes I would like to import queried values from my app.js to be rendered with the templates.
How would I in the easiest way save things lets say with app.locals or req.variableName?
Since the route using express.Router() ties it together with app.js, should I be using app.params() and somehow make these values accessible?
Using globals seems like a worse idea as I'm scaling up the app. I'm not sure if best practice would be saving values to the process environment either using app.locals.valueKey = key.someValue...
Big thanks in advance to anyone
If I understand the question correctly, you want to pass a value to a later middleware:
app.js:
// Let's say it's like this in this example
var express = require('express');
var app = express();
app.use(function (req, res, next) {
var user = User.findOne({ email: 'someValue' }, function (err, user) {
// Returning a document with the keys I'm interested in
req.user = { key1: value1, key2: value2... }; // add the user to the request object
next(); // tell express to execute the next middleware
});
});
// Here I include the route
require('./routes/public.js')(app); // I would recommend passing in the app object
/routes/public.js:
module.export = function(app) {
app.get('/', function(req, res) {
// Serving Home Page (where I want to pass in the values)
router.get('/', function (req, res) {
// Passing in the values for Swig to render
var user = req.user; // this is the object you set in the earlier middleware (in app.js)
res.render('index.html', { pagename: user.key2, ... });
});
});
});
I would like to send error messages back to the client without adding them to the url. Here is my attempt:
exports.register = function(req, res) {
if (req.body.password != req.body.password_repeat) {
res.locals.err = 'Passwords must match.';
res.locals.action = 'register';
res.redirect('/');
return;
}
...
exports.index = function(req, res) {
req.url = '/';
res.render('index', {
action: res.locals.action,
error: res.locals.error,
redirect: res.locals.redirect
});
};
So the redirect works fine and exports.index executes. The problem is that res.locals are gone by then. Is this because once I redirect it is considered a new req/res cycle? Any way I can pass this information through redirect without doing something like res.redirect('/?error=error')
You can use flash package from expressjs, but you need to have session middleware to use it. Also, you can use express-flash package from RGBboy but you need to have both cookieParser and session middlewares in this case.
I am building a crappy login system as a newbie. I have done this so far:
app.post("/verifyLogin",function(request,response){
var usr=request.body.username;
var pass=request.body.password;
userModel.find({$and:[{username:usr},{password:pass}]},function(err,user){
if(user.length==0)
{
response.redirect("/?msg=failed");
}
else
{
request.session.user=user;
response.redirect("/dashboard");
}
});
});
This works fine but after successful login i want to get the user details in the dashboard. I am clueless. Please shed some light.
EDIT
I have the following setup for dashboard in routes:
app.get("/dashboard",function(request,response){
response.sendfile('/lms/site/dashboard.html');
});
If you mean you want to pass the users' details to a template:
app.get('/dashboard', function(req, res) {
res.render('dashboard', {
user : req.session.user
});
});
This assumes a few things though:
you have a working templating setup;
you have a template called dashboard (with an extension matching your templating setup);
you're going to provide some sort of setup to make sure a user is logged in before they can open /dashboard.
EDIT: since you don't want to use templating, you could use AJAX to get the user details from the server into the client:
// server side
app.get('/userdata', function(req, res) {
// check if a user is logged in
...
// return the user details as JSON
res.send(req.session.user);
});
// client side (in 'dashboard.html', this assumes is will load jQuery)
$.getJSON('/userdata', function(user) {
// process user data, insert it into the DOM somewhere...
});
EDIT 2: to check if a user is logged in, you could create a middleware which would check for the existence of req.session.user and redirect to the login page if it's undefined:
var isLoggedIn = function(req, res, next) {
if (req.session && req.session.user)
next(); // user logged in, so pass
else
res.redirect('/'); // not logged in, redirect to login page
};
You would use the isLoggedIn middleware for all routes that require a user to be logged in:
app.get('/userdata', isLoggedIn, function(req, res) {
res.send(req.session.user);
});
I'm trying to configure passport-twitter in my locomotive project.
The problem is that nothing happens after hitting the /auth/twitter url.
Edit: I hit the controller but twitter seems to not be invoked.
What I did was set a match to /auth/twitter at routes.js and mapped this to an auth_controller.js
Something like the code below:
routes.js
this.match('auth/twitter/', 'auth#twitter');
this.match('auth/twitter/callback/', 'auth#callback');
auth_controller.js
var locomotive = require('locomotive')
, Controller = locomotive.Controller
, passport = require('passport');
var AuthController = new Controller();
AuthController.twitter = function() {
console.log('[##] AuthController.twitter [##]');
passport.authenticate('twitter'), function(req, res) {};
}
AuthController.callback = function() {
console.log('[##] AuthController.callback [##]');
passport.authenticate('twitter', { failureRedirect: '/show' }),
function(req, res) {
res.redirect('/list');
};
}
module.exports = AuthController;
I really don't know if that's the right way to use it with locomotive, any help will be very appreciated.
Cheers,
Fabio
Passport needs to be configured first. An example on how to do that can be found here. In the case of LocomotiveJS, the obvious place of putting that configuration would be an initializer:
// config/initializers/10_passport_twitter.js <-- you can pick filename yourself
module.exports = function(done) {
// At least the following calls are needed:
passport.use(new TwitterStrategy(...));
passport.serializeUser(...);
passport.deserializeUser(...);
};
Next, configure sessions and initialize Passport:
// config/environments/all.js
module.exports = {
...
// Enable session support.
this.use(connect.cookieParser());
this.use(connect.session({ secret: YOUR_SECRET }));
// Alternative for the previous line: use express.cookieSession() to enable client-side sessions
/*
this.use(express.cookieSession({
secret : YOUR_SECRET,
cookie : {
maxAge : 3600 * 6 * 1000 // expiry in ms (6 hours)
}
}));
*/
// Initialize Passport.
this.use(passport.initialize());
this.use(passport.session());
...
};
Next, configure routes:
// config/routes.js
this.match('auth/twitter/', 'auth#twitter');
this.match('auth/twitter/callback/', 'auth#callback');
Because passport.authenticate is middleware, it's easier to use a before hook in your controller:
// app/controllers/auth_controller.js
...
AuthController.twitter = function() {
// does nothing, only a placeholder for the following hook.
};
AuthController.before('twitter', passport.authenticate('twitter'));
AuthController.callback = function() {
// This will only be called when authentication succeeded.
this.redirect('/list');
}
AuthController.before('callback', passport.authenticate('twitter', { failureRedirect: '/auth/twitter' })};
Disclaimer: I haven't tested the code above, I'm basing it on my own code which I recently used in a project, and which uses passport-local instead of passport-twitter. However, the basics are pretty much similar, apart from the callback-URL which isn't needed for passport-local.