Andrew Lee
Andrew Lee

Reputation: 304

Can't get passportjs to authenticate local strategy with express and mysql

I am trying to implement a local Strategy with passport so that users can login based on the database credentials from mysql. However I keep getting a "view error" for some reason when I try logging in with my login form.

When I login with no credentials it shoots out a "Missing Credential" connect-flash error. However when I put in a username and password in the login I get a "Failed to lookup view "error" in views directory".

I was hoping if someone can point out where the problem lies within my code.

My structure is setup in this way

config
 --database.js
 --passport.js
models
 --user.js
routes
 --users.js
views
 --login.js
app.js

app.js file

const express = require('express');
const mysql = require('mysql');
const path = require('path');
const logger = require('morgan');
const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser');
const expressValidator = require('express-validator');
const flash = require('connect-flash');
const session = require('express-session');
const passport = require('passport');
const config = require('./config/database');

// Init MySql
const db = mysql.createConnection(config);

db.connect((err) => {
    if(err) throw err;
    console.log('MySql Connected...');
});

// Init App
const app = express();

// View Engine Setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');

// Connect to public directory
// uncomment after placing your favicon in /public
// app.use(favicon(path.join(__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')));

// Express Session Middleware
app.use(session({
    secret: 'secretKey',
    resave: true,
    saveUninitialized: true
}));

// Express Messages Middleware
app.use(require('connect-flash')());
app.use((req, res, next) => {
    res.locals.messages = require('express-messages')(req, res);
    next();
});

// Express Validator Middleware
app.use(expressValidator({
    errorFormatter: (param, msg, value) => {
        var namespace = param.split('.'),
        root = namespace.shift(),
        formParam = root;
    while(namespace.length) {
        formParam += '[' + namepsace.shift() + ']';
    }
    return {
        param: formParam,
        msg: msg,
        value: value
    };
    }
}));

// Passport Config
require('./config/passport')(passport);
// Passport Middleware
app.use(passport.initialize());
app.use(passport.session());


// Route Files
let index = require('./routes/index');
let users = require('./routes/users');
let posts = require('./routes/posts');

// Routers
app.use('/', index);
app.use('/users', users);
app.use('/posts', posts);

// catch 404 and forward to error handler
app.use((req, res, next) => {
    let err = new Error('Not Found');
    err.status = 404;
    next(err);
});

// error handler
app.use((err, req, res, next) => {
    // set locals, only providing error in development
    res.locals.message = err.message;
    res.locals.error = req.app.get('env') === 'development' ? err : {};

    // render the error page
    res.status(err.status || 500);
    res.render('error');
});

// Start Server
app.listen('3000', (req, res, next) => {
    console.log('Server started on port 3000...');
});

module.exports = app;

models/user.js

let mysqlModel = require('mysql-model');
const config = require('../config/database');

let UserModel = mysqlModel.createConnection(config);

// User Model
let User = UserModel.extend({
    tableName: 'users'
});

module.exports = User;

routes/users.js

const express = require('express');
const mysql = require('mysql');
const router = express.Router();
const config = require('../config/database');
const bcrypt = require('bcryptjs');
const passport = require('passport');

// Init MySql
const db = mysql.createConnection(config);

// User Model
let User = require('../models/user');

// Login Form
router.get('/login', (req, res) => {
    res.render('login');
});

// Login Process
router.post('/login', (req, res, next) => {
    passport.authenticate('local', {
        successRedirect: '/',
        failureRedirect: '/users/login',
        failureFlash: true
    })(req, res, next);
});

module.exports = router;

config/passport.js

const LocalStrategy = require('passport-local').Strategy;
const mysql = require('mysql');
const bcrypt = require('bcryptjs');
const config = require('../config/database');

// Init MySql
const db = mysql.createConnection(config);

// User Model
const User = require('../models/user');

module.exports = (passport) => {
    // Local Strategy
    passport.use(new LocalStrategy((username, password, done) => {
        // Match Username
        let query = {username:username};
        User.findOne(query, (err, user) => {
            if(err) throw err;
            if(!user){
                return done(null, false, {message: 'No user found'});
            }
        // Match Password
        bcrypt.compare(password, user.password, (err, isMatch) => {
                if(err) throw err;
                if(isMatch){
                    return done(null, user);
                } else {
                    return done(null, false, {message: 'Wrong Password'});
                }
            });
        });
    }));
            passport.serializeUser(function(user, done) {
                done(null, user.id);
        });
            passport.deserializeUser(function(id, done) {
            User.findById(id, function(err, user) {
            done(err, user); done(err, user);
        });
    });
}

views/login.pug

extends layout

block content
        h1 Login
        hr
        form(method='POST', action='/users/login')
            #form-group
                label Username
                input.form-control(name='username', type='text')
            #form-group
                label Password
                input.form-control(name='password', type='password')
                br
                input.btn.btn-primary(type='submit', value='Submit')

I have been stuck for quite some time and any help would be appreciated. Thank you.

Upvotes: 0

Views: 589

Answers (1)

TomPlum
TomPlum

Reputation: 135

I can't simply comment as I don't have the reputation. But I had a similar problem with the order of the middleware stack in app.js.

Do you initialise passport with user serialisation and deserialisation functions? E.g;

module.exports = function(passport){
    //Serialise User
    passport.serializeUser(function(user, done) {
        console.log("Serializing User: " + user.username + "\n" + user + ".");
        done(null, user._id);
    });

    //De-Serialise User
    passport.deserializeUser(function(id, done) {
        User.findById(id, function(err, user) {
            console.log("Deserializing User: " + user.username + "\n" + user);
            done(err, user);
        });
    });

    //Setting up Passport Strategies for Login and SignUp/Registration
    login(passport);
    signup(passport);
}

You'll also want to specify passport for your routes in app.js

// Route Files
let index = require('./routes/index')(passport);
let users = require('./routes/users')(passport);
let posts = require('./routes/posts')(passport);

and in the route files themselves, you'll want to wrap your routes in an modules export function for passport. E.g;

module.exports = function(passport) {
    //Routes

    return router;
}

Then you can protect your routes by writing a function that checks if the user is logged in.

let isAuthenticated = function(req, res, next) {
    if (req.isAuthenticated()) {
        console.log("User " + req.user.username + " authenticated.");
        return next();
    } else {
        res.redirect('/unauthorised');
    }
}

//Protect a route
router.get('/some-route', isAuthenticated, function(req, res) {
    res.render('some-file');
}

Have a look here for a tutorial on the matter.

Upvotes: 1

Related Questions