I am building a MEAN-stack application and am finally getting to the point of creating a user authentication. To do so, I followed this tutorial: http://code.tutsplus.com/tutorials/authenticating-nodejs-applications-with-passport--cms-21619
Now, when I incorporate this into my project it works, but only partially. Namely, it seems that the only page I can navigate to properly is the app's home page. If I click on any links or type something other than home in the address bar, it takes me back to the login screen.
What are some possible reasons for that?
My routes/index.js file looks as follows:
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 = 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;
}
I also have some AngularJS routes specified in another file (application worked perfectly with these before I started adding authentication).
app.config(['$routeProvider', function($routeProvider){
$routeProvider
.when('/', {
templateUrl: 'partials/home.html',
controller: 'HomeCtrl'
})
.when('/calendar',{
templateUrl: 'partials/calendar.html',
//controller: 'Calendar'
})
.when('/add-activity', {
templateUrl: 'partials/activity-form.html',
controller: 'AddActivityCtrl'
})
.when('/activity/:id',{
templateUrl: 'partials/activity-form.html',
controller: 'EditActivityCtrl'
})
.when('/activity/delete/:id', {
templateUrl: 'partials/activity-delete.html',
controller: 'DeleteActivityCtrl'
})
.otherwise({
redirectTo: '/'
});
}]);
Is there something I am missing?
P.S. I noticed that currently my URL of home page is
http://localhost:3000/home#/
whereas previously it was
http://localhost:3000/#/
I added "home" to differentiate from "/" which is the authentication page; however, I am unsure about how "#" is tacked onto the path in the first quote.
I was able to resolve this as follows. I changed the Express routing to contain a
"login"
route and changed the home route to just
"/"
As a result, the home path became
http://localhost:3000/#/
The hash sign is tacked on by and for the Angular. From my understanding, the Angular treats such path as "/". Then, the remaining routing is done by Angular and I have a single-page AngularJS app.
Working code:
Express
var express = require('express');
var router = express.Router();
module.exports = function(passport){
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()){
//console.log(next());
return next();
}
// if the user is not authenticated then redirect him to the login page
res.redirect('/login');
}
/* GET login page. */
router.get('/login', function(req, res) {
// Display the Login page with any flash message, if any
res.render('login', { message: req.flash('message') });
});
/* Handle Login POST */
router.post('/login', passport.authenticate('login', {
successRedirect: '/',
failureRedirect: '/login',
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: '/',
failureRedirect: '/signup',
failureFlash : true
}));
/* GET Home Page when logged in */
router.get('/', isAuthenticated, function(req, res){
res.render('index', { user: req.user });
});
/* GET Home Page */
router.get('/', isAuthenticated, function(req, res){
res.render('index', { user: req.user });
});
/* Handle Logout */
router.get('/signout', function(req, res) {
req.logout();
res.redirect('/login');
});
return router;
}
Working code: Angular
app.config(['$routeProvider', function($routeProvider){
$routeProvider
.when('/', {
templateUrl: 'partials/home.html',
controller: 'HomeCtrl'
})
.when('/calendar',{
templateUrl: 'partials/calendar.html',
//controller: 'Calendar'
})
.when('/add-activity', {
templateUrl: 'partials/activity-form.html',
controller: 'AddActivityCtrl'
})
.when('/activity/:id',{
templateUrl: 'partials/activity-form.html',
controller: 'EditActivityCtrl'
})
.when('/activity/delete/:id', {
templateUrl: 'partials/activity-delete.html',
controller: 'DeleteActivityCtrl'
})
.otherwise({
redirectTo: '/'
});
}]);
Related
Hello I am new in NodeJs and I have been following this tutorial http://code.tutsplus.com/tutorials/authenticating-nodejs-applications-with-passport--cms-21619 to create a app with authenticating.
I tried to follow all the structre and code from the tutorial (code is on github https://github.com/tutsplus/passport-mongo) but when I open my app in browser
i get error this error
TypeError: passport.authenticate is not a function
at module.exports (C:\myApp\routes\index.js:24:34)
This is my index.js route file
var express = require('express');
var router = express.Router();
var passport = require('passport');
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 = 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;
}
Probabbly the problem is there, maybe routing was change in some version of express, but I cant figure out what is the problem.
Can you help pme please ?
I had same problem. Look at app.js. There must be:
var routes = require('./routes/index')(passport);
You have just put the parenthesis at the wrong place.
It should be
router.post('/login', passport.authenticate('login'), {
successRedirect: '/home',
failureRedirect: '/',
failureFlash : true
});
I have my routes.js
module.exports = function(app) {
//I can't use this because angular routing does not work
/*app.get('*', function(req, res) {
res.sendfile('./public/index.html');
});*/
app.get('/submit', function(req,res){
res.sendfile('./public/submit.html');
});
app.get('/schedule', function(req,res){
res.sendfile('./public/schedule.html');
});
app.get('/requests', function(req,res){
res.sendfile('./public/requests.html');
});
app.get('/tv_left', function(req,res){
res.sendfile('./public/tv_left.html');
});
app.get('/tv_center', function(req,res){
res.sendfile('./public/tv_center.html');
});
app.get('/tv_right', function(req,res){
res.sendfile('./public/tv_right.html');
});
app.get('/', function(req, res){
res.sendfile('./public/index.html');
});
};
and my appRoutes.js like this
angular.module('appRoutes', []).config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) {
$routeProvider
// home page
.when('/', {
templateUrl: 'index.html',
controller: 'LoginController'
})
.when('/submit', {
templateUrl: 'submit.html',
controller: 'SubmitController'
});
$locationProvider.html5Mode(true);
}]);
basicly if I use app.get('*'), then any request will go back to index.html, eventhough the url changed.
That's because express handles routes in the order they are defined. If you want index.html as a catch-all route, move it to the bottom of the function.
Further reading: https://www.safaribooksonline.com/blog/2014/03/10/express-js-middleware-demystified/
I'm relatively new to Node and Passport and I was experimenting with login forms using OAuth. I had no problems with setting up Passport and it was completely functional.
It only broke down when I started cleaning up the code to separate the routes from the middlewares.
I've included a part of my code before and after the changes.
So, this works:
module.exports = function(app, passport) {
app.get('/login', loginIndex)
app.post('/login', passport.authenticate('local-login', {
successRedirect : '/profile',
failureRedirect : '/login',
failureFlash : true
}))
function loginIndex(req, res) {
res.render('login.ejs', {message: req.flash('loginMessage')})
}
}
But this does not:
module.exports = function(app, passport) {
app.get('/login', loginIndex)
app.post('/login', loginAuth)
function loginIndex(req, res) {
res.render('login.ejs', {message: req.flash('loginMessage')});
}
function loginAuth(){
passport.authenticate('local-login', {
successRedirect : '/profile',
failureRedirect : '/login',
failureFlash : true
})
}
}
So, the only difference between the two is that I've moved the passport.authenticate() call into the function loginAuth().
I guess it has to do with the internal working of passport.authenticate(), but I'm not sure.
Thanks.
Try this:
app.post('/login', loginAuth())
...
function loginAuth(){
return passport.authenticate('local-login', {
successRedirect : '/profile',
failureRedirect : '/login',
failureFlash : true
})
}
In your original code, you are executing passport.authenticate and in the second version you are just passing a function without executing the passport logic.
I'm using Passport to authenticate my users on NodeJS. Currently I'm using ExpressJS and I'm trying to route my traffic. I currently use the following code:
website.js (main file)
require("./routes.js")(app);
routes.js
var pages = {
home: require("./pages/home"),
about: require("./pages/about"),
register: require("./pages/register"),
login: require("./pages/login"),
api: require("./api/index")
};
module.exports = function(app) {
app.use("/", pages['home']);
for (page in pages) {
app.use("/" + page, pages[page]);
}
app.get("/logout", function(req, res) {
req.logout();
req.redirect("/");
});
}
register.js
var express = require('express');
var router = express.Router();
var app = express();
router.get("/", function(req, res) {
res.render("register", { page: "Register", message: req.flash("registerMessage") });
});
app.post("/", passport.authenticate("register", {
successRedirect: "/about/",
failureRedirect: "/register/",
failureFlash: true,
successFlash: "Logged in!"
}));
module.exports = router;
The problem I am facing is that POST requests to this will result in a 404. The page is not found. The GET request (so /register) properly shows the registration form, but upon submitting I get a 404. If I change router.get("/", function(req,res){}) to router.use("/", function(req, res, next) {}), I will get HTTP 500 errors when I call "Next()" (Can't set headers after they are sent.), and POST still doesn't work.
Could anyone tell me how to correctly catch POST requests behind router middleware?
I solved my issue
I solved my issue by using the following:
router.route("/")
.get(function(req, res, next) {
res.render("register", { page: "Register", message: req.flash("registerMessage") });
})
.post(passport.authenticate("register", {
successRedirect: "/about/",
failureRedirect: "/register/",
failureFlash: true,
successFlash: "Logged in!"
}));
I try to login my user via Facebook with use of PassportJS and pass the user data to Angular.
On the server side it all looks ok with the following code for the Facebook callback in users controller:
exports.facebookCallback = function() {
return function(req, res, next) {
passport.authenticate('facebook', function(err, user, email) {
if (err || !user) {
return res.redirect('/auth');
}
req.login(user, function(err) {
if (err) {
return res.redirect('/auth');
}
return res.redirect('/');
});
})(req, res, next);
};
};
From what I understand from the PassportJS docs, calling req.login should put user data into the session.
My routes on the server side looks following:
app.get('/auth', usersCtrl.auth);
app.get('/auth/signout', usersCtrl.logout);
app.get('/auth/facebook', passport.authenticate('facebook', {
scope: ['email', 'user_hometown']
}));
app.get('/auth/facebook/callback', usersCtrl.facebookCallback());
express and passport configuration includes:
app.use(express.cookieParser());
app.use(express.session({secret: '1234567890QWERTY'}));
app.use(express.bodyParser());
app.use(passport.initialize());
app.use(passport.session());
Now on the angular side I try to get the user data from the session in a service defined like this:
module.exports = require('angular')
.module('HomeModule', [])
.controller('HomeCtrl', function ($scope) {
//home controller code ors here
}).controller('NavbarCtrl', ['$scope', 'Authentication', function ($scope, Authentication) {
$scope.authentication = Authentication;
//rest of the navbar controller goes here
}]).factory('Authentication', [
function() {
var _this = this;
_this._data = {
user: window.user
};
return _this._data;
}
]);
Unfoortunately, the user data is not available in window.user on angular side.
Any ideas what I'm doing wrong here?
As Girish said, the passport session object won't be available on client side. As you seem to be using express, a simple way to do this is to use express-expose.
If you want the user data to be available on all pages when the user is authenticated, you can add something like this before your routes declaration
app.use(function (req, res, next) {
if (req.isAuthenticated()) res.expose(req.user, 'user');
next ();
});
The user data will be available client side as window.user.
The passport session object won't be available on the window object , instead you need to get it from the server using some service or a redirect url.
After successful authentication , the primary route function will be called,
which, in this case, will redirect the user to the home page.
app.get('/auth/facebook/callback',
passport.authenticate('facebook', { failureRedirect: '/login' }),
function(req, res) {
res.redirect('/');
});
app.get('/', function(req, res){
res.render('index', { user: req.user });
});
or you can create a route to get the logged in user data
app.get('/account', function(req, res){
if (req.isAuthenticated()) {
res.send({user : req.user});
}else{
res.redirect('/login');
}
});
On the Angular side, you can set the user data to rootscope from the $http response,
$rootScope.session = {}
$rootScope.session.user = res.user;