I'm working on developing a node.js (using node.js v0.10.26) express web application using Google's oauth 2.0 with passport.js. Up to now it's worked fine, as the route definitions were contained within the app.js file. However, now that I'm breaking up the route logic into separate modules, I'm running into problems. My file structure is broken up like this:
authentication
├── cert.pem
└── key.pem
bin
└── www
data
└── (mongo database files)
node_modules
└── (node module files for: body-parser, cookie-parser,
debug, express, express-session, jade, method-override,
mongodb, mongoose, morgan, passport, passport-google, static-favicon)
routes
├── account.js
├── index.js
└── login.js
views
├── account.jade
├── index.jade
├── layout.jade
└── login.jade
app.js
oauth.js
package.json
The .pem files are for the ssl key and certificate. www is a script which establishes an https server:
var debug = require('debug')('kaiwaquiz');
var fs = require('fs');
var http = require('http');
var https = require('https');
var app = require('../app');
var options = {
key: fs.readFileSync('./authentication/key.pem'),
cert: fs.readFileSync('./authentication/cert.pem')
};
var port = process.env.PORT || 3000;
var server = https.createServer(options, app).listen(port, function(){
debug('Express server listening on port ' + server.address().port);
});
It calls uses app.js (note the comment above the /login route):
//Express Dependencies
var express = require('express');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var logger = require('morgan');
var session = require('express-session');
var methodOverride = require('method-override');
var favIcon = require('static-favicon');
//Passport Dependencies
var config = require('./oauth.js');
var passport = require('passport');
var GoogleStrategy= require('passport-google').Strategy;
//Mongo Dependencies
var mongodb = require('mongodb');
var mongoose = require('mongoose');
//Server Variables
var app = express();
var indexRoutes = require('./routes/index');
var loginRoutes = require('./routes/login');
var accountRoutes = require('./routes/account');
//Serialize and Deserialize
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(obj, done){
done(null, obj);
});
//Passport Configuration
passport.use(new GoogleStrategy({
clientID: config.google.clientID,
clientSecret: config.google.clientSecret,
returnURL: config.google.returnURL,
realm: config.google.realm
},
function(accessToken, refreshToken, profile, done){
process.nextTick(function(){
app.locals.currentUserName = refreshToken.displayName;
return done(null, profile);
});
}
));
//Configure View Engine
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
//Process Static Content
app.use(favIcon());
app.use(express.static(__dirname + '/public'));
//Process Dynamic Content
app.use(cookieParser());
app.use(bodyParser());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded());
app.use(methodOverride());
app.use(session({secret: 'its_a_secret'}));
app.use(passport.initialize());
app.use(passport.session());
//Log results
app.use(logger('dev'));
function ensureAuthenticated(req, res, next) {
if(req.isAuthenticated()) {return next();}
res.redirect('/login');
}
//Application Routes
///THE FOLLOWING WORKS FINE///
app.get('/', indexRoutes);
///THE FOLLOWING /LOGIN ROUTE FAILS UNLESS THE loginRoutes MODULE IS REPLACED BY function(req, res){ res.render('login'); }///
app.get('/login', loginRoutes);
app.get('/account', ensureAuthenticated, accountRoutes);
app.get('/auth/google', passport.authenticate('google'), function(req, res){});
app.get('/auth/google/callback', passport.authenticate('google', {failureRedirect: '/'}),
function(req, res){
res.redirect('/account');
}
);
app.get('/logout', function(req, res){
req.logout();
res.redirect('/');
});
module.exports = app;
The index route works fine, and uses index.js:
var express = require('express');
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res) {
res.render('index');
});
module.exports = router;
The login route fails, even though login.js employs the same format:
var express = require('express');
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res) {
res.render('login');
});
module.exports = router;
It still fails, even if I use index.js, and only works if I put the res.render() function directly in the code like this:
app.get('/login', function(req, res){
res.render('login');
});
The oauth.js file just contains the client information for connecting using google.com
Does anyone have any ideas as to why this problem is occuring?
Edit:
If I change the /login route, the /account route breaks in the same way that /login did. So, this app.js works fine:
//Express Dependencies
var express = require('express');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var logger = require('morgan');
var session = require('express-session');
var methodOverride = require('method-override');
var favIcon = require('static-favicon');
//Passport Dependencies
var config = require('./oauth.js');
var passport = require('passport');
var GoogleStrategy= require('passport-google').Strategy;
//Mongo Dependencies
var mongodb = require('mongodb');
var mongoose = require('mongoose');
//Server Variables
var app = express();
var indexRoutes = require('./routes/index');
var loginRoutes = require('./routes/login');
var accountRoutes = require('./routes/account');
//Serialize and Deserialize
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(obj, done){
done(null, obj);
});
//Passport Configuration
passport.use(new GoogleStrategy({
clientID: config.google.clientID,
clientSecret: config.google.clientSecret,
returnURL: config.google.returnURL,
realm: config.google.realm
},
function(accessToken, refreshToken, profile, done){
process.nextTick(function(){
app.locals.currentUserName = refreshToken.displayName;
return done(null, profile);
});
}
));
//Configure View Engine
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
//Return Static Content
app.use(favIcon());
app.use(express.static(__dirname + '/public'));
//Return Dynamic Content
app.use(cookieParser());
app.use(bodyParser());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded());
app.use(methodOverride());
app.use(session({secret: 'its_a_secret'}));
app.use(passport.initialize());
app.use(passport.session());
//Log results
app.use(logger('dev'));
function ensureAuthenticated(req, res, next) {
if(req.isAuthenticated()) {return next();}
res.redirect('/login');
}
//Application Routes
app.get('/login', function(req, res){
res.render('login');
});
app.get('/account', ensureAuthenticated, function(req, res){
res.render('account', {user: req.app.locals.currentUserName});
});
app.get('/', function(req, res){
res.render('index');
});
app.get('/auth/google', passport.authenticate('google'), function(req, res){});
app.get('/auth/google/callback', passport.authenticate('google', {failureRedirect: '/'}),
function(req, res){
res.redirect('/account');
}
);
app.get('/logout', function(req, res){
req.logout();
res.redirect('/');
});
module.exports = app;
According to the Router documentation, I think you need to do
app.use('/login', loginRoutes);
instead of
app.get('/login', loginRoutes);
Same goes for your index and other routes that are using express.Router().
try the following as login.js:
var express = require('express');
var router = express.Router();
/* GET login page. */
router.get('/login', function(req, res) {
res.render('login');
});
module.exports = router;
The documentation says:
// will handle any request that ends in /events
// depends on where the router is "use()'d"
router.get('/events', function(req, res, next) {
// ..
});
Through some trial and error, I've been able to determine that external routing modules must be use()'d, instead of get()'d, like mscdex indicated. However, the end routes must use an http verb like get(), post(), etc... The /account route, which currently uses a get(), contains two methods, the ensureAuthenticated(), and inline rendering method. This is because the http verb methods can contain more than one method to be executed, as opposed to the use(). For the future, it seems better to do the authentication within the routing modules themselves, as the method to do so is added to the request variable. Thanks for the assistance!
Related
I am trying to retrieve output on other url / directory in nodejs but it's Display Error ("Cannot GET /test").
Please Suggest me what i have to do to get my output on ("http://localhost:8080/test").
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
var multer = require('multer');
var upload = multer();
var session = require('express-session');
var cookieParser = require('cookie-parser');
app.set('view engine', 'pug');
app.set('views','./views');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(upload.array());
app.use(cookieParser());
app.use(session({secret: "Your secret key"}));
var Users = [];
app.get('/', function(req, res){
res.render('signup');
});
app.post('/signup', function(req, res){
if(!req.body.id || !req.body.password){
res.status("400");
res.send("Invalid details!");
} else {
Users.filter(function(user){
if(user.id === req.body.id){
res.render('signup', {
message: "User Already Exists! Login or choose another user id"});
}
});
var newUser = {id: req.body.id, password: req.body.password};
Users.push(newUser);
req.session.user = newUser;
res.redirect('/test');
}
});
app.listen(8080);
OutPut : Cannot GET /test
You did not specify a route for /test. You should have
app.get('/test', function(req, res, next){
res.render('test'); // This should have a view
});
Here i writing sample. create views folder in root folder and add one test.pub file to views then just run
Note:dont forget to install pug and express
var express = require('express')
var app = express()
app.set('view engine', 'pug');
app.set('views','./views');
// here am directly sending response without using view engine
app.get('/', function (req, res) {
res.send('hello world')
})
// here am sending response with using view engine
app.get('/test', function(req, res){
res.render('test');
});
app.listen(8000,function(){
console.log(" Server is running on port 8000");
});
I wanted to separate each group of routes in a file. I need to use isAuthenticated in most routes to restrict access. When I put all routes in the same file it works.
When I use module.exports = isAuthenticated in auth.js and in order.js I use var auth = require('auth.js'); I will get a error of 'isAuthenticated is not defined' when I go to http://localhost:3000/order the routes defined in auth.js just works as expected.
I'm using Express.js middleware 'Passport local strategy' (followed tutorial from tutsplus and downloaded github repository from here).
My project structure:
bin/www
models/auth/init.js
models/auth/user.js
models/auth/signup.js
models/auth/login.js
models/order.js
views/error.pug
views/index.pug
views/home.pug
views/layout.pug
views/order.pug
views/register.pug
routes/order.js
routes/auth.js
public/stylesheets/style.css
app.js
db.js
./routes/auth.js:
var express = require('express');
var router = express.Router();
var isAuthenticated = function (req, res, next) {
// if user is authenticated in the session, call the next() to call the next request handler
// Passport adds this method to request object. A middleware is allowed to add properties to
// request and response objects
if (req.isAuthenticated())
return next();
// if the user is not authenticated then redirect him to the login page
res.redirect('/');
}
module.exports = isAuthenticated;
module.exports = function(passport){
/* GET login page. */
router.get('/', function(req, res) {
// Display the Login page with any flash message, if any
res.render('index', { message: req.flash('message') });
});
/* Handle Login POST */
router.post('/login', passport.authenticate('login', {
successRedirect: '/home',
failureRedirect: '/',
failureFlash : true
}));
/* GET Registration Page */
router.get('/signup', function(req, res){
res.render('register',{message: req.flash('message')});
});
/* Handle Registration POST */
router.post('/signup', passport.authenticate('signup', {
successRedirect: '/home',
failureRedirect: '/signup',
failureFlash : true
}));
/* GET Home Page */
router.get('/home', isAuthenticated, function(req, res){
res.render('home', { user: req.user });
});
/* Handle Logout */
router.get('/signout', function(req, res) {
req.logout();
res.redirect('/');
});
return router;
}
./routes/order.js:
var express = require('express');
var router = express.Router();
var order = require('../models/order');
var auth = require('./auth.js');
module.exports = function(passport){
router.get('/order', isAuthenticated, function(req, res){
res.render('order');
});
router.post('/order', isAuthenticated, function(req, res)
{
var testorder = new order({
orderid: '52363',
productname: 'it works',
quantity: 1337
});
testorder.save(function (err) {
if (err) {console.log(err.stack);}
console.log('saving done...');
});
res.render('order', { product: req.body.productname });
});
};
./app.js
var express = require('express');
var path = require('path');
var favicon = require('static-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var pug = require('pug');
var dbConfig = require('./db');
var mongoose = require('mongoose');
// Connect to DB
mongoose.connect(dbConfig.url);
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
app.use(favicon());
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded());
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
// Configuring Passport
var passport = require('passport');
var expressSession = require('express-session');
// TODO - Why Do we need this key ?
app.use(expressSession({secret: 'mySecretKey'}));
app.use(passport.initialize());
app.use(passport.session());
// Using the flash middleware provided by connect-flash to store messages in session
// and displaying in templates
var flash = require('connect-flash');
app.use(flash());
// Initialize Passport
var initPassport = require('./models/auth/init.js');
initPassport(passport);
var auth = require('./routes/auth')(passport);
var order = require('./routes/order');
app.use('/', auth);
app.use('/order', order);
/// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: err
});
});
}
module.exports = app;
Try auth.isAuntheticated(),
It should work that way.
I am following Practical Node.js book. It is based on older version on express.js. The book is trying to build a blog. It has several different routes files. Ex- index.js, admin.js, article.js etc. This route classes are called from app.js. Ex:
app.use('/', routes.index);//// THE ISSUE IS HERE /////
app.get('/login', routes.user.login);
app.post('/login', routes.user.authenticate);
app.get('/logout', routes.user.logout);
app.get('/admin', routes.article.admin);
app.get('/post', routes.article.post);
app.post('/post', routes.article.postArticle);
Whenever someone tries to access '/', I am setting a collections object of artices and users in the request object.
var dbarticles = require('./db/articles.json');
var dbusers = require('./db/users.json');
app.use(function(req, res,next) {
if (!collections.articles || ! collections.users) return next(new Error("No collections."))
req.collections = collections;
return next();
});
app.use('/', routes.index);//// THE ISSUE IS HERE /////
The problem is that in index.js file, the value of req.collections is no available, I get 'undefined'. What am I missing. I have checked in the console.log, that the value is present in req.collections before the '/', route.index is hit.
Here's my app.js
var express = require('express');
var router = express.Router();
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var session = require('express-session');
var methodOverride = require('method-override');
var routes = require('./routes');
var dbarticles = require('./db/articles.json');
var dbusers = require('./db/users.json');
var collections = {
articles: dbarticles,
users: dbusers
};
var logger = require('morgan');
var errorHandler = require('errorhandler');
var app = express();
app.locals.appTitle = 'blog-express';
//console.log(collections.articles || collections.users);
app.use(function(req, res,next) {
if (!collections.articles || ! collections.users) return next(new Error("No collections."))
req.collections = collections;
return next();
});
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
// uncomment after placing your favicon in /public
//app.use(favicon(__dirname + '/public/favicon.ico'));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded());
app.use(methodOverride());
app.use(require('stylus').middleware(__dirname + '/public'));
app.use(express.static(path.join(__dirname, 'public')));
// Pages and routes
app.use('/', routes.index);
app.get('/login', routes.user.login);
app.post('/login', routes.user.authenticate);
app.get('/logout', routes.user.logout);
app.get('/admin', routes.article.admin);
app.get('/post', routes.article.post);
app.post('/post', routes.article.postArticle);
app.get('/articles/:slug', routes.article.show);
// REST API routes
app.get('/api/articles', routes.article.list);
app.post('/api/articles', routes.article.add);
app.put('/api/articles/:id', routes.article.edit);
app.delete('/api/articles/:id', routes.article.del);
app.all('*', function(req, res) {
res.sendStatus(404);
})
// catch 404 and forward to error handler
// error handlers
// development error handler
// development only
if ('development' == app.get('env')) {
app.use(errorHandler());
}
// production error handler
// no stacktraces leaked to user
//module.exports = router;
module.exports = app;
Here is my index.js
exports.article = require('./article');
exports.user = require('./user');
/*
* GET home page.
*/
exports.index = function(req, res, next){
console.log(".." + res.collections);
req.collections.articles.find({published: true}, {sort: {_id:-1}}).toArray(function(error, articles){
if (error) return next(error);
res.render('index', { articles: articles});
})
};
If some one needs to take a look at the code base of book, please check this github url - https://github.com/azat-co/practicalnode/tree/master/ch5/blog-express
Try changing app.use('/', routes.index) to app.get('/',routes.index)
If that doesn't work, try setting the middleware inline...
var setCollections = function (req, res,next) {
if (!collections.articles || ! collections.users) return next(new Error("No collections."))
req.collections = collections;
return next();
}
app.get('/', setCollections, routes.index)
So i managed to get passport-twitter working together with jsonwebtoken library, but in order for it to work properly I have to use express-session as the middleware. I don't want to add session because I'm using jsonwebtoken to return the token.
Here's the code
autheticate.js
router.get('/twitter', function(req, res, next){
passport.authenticate('twitter', {session: false}, function(err, user, info){
if(err){ return next(err); }
if(user){
var token = createToken(user);
console.log(token);
return res.json({token: token});
} else {
return res.status(401).json(info);
}
})(req, res, next);
});
I already added session: false as the argument, but on server.js it keeps spitting error, that i need to use express-session.
server.js
var express = require('express');
var path = require('path');
var logger = require('morgan');
var bodyParser = require('body-parser');
var mongoose = require('mongoose');
var passport = require('passport');
var session = require('express-session');
var config = require('./config');
mongoose.connect('mongodb://localhost', function() {
console.log("Connected to the database");
})
require('./passport')(passport);
var app = express();
var authenticate = require('./routes/authenticate')(app, express, passport);
var api = require('./routes/api') (app, express, passport);
// uncomment after placing your favicon in /public
//app.use(favicon(__dirname + '/public/favicon.ico'));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(session({
secret: config.TOKEN_SECRET,
resave: true,
saveUninitialized: true,
}));
app.use(express.static(path.join(__dirname, 'public')));
app.use(passport.initialize());
app.use('/auth', authenticate);
app.use('/api', api);
app.get('*', function(req, res) {
res.sendFile(__dirname + '/public/app/views/index.html');
});
app.listen(3000, function(err) {
if(err) {
return res.send(err);
}
console.log("Listening on port 3000");
});
So whenever i delete app.use(session()) and try to authenticate with passport-twitter. I will get this error
error Oauth Strategy requires app.use(express-session));
I know that the obvious solution is to add that line, but I dont want to use session. Does Oauth 0.1 really need to use session?
Passports OAuth based strategies use the session middleware to keep track of the login process. You do not need to use the session middleware for anything else, just base your authentication on your token and ignore the session.
[FIXED]:
in my code, i was missing
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(obj, done) {
done(null, obj);
});
i am writing an demo app to access microsoft one-drive
when i visit http://localhost:3000/auth/mslive, my app will redirect me to single-sign-on with windows-live
however, callback function in "/mslive" and "/mslive/callback" never get hit.
and I keep getting 500 error when windows live try to re-direct back to my app.
any idea?
Remote Address:127.0.0.1:3000
Request URL:http://anotherdemoapp.localtest.me:3000/auth/mslive/callback?code=d3f9d642-b013-af4e-e4d0-0cb828554ca5
Request Method:GET
Status Code:500 Internal Server Error
app.js
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var routes = require('./routes/index');
var authRoutes = require("./routes/auth");
var app = express();
//// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
// uncomment after placing your favicon in /public
//app.use(favicon(__dirname + '/public/favicon.ico'));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use('/', routes);
app.use("/auth", authRoutes);
auth.js
var express = require('express');
var path = require("path");
var passport = require("passport");
var passportMSLive = require("passport-windowslive");
var app = express();
app.use(passport.initialize());
passport.use(new passportMSLive.Strategy({
clientID: process.env.MSLiveClientID,
clientSecret: process.env.MSLiveClientSecret,
callbackURL: "http://anotherdemoapp.localtest.me:3000/auth/mslive/callback"
},
function (accessToken, refreshToken, profile, done) {
// this callback function will be invoked after successfully login
return done(null, profile);
}
));
// call back
app.get(
"/mslive",
passport.authenticate("windowslive", {
scope: ["wl.offline_access", "wl.contacts_skydrive", "wl.skydrive", "wl.skydrive_update", "wl.signin"]
}),
function (req, res) {
// never enter here
res.send("mslive login");
}
);
app.get(
"/mslive/callback",
passport.authenticate("windowslive", {
failureRedirect: '/mslive/fail'
}),
function (req, res) {
// never enter here
res.send("mslive callback");
}
);
app.get("/mslive/fail", function (req, res) {
// never enter here
res.send("login failed");
});
module.exports = app;