Trouble with authentication - javascript

I´m facing a confusion issue while implementing the authentication for my restful api using passport local strategy.
Note:
I got the authentication working successfully when I´m doing it all in my index.js. But I want to use in Classes for better Code separation.
I have a passport.js Module
// config/passport.js
// load all the things we need
var LocalStrategy = require('passport-local').Strategy;
// load up the user model
var mysql = require('mysql');
var dbconfig = require('./database');
var connection = mysql.createConnection(dbconfig.connection);
module.exports = function(passport) {
// passport needs ability to serialize and unserialize users out of session
passport.serializeUser(function (user, done) {
//console.log("SER");
console.log(user),
done(null, user);
});
passport.deserializeUser(function (user, done) {
console.log("XXXX");
console.log(user);
connection.query("SELECT * FROM users WHERE name = ? ",user.name, function(err, rows){
console.log("DER");
console.log(rows);
done(err, rows[0]);
});
});
// passport local strategy for local-login, local refers to this app
passport.use('local-login', new LocalStrategy(
function (username, password, done) {
console.log("hhh");
console.log(username);
connection.query("SELECT * FROM users WHERE name = ? ",username, function(err, rows){
console.log(rows);
return done(err, rows[0]);
});
})
);
// route middleware to ensure user is logged in
function isLoggedIn(req, res, next) {
if (req.isAuthenticated())
return next();
res.sendStatus(401);
}
};
This is my Controller Class:
class AuthenticateController {
constructor(router, passport) {
this.router = router;
this.registerRoutes();
this.passport = passport;
}
registerRoutes() {
this.router.post('/login/:username/:password', this.login.bind(this));
//this.router.get('/logout', this.logout.bind(this));
this.router.get('/content', this.content.bind(this));
}
login(req, res) {
this.passport.authenticate("local-login", { failureRedirect: "/login"}),
res.redirect("/content");
}
content(req, res ) {
console.log(req.user);
if (req.isAuthenticated()) {
res.send("Congratulations! you've successfully logged in.")
} else {
res.sendStatus(401);
}
}
isLoggedIn(req, res, next) {
console.log(req.user);
if (req.isAuthenticated())
return next();
res.sendStatus(401);
}
}
module.exports = AuthenticateController;
The Controller gets the router and passport fully configured as parameters from my index.js.
//index.js
var express = require('express')
, cors = require('cors')
, app = express()
, passport = require('passport')
, morgan = require('morgan');
require('./config/passport')(passport); // pass passport for configuration
var bodyParser = require('body-parser');
app.use(bodyParser.json());
app.use(require('express-session')({secret: 'vidyapathaisalwaysrunning',
resave: true,
saveUninitialized: true }));
app.use(passport.initialize());
app.use(passport.session());
app.use(cors());
var apiRouter = express.Router();
app.use('/api', apiRouter);
//
var apiV1 = express.Router();
apiRouter.use('/v1', apiV1);
var authenticateApiV1 = express.Router();
apiV1.use('/auth', authenticateApiV1);
var AuthenticateController = require('./controllers/authenticate');
var ac = new AuthenticateController(authenticateApiV1, passport); //pass in our fully configured passport
//If I call this /login instead of the /auth/login/ in the Controller Class it works!
//app.post("/login",
// passport.authenticate("local-login", { failureRedirect: "/login"}),
// function (req, res) {
// res.redirect("/content");
// });
What is working and what is not working
The Authentication in general is working. In my posted index.js you see app.post("/login", .... If I call this one the authentication is successfully and if I try to reach the restricted content in /auth/content/ req.user has a value (the user object) and I can successfully call req.isAuthenticated() .
BUT, If I use the authentication from /auth/login/username/password the req.user is undefined when trying to reach the restricted Content.
I get no error and the response of /auth/login/username/password/ HTTP Code 301 - 'redirecting to /content.
I have currently no idea what I´m doing wrong here and I´m pretty new to the topic of Node/express/ passport ..
Hope someone has an Idea. If you need something else to help me, just mention it in the comments and I will do my best to provide you everything you need.
Thanks
EDIT:
I recently tried to read the req.user in the login function and even there it is undefined
login(req, res) {
this.passport.authenticate("local-login", { failureRedirect: "/login"}),
console.log(req.user) //undefined
res.redirect("/content");
}
I guess it could be some async problem and I should use some callback functions, but I don´t know how to apply this in my login()
EDIT 2:
Another Issue I´m facing is the integration of the isLoggedIn() request.
If I do this:
registerRoutes() {
this.router.get('/', this.isLoggedIn, this.getUsers.bind(this));
this.router.get('/:id', this.getSingleUser.bind(this));
}
it results in 401 - Unauthorized
A console.log(req.user); in the isLoggedIn() results in undefined.
But if I call the first route without calling isLoggedIn() and do console.log(req.user); the user object exists.

The correct use of callback with passport authentication for local strategy can be as below:
function(req, res, next){
passport.authenticate('local-login', function(err, user, info){
if(err)
return logger.log('error', err);
if(user)
req.login(user, function(err){
if(err) return next(err);
return res.json({'success': true});
});
if(!user)
return res.json({'error':true, 'message': info.message, 'type': info.type});
})(req, res, next);
}
Please note the use of req.login() to explicitly set user in session.

The only thing I'm finding strange is that your route declarations are different.
In the AuthenticateController the route is declared as:
this.router.post('/login/:username/:password', ...
While in index.js, the route is simply declared as
app.post("/login", ...
How is your client submitting the login credentials to the server? If it is by form, like the tutorial, could it be that having :username and :password declared as route params but being sent by form messes with passport?
Try registering the route exactly like index.js
this.router.post('/login', ...
EDIT:
I've found another dicrepancy. In AuthenticateController the res.redirect("/content"); is not wrapped inside a callback. So it is being executed before Authenticate finishes running.
In the index.js example, passport is being used as a route middleware:
app.post("/login",
passport.authenticate("local-login", { failureRedirect: "/login"}),
function (req, res) {
res.redirect("/content");
});
While in the passport.js it is inside the callback. Consider declaring it in the route:
registerRoutes() {
this.router.post('/login', this.passport.authenticate("local-login", { failureRedirect: "/login"}), this.login.bind(this));
(...)
}
login(req, res) {
res.redirect("/content");
}
O, better yet, why not use passport's option to declare both success and failure redirects, since that seems to be all that you are doing:
login(req, res) {
this.passport.authenticate("local-login", { successRedirect: "/content", failureRedirect: "/login" });
}

You are passing this.login.bind(this) as a middleware to this.router.post('/login/:username/:password', this.login.bind(this)); but login(req, res) only responds to the request with res.redirect("/content"); i.e. redirecting to /content
So like you said, you need to supply a callback that does something with the user that is returned from passports middleware verify callback.
app.post("/login",
passport.authenticate("local-login", { failureRedirect: "/login"}),
function (req, res) {
console.log(req.user); // log user in console
res.json({user: req.user}); // send user as json response
});
The custom callback mentioned by #divsingh is if you want to explicitly have control of setting the session, error messages and redirecting the request. Any other information can be found under http://passportjs.org/docs

Related

req.session.returnTo variable is not saved redirect

I tried to follow along with Colt Steele's yelpcamp project but his packages are old, so I wonder if the codes are outdated.
I use the middleware isLoggedIn to return the user to the previous page after logging in.
In my middleware.js file:
module.exports.isLoggedIn = (req, res, next) => {
if (!req.isAuthenticated()) {
req.session.returnTo = req.originalUrl
req.flash('error', 'You must sign in first!');
return res.redirect('/login');
}
next();
}
In my campgrounds.js page, I use isLoggedIn as the middleware, so I have to log in to create a new campground. So if I am not logged in and I go to localhost:3000/campgrounds/new, return to should be '/campgrounds/new'.
router.get('/new', isLoggedIn, (req, res) => {
res.render('campgrounds/new');
});
In the app.js page, I set up a middleware the set the session's currentUser to req.user. Also, I print out req.session.returnTo with this middleware, up until this point, the req.session's returnTo variable is still 'campgrounds/new'.
app.use((req, res, next) => {
console.log(req.session);
console.log(req.session.returnTo);
res.locals.currentUser = req.user;
res.locals.success = req.flash('success');
res.locals.error = req.flash('error');
next();
});
In users.js, if I successfully log in, I get redirect to the route where returnTo is, the problem is returnTo changed from '/campgrounds/new' to undefined all the sudden.
router.post('/login', passport.authenticate('local', { failureFlash: true, failureRedirect: '/login' }), (req, res) => {
req.flash('success', 'Welcome back!');
console.log("Session\n---------\n", req.session);
console.log("returnTo\n---------\n", req.session.returnTo);
const redirectUrl = req.session.returnTo || '/campgrounds';
console.log("redirectUrl\n---------\n", redirectUrl);
delete req.session.returnTo;
res.redirect(redirectUrl);
});
Now, if I go to localhost:3000/campgrounds/new, I will be redirected to /login, then in after I login, I will be redirected to /campgrounds instead of campgrounds/new.
Here is the console log from my Git Bash.
Any idea what's going on here?

Nodejs app suddenly started to act differently

This part of code handles the login authorization routing in my app
const express = require("express");
const authController = require('../controllers/authController');
const indexController = require('../controllers/indexController');
const router = express.Router();
router.use("/login", (req, res, next) => {
if(req.session.loggedIn) {
res.redirect('/account');
}
next();
});
router.use("/account", (req, res, next) => {
if(!req.session.loggedIn) {
res.redirect('/login');
}
next();
});
router.get('/', (req, res) => {
res.redirect('/login');
});
router.get('/login', (req, res) => {
res.render('login');
});
router.get('/log-out', authController.logOut);
router.get("/account", indexController.getAccountData);
module.exports = router;
There were no problems and it was working fine till recent days.
I haven't change anything in this file nor authController nor indexController.
When I make a change (in other parts), nodemon restarts the app and I automatically jump to login page cause obviously all sessions are destroyed. But I get an error in getAccountData function (Error says req.session is undefined).
As you can see there's no way for the app to reach that function with no sessions set.
I have to restart the app again to act correct.
The session will be cleared each time the server restarts. So to escape from that you need to save the session to database. If you are using mongodb or I can give you example using mongodb.
import MongoStore from "connect-mongo";
import session from "express-session";
app.use(
session({
store: MongoStore.create({
mongoUrl: process.env.MONGODB_URI || "mongodb://localhost:27017/project",
}),
secret: "secret key",
cookie: { maxAge: sessionExpireInMilliseconds },
})
);
req.session.loggedIn would error out whenever session is undefined. You need to check if it is defined before trying to access loggedIn.
Try:
router.use("/login", (req, res, next) => {
if(req.session && req.session.loggedIn) {
res.redirect('/account');
}
next();
});
router.use("/account", (req, res, next) => {
if(!req.session || !req.session.loggedIn) {
res.redirect('/login');
}
next();
});

Passport Authenticate doesn't redirect

I was writing a local-signup strategy and noticed that it doesn't work so I stepped back and tried to authenticate against my empty collection. Every time I submit the form it takes ~30-40s until it results in a timeout. I ensured passport.authenticate() is called but it seems ike it's not doing any redirects and hence it is timing out because I am not rendering something either.
Questions:
I expected that it would do a redirect to the failureUrl (which is '/signup'), but instead nothing is happening. What am I doing wrong here?
Why there is no single log message coming from passport? This is driving me crazy because I have absolutely no idea what is going wrong there.
I am new to node.js and as far as I got I don't need to pass the configured passport object to the router but instead I can just do const passport = require('passport') is that correct?
This is my function handler for the /signup route:
function processSignup (req, res) {
logger.info("POST request received")
logger.info(req.body)
passport.authenticate('local', {
successRedirect : '/profile', // redirect to the secure profile section
failureRedirect : '/signup', // redirect back to the signup page if there is an error
failureFlash : true // allow flash messages
})
}
Winston prints:
7:32:04 PM - info: POST request received 7:32:04 PM - info:
username=dassd#dass.de, password=dasdsa, submit=Register
My passport.js file looks like this:
const LocalStrategy = require('passport-local').Strategy
const User = require('./user-model')
const passport = require('passport')
// expose this function to our app using module.exports
function config() {
passport.serializeUser(function(user, done) {
done(null, user.id)
})
// used to deserialize the user
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user)
})
})
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, { message: 'Incorrect username.' });
}
if (!user.validPassword(password)) {
return done(null, false, { message: 'Incorrect password.' });
}
return done(null, user);
});
}
));
}
module.exports = {
config: config
}
The relevant snipped of my app.js:
// required for passport
require('./authentication/passport').config();
app.use(cookieParser())
app.use(bodyParser())
app.use(session({
secret: 'secretToBeChanged',
saveUninitialized: false,
resave: false
}))
app.use(passport.initialize())
app.use(passport.session()) // persistent login sessions
app.use(flash()) // use connect-flash for flash messages stored in session
After a quick look at the documentation for passportjs, I think you need to do something like this:
function processSignup (req, res, next) {
logger.info("POST request received")
logger.info(req.body)
const handler = passport.authenticate('local', {
successRedirect : '/profile', // redirect to the secure profile section
failureRedirect : '/signup', // redirect back to the signup page if there is an error
failureFlash : true // allow flash messages
});
handler(req, res, next);
}
passport.authenticate() returns a function that is meant to be used as the route handler function.
Normally, you would type something like:
app.post('/login', passport.authenticate('local', {
successRedirect: '/',
failureRedirect: '/login',
failureFlash: true
}));
But since you have abstracted with your own route handler function, you need to invoke the one returned from passport.authenticate().
In the end Mikael Lennholm was right and he pointed me into the right direction. I couldn't find that in any passport.js tutorials. However the passport.js documentation contains this code snippet which represents the same but I prefer it's code style:
passport.authenticate('local', function(err, user, info) {
if (err) { return next(err); }
if (!user) { return res.redirect('/login'); }
req.logIn(user, function(err) {
if (err) { return next(err); }
return res.redirect('/users/' + user.username);
});
})(req, res, next);

passportjs local strategy not getting called

I am using express and passport to build a rest api backend and it seems that my localStrategy is not getting called on request.
The entry point of my application looks like the following:
app.js
var fs = require('fs');
var express = require('express');
var mongoose = require('mongoose');
var passport = require('passport');
var config = require('./config/config');
var morgan = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var app = express();
app.use(morgan('dev')); // log every request to the console
app.use(cookieParser()); // read cookies (needed for auth)
app.use(bodyParser.urlencoded({extended:true}));
app.use(passport.initialize());
//connect to mongodb
mongoose.connect(config.db, options);
//load models (shorten forEach)
...
require(__dirname + '/models/' + file)(mongoose);
//load passport config
require('./config/passport')(mongoose, passport);
//load routes
require('./config/routes')(app, passport);
//start server
var server = app.listen(3000, ....
routes.js
...
app.post('/auth', function(req, res){
console.log("reached auth endpoint");
console.log(req.body);
passport.authenticate('local', { session: false}, function(err, user, info){
console.log("Test:"+user);
if(err) {
console.log("Error1");
return next(err)}
if(!user){
console.log("Error2");
return res.json(401, {error: 'Auth Error!'});
}
console.log("Error3");
var token = jwt.encode({ username: user.email }, "hanswurst");
res.json({token: token});
}),
function(req, res){
console.log("passport user", req.user);
};
});
passport.js
...
passport.use(new LocalStrategy({
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true
},
function(email, password, done){
console.log("TEST");
User.findOne({'email': email}, function(err, user){
if(err){
console.log("Unknown error");
return done(err);
}
if(!user){
console.log("No User found");
return done(null, false);
}
if(!user.validPassword(password)){
console.log("Password was incorrect");
return done(null, false);
}
console.log("User was found");
return done(null, user);
});
}
));
The only result i get on form request is
reached auth endpoint
{ email: 'test#mail.com', password: 'secret' }
POST /auth - - ms - -
For me the request body looks fine and it should enter my LocalStrategy. I am bit helpless as i am not getting any other console output from this point.
First off, you have some basic javascript syntax errors in routes.js. This part right here (lots of code removed for clarity) is broken:
passport.authenticate(/*...*/), function(req, res) {/*..*/};
It might have been that you just added some console.log calls in the wrong place. To clear up the confusion, passport.authenticate() does not perform the authentication right away, what it does is return a middleware for you. You would use it like this for example :
var middleware = passport.authenticate(...);
app.post('/auth', middleware);
So to fix your problem, try invoking the middleware returned by authenticate right away, like this:
app.post('/auth', function(req, res, next) {
console.log("reached auth endpoint");
console.log(req.body);
passport.authenticate('local', {
session: false
}, function(err, user, info) {
console.log("Test:" + user);
if (err) {
console.log("Error1");
return next(err);
}
if (!user) {
console.log("Error2");
return res.json(401, {
error: 'Auth Error!'
});
}
console.log("Error3");
var token = jwt.encode({
username: user.email
}, "hanswurst");
res.json({
token: token
});
})(req, res, next);
});
Also, I have to tell you that require caches modules. To make config/passport.js aware of mongoose and passport, you should not feed them as parameters like this:
require('./config/passport')(mongoose, passport);
Simply require them again inside config/passport.js like so:
// (in config/passport.js)
// Both of these vars point to the same thing you require'd in app.js
var mongoose = require('mongoose');
var passport = require('passport');
[Edit] I Found the problem. Because Express is no longer supporting subpackages like body-parser etc, you need to set those separately. All would be well had I done that in the first place, but I only activated:
app.use(bodyParser.urlencoded({extended:true}));
You also need to set
app.use(bodyParser.json());
To get it to work properly. Stupid oversight but still, got me stumped for 3 days.
I Have the same problem, but nothing seems to work for me though.
I'll drop the code from the top down in execution order
jade template
.navbar-right(ng-controller="mvNavBarLoginCtrl")
form.navbar-form
.form-group
input.form-control(placeholder='Email', ng-model='username')
.form-group
input.form-control(type='password', placeholder='password', ng-model='password')
button.btn.btn-primary(ng-click="signIn(username,password)") Sign In
Next step Login controller
angular.module('app').controller('mvNavBarLoginCtrl',function($scope, $http){
$scope.signIn = function (username, password){
console.log('un = ' + username); // Prints fine in the console
console.log('pw = ' + password); // Prints fine in the console
$http.post('/login', {username: username, password: password}).then(function(response){
if(response.data.success){
console.log('Logged in!!');
}
else{
console.log('Failed to log in!!');
}
});
}
});
Next step Route handler
app.post('/login', function(req, res, next){
console.log(req.username); // Already empty
console.log(req.password); // Already empty
console.log(req.body); // Already empty
// Because of the use of AngularJS we can not call passport directly,
// missing req and res will break the code
var auth = passport.authenticate('local', function(err, user){
console.log(user); // prints undefined
if(err){return next(err);}
if(!user){res.send({success:false});}
req.logIn(user, function(err){
if(err){return next(err);}
res.send({success: true, user: user});
});
});
// call the auth function to start authenticate
auth(req, res, next);
});
Next step passport authentication handler
var User = mongoose.model('User');
passport.use(new LocalStrategy({
username: 'username',
password: 'password'
},
function(username, password, done){
console.log("called"); // never printed
// find user based in mongoose schema see 'var User'
User.findOne({userName:username}).exec(function (err,user){
console.log(user) // never printed
if(err){return done(err);}
if(user){
return done(null, user);
}
else{
return done(null, false);
}
});
}
));
passport.serializeUser(function(user, done){
if(user) {
done(null, user._id);
}
});
passport.deserializeUser(function(id, done){
user.findOne({_id:id}).exec(function(err, user){
if(user) {
done(null, user);
}
else{
done(null, false);
}
});
});
There are no errors not in the console and not as node output. I am stumped and read about 5 or so other thread with similar issues, nothing worked.
If somebody could give me the golden tip, I would be eternally gratefull.

Pushing user data from Nodejs server to Angular after successful login

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;

Categories

Resources