Passport.js throws "missing credentials" error with local authentication strategy even when custom usernameField is set

Viewed 547

I'm trying to implement passport.js's local strategy for authentication. My server is using express routes (I believe this might be the cause of the problem).

If I try to pass anything on the frontend with Axios, the server throws me the error "Missing credentials", even though console logs on the server show me correct data was received.

My main server file:

import express from 'express';
import mongoose from 'mongoose';
import cors from 'cors';
import passport from 'passport';
import cookieParser from 'cookie-parser';
import session from 'express-session';
import authStrategy from "./utils/passportConfig";

// enables ENV variables to be present under command proccess.env
require('dotenv').config();

// enables express.js on the app and defines the port
const app = express();
const port = process.env.PORT || 5000;

// enables CORS - cross origin resource sharing - 
// makes possible requesting resources from another domain
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cors());

const secret = process.env.SECRET;
app.use(session({
    secret: secret!, 
    resave: true,
    saveUninitialized: true,
}))

app.use(cookieParser(secret));
app.use(passport.initialize());
app.use(passport.session());
authStrategy(passport);


// mongoose helps us connect to our MongoDB database
const uri = process.env.MONGO_URI!;
mongoose.connect(uri, { useNewUrlParser: true, useCreateIndex: true });
const connection = mongoose.connection;
connection.once('open', () => {
    console.log("MongoDB databse connection established successfully");
});

// Routes
const usersRouter = require('./routes/users')(passport);
app.use('/user', usersRouter);

// starts the server, and listens for changes on predefined port
app.listen(port, () => {
    console.log(`Server is running on port: ${port}`);
});

My passport local strategy configuration:

import User, { IUser } from "../models/user.model";
import bcrypt from "bcrypt";
import * as passportLocal from "passport-local";
import { PassportStatic } from "passport";

const localStrategy = passportLocal.Strategy;

export default module.exports = (passport: PassportStatic) => {
    passport.use(
        new localStrategy({
            usernameField: "email",
        },
        (email, password, done) => {
            User.findOne({ email: email.toLowerCase() }).exec()
                .then((user) => {
                    if (!user) {
                        return done(null, false, { message: "User with that email does not exist"});
                    }
                    bcrypt.compare(password, user.password, (err) => {
                        if (err) {
                            return done(null, false, { message: "Incorrect password" });
                        }
                        return done(null, user);
                    })
                })
                .catch((err) => {
                    return done(err);
                })
        })
    )
    passport.serializeUser((req: any, user: any, done: any) => {
        done(null, user.id);
    })
    passport.deserializeUser((id, done) => {
        User.findOne({_id: id}, (err: any, user: any) => {
            done(err, user);
        });
    })
}

My express route handling:

import express from "express";
import { PassportStatic } from "passport";

module.exports = (passport: PassportStatic) => {
    const router = express.Router();
    
    router.route('/login').post( function (req, res, next) {
        console.log("@@@@", req.body)
        passport.authenticate('local', function (err, user, info) {
            console.log("MMMM", err, user, info)
            if (err) { return next(err); }
            if (!user) { return res.status(500).json("User does not exist") }
            req.logIn(user, err => {
                if (err) {
                    return next(err);
                }
                res.status(200).json("Successfully authenticated");
                console.log(req.user);
            })
        })(res, res, next);
    });
    
    return router;
}

What console logs on login router return:

@@@@ { email: 'stackoverflow@test.com', password: 'fakepassword' }
MMMM null false { message: 'Missing credentials' }

All solutions to this problem I have found say, that Missing credentials error is due to passport authentication strategy expecting username and password key, and that if you pass anything else, you need to let passport know. As you can see in the passport strategy config file, I have done that.

passport.use(
        new localStrategy({
            usernameField: "email",
        },
        (email, password, done) => {
        ...

What might be the issue here? Is passport not being passed correctly from server.ts? Does the passport not use configured local strategy?

Used packages versions: express: 4.17.1 | express-session: 1.17.1 | passport: 0.4.1 | passport-local: 1.0.0 |

Edit: Issue solved

There is a problem in the express route handling file, I'm passing `req` twice in the authentication middleware.

I have marked the first reply as correct, as that is the solution most people need when they receive Missing credentials error.

1 Answers

It seems like you have missed the password field while configuring the passport local strategy.

It must look something like this:

passport.use(
  "local",
  new LocalStrategy(
    {
      usernameField: "email",
      passwordField: "password",
      passReqToCallback: false,  //optional
    },
    async (username, password, done) => {
...
Related