Passport: Authentication not Working - node.js

I am new to NodeJS. I am made a basic CRUD app with authentication using express. Sign up was successful but login is not successful.I am using passport and bcryptjs for authentication.
Here is home route. As index is called automatically so this is not getting called. For other routes it works only if manually pass the function ensureAuthenticated to their parameters just like below. I want it to work for all endpoints without manually doing so.
app.get("/", ensureAuthenticated, function (req, res) {
res.render('index');
});
Below is the login functionality.
app.get("/login", function (req, res) {
const loginPath = path.join(__dirname, '/login.html');
res.sendFile(loginPath);
});
passport.use(new localStrategy({
usernameField: 'adminUsername',
passwordField: 'password',
session: false
},
function (adminUsername, password, done) {
Admin.getAdminByAdminUsername(adminUsername, function (err, admin) {
if (err) throw err;
console.log('getAdmin called');
if (!admin) {
console.log('Admin Not Found');
return done(null, false);
}
Admin.comparePassword(password, admin.password, function (err, isMatch) {
console.log('comparePassword called');
if (err) throw err;
if (isMatch) {
return done(null, admin);
} else {
console.log('Wrong Password!');
return done(null, false);
}
});
});
}));
passport.serializeUser(function (admin, done) {
done(null, admin.id);
});
passport.deserializeUser(function (id, done) {
Admin.getAdminById(id, function (err, admin) {
done(err, admin);
console.log('findById called');
});
});
app.post('/login', passport.authenticate('local', {
failureRedirect: '/login'}), function(req, res){
console.log('login called');
res.redirect('/');
});
app.get('/logout', function(req,res){
req.logout();
res.redirect('/login');
});
function ensureAuthenticated(req, res, next){
if (req.isAuthenticated()) {
return next();
} else {
res.redirect('/login');
}
}
The problem is even without authentication, I still have access via URL endpoints.
Here is the Admin schema file.
const mongoose = require("mongoose");
const bcrypt = require('bcryptjs');
const schema = new mongoose.Schema({
adminEmail: {
type: String,
unique: true,
required: true
},
adminUsername:{
type: String,
unique: true,
required: true
},
password:{
type: String,
required: true
}
});
const Admin = mongoose.model('Admin', schema);
module.exports = Admin;
module.exports.getAdminByAdminUsername = function (adminUsername, callback) {
const query = {adminUsername: adminUsername};
Admin.findOne(query, callback);
}
module.exports.getAdminById = function (adminId, callback) {
Admin.findById(adminId, callback);
}
module.exports.comparePassword = function (password, hash, callback) {
bcrypt.compare(password, hash, function (err, isMatch) {
if (err) throw err;
callback(null, isMatch);
});
}

Related

How do i test postman when i have passport authentication

Im trying to test using postman but i have a passport authentication which is blocking me from testing as it will redirect me to the login screen it is not authenticated.
How i can get a token or authenticate myself in postman to be able to test
I have tried to use /auth/local in postman but it just returns that i cant get the route
Passport.js
var LocalStrategy = require('passport-local').Strategy;
var { User } = require('../model/user.js');
var bcrypt = require('bcrypt');
module.exports = function (passport) {
passport.use(new LocalStrategy(function (username, password, done) {
let query = { username: username };
User.findOne(query, function (err, user) {
if (err) throw err;
if (!user) {
return done(null, false,{ message: 'No user found' });
}
bcrypt.compare(password, user.password, function (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);
});
});
}
Route.js
router.get('/register', function (req, res) {
res.sendFile(__dirname + "/views/register.html");
});
router.post('/register', async (req, res) => {
var data = req.body;
var salt = await bcrypt.genSalt(10)
var hashedpw = await bcrypt.hash(data.password, salt)
const newUser = await User.create({
name: data.name,
email: data.email,
username: data.username,
password: hashedpw,
});
newUser.save();
req.flash('success', 'You are now registered and can log in');
res.redirect('/');
});
router.get('/login', function (req, res) {
res.locals.success = req.flash('success');
res.locals.error = req.flash('message');
res.render(__dirname + "/views/login.ejs");
});
router.post('/login', async (req, res, next) => {
passport.authenticate('local', {
successRedirect: '/',
failureRedirect: '/login',
failureFlash: true
})(req, res, next);
});
router.get('/logout', async (req, res) => {
req.logout(function (err) {
if (err) { return next(err); }
req.flash('success', 'You are logged out');
res.redirect("/")
});
});
function ensureAuthenticated(req, res, next) {
if (req.isAuthenticated()) {
return next();
} else {
res.redirect('/login');
}
}
Following this guide:
https://mevelix.com/articles/postman-auth-for-laravel,4
you have to create the Command:
php artisan make:command DevPostman
then in the newly created class, copy the content it is showed in the link,
inside the class namespace App\Console\Commands\DevPostmanCommand
Then you can execute the command:
php artisan dev:postman web
in this way you are creating a simulated session.
This is my ouput, for example:
you paste this output directly in PostMan, inside the Tab Pre-Request Scripts:
In this way you are allowed to avoid the login inside Postman, because you are simulating a session.
See the first link to have the complete code of the DevPostmanCommand class.

400 Bad Request in NodeJs Application

When ever I submit a from to login in or get registered I get 400 bad request. But in register route the user get registered but it also gives bad request. When we go to login route same as register route I get BAD REQUEST. 0
I am using the following dependencies:
express session
passport
passport-local
passport-local-mongoose
Is there something wrong with the implementation of the passport-local-mongoose or its passport side or serialize or deserialize the user. Can anybody help me with this problem I am stuck on this for three days. Here is some code.
//-----------------------//Require---------------------
const express = require("express");
const app = express();
const bodyParser = require("body-parser");
const ejs = require("ejs");
const session = require("express-session");
const passport = require("passport");
const LocalStrategy= require("passport-local").Strategy;
const passportLocalMongoose = require("passport-local-mongoose");
const mongoose = require("mongoose");
//-----------------------//App.use---------------------
app.use(express.static("public"));
app.set("view engine", "ejs");
app.use(bodyParser.urlencoded({extended: true}));
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: true
}));
//-----------------------//Passport---------------------
app.use(passport.initialize());
app.use(passport.session());
//-----------------------//Mongoose---------------------
mongoose.connect('mongodb://localhost/Twitter', {useNewUrlParser: true, useUnifiedTopology: true});
mongoose.set('useCreateIndex', true);
const tweetschema = new mongoose.Schema({
username: String,
password: String,
tweets: String
});
//-----------------------//Schema Plgin---------------------
tweetschema.plugin(passportLocalMongoose);
//-----------------------//New Model---------------------
const Tweet = new mongoose.model("Tweet", tweetschema);
//-----------------------//Local Strategy-------------------
passport.use(new LocalStrategy(Tweet.authenticate()));
//-----------------------//Seralize Passport---------------------
passport.serializeUser(Tweet.serializeUser());
passport.deserializeUser(Tweet.deserializeUser());
//-----------------------//Get Routes---------------------
app.get("/" ,(req, res)=>{
Tweet.find({}, function(err, founItems){
res.render("home", {founItems:founItems});
});
});
app.get("/tweets", (req, res)=>{
if(req.isAuthenticated()){
res.render("Tweets");
}else{
res.redirect("/login");
}
});
//-----------------------//Post Routes---------------------
app.post("/login", (req, res)=>{
const user = new Tweet({
username: req.body.email,
password: req.body.password
});
req.logIn(user, (err)=>{
if(err){
res.send(err);
}
passport.authenticate("local")(req, res, ()=>{
console.log("Successfull.");
})
})
});
app.post("/reg", (req, res)=>{
Tweet.register({username: req.body.email}, req.body.password, (err, user)=>{
if(err){
console.log(err);
res.redirect("/reg");
}else{
if(user){
passport.authenticate("local")(req, res, ()=>{
res.redirect("/tweets");
console.log("Successfully Regsitered The User!");
})
}
}
})
})
You redirect user to /login route, but you don't have get request for this.
If you have it but not uploaded try this in Seralize Passport
passport.serializeUser(function (user, done) {
done(null, user.id);
});
passport.deserializeUser(function (id, done) {
User.findById(id, function (err, user) {
done(err, user);
});
});
What about this:
app.post("/login", (req, res) => {
const email = req.body.email;
User.findOne({ username: email }, function (err, u) {
if (err) {
console.log(err);
} else {
if (u) {
u.authenticate(req.body.password, (err, model, info) => {
if (info) {
res.send("Wrong email or password!");
}
if (err) {
console.log(err);
} else if (model) {
req.login(u, (err) => {
if (err) {
console.log(err);
} else {
passport.authenticate("local");
req.session.save((error) => {
if (err) {
console.log(err);
} else {
res.redirect("/");
}
});
}
});
}
});
} else {
res.send("Wrong email or password!");
}
}
});
});
So you first search user in the database with email: User.findOne({ username: email }, function (err, u){} I suggest to make username unique username: { type: String, unique: true} in tweetSchema.
After that you check for err. If u exists, you authenticate it with password. According to passport-local-mongoose- u.authenticate(password, (err, model, info)=>{}) has two arguments: password and callback function. In callback we check for info which is "an instance of AuthenticationError describing the reason the password failed, else undefined." After that we check for err and it is "null unless the hashing algorithm throws an error." And finally, we check for model that is "the model getting authenticated if authentication was successful otherwise false."
So, model is authenticated. After that we must use the user with req.login(u,(err)). Check for errors and if everything is alright, we authenticate user locally passport.authenticate("local");. If you want to save session, write:
req.session.save((error) => {
if (err) {
console.log(err);
} else {
res.redirect("/");
}
});
That's all.
For registration :
app.post("/register", (req, res) => {
const email = req.body.email;
const password = req.body.password
User.find({ email: email }, function (err, docs) {
if (docs.length === 0) {
User.register(
{
username: email,
},
password,
function (err, user) {
if (err) {
console.log(err);
} else {
req.login(user, (err) => {
if (err) {
console.log(err);
} else {
passport.authenticate("local");
req.session.save((error) => {
if (err) {
console.log(err);
} else {
res.redirect("/");
}
});
}
});
}
}
);
} else {
res.send("The accout already exists!");
}
});
});

TypeError Passport JS username property undefined

I am a beginner in Reactjs and nodejs trying to make my first full stack app. In my postjob schema I want the username of the current logged in user in the publisher field. Passport has a req.user command to get details of the current user but whenever I try to use this it gives the type error.If you guys need any other file let me know
postjob schema
const mongoose=require('mongoose')
const postjobtemplate= new mongoose.Schema({
jobtitle:{
type:String,
required:true
},
company:{
type:String,
required:true
},
officelocation:{
type:String,
required:true
},
jobtype:{
type:String,
required:true
},
publisher:{ ///<=HERE
type:String,
required:true
},
date:{
type:Date,
default:Date.now
}
})
module.exports=mongoose.model("postjobtable",postjobtemplate)
routes js
router.post('/postjob',async(request,response,next)=>{
const postjob=new postjobtemplatecopy({
jobtitle:request.body.jobtitle,
company:request.body.company,
officelocation:request.body.officelocation,
jobtype:request.body.jobtype,
publisher:request.user.username, //////<=HERE
})
postjob.save()
.then(data=>{
response.json(data)
})
.catch(error=>{
response.json(error)
})
});
router.post("/login", (req, res, next) => {
passport.authenticate("local", (err, user, info) => {
if (err) throw err;
if (!user) res.send("No User Exists");
else {
req.logIn(user, (err) => {
if (err) throw err;
res.status(200).send("Successfully Authenticated");
});
}
})(req, res, next);
});
server js
const express =require('express')
const app=express()
const mongoose=require('mongoose')
const dotenv=require('dotenv')
const routes=require('./routes/routes')
const cors=require('cors')
const passport=require("passport")
const passportlocal = require("passport-local").Strategy;
const bodyParser=require("body-parser")
const session=require("express-session")
const cookieparser=require('cookie-parser')
dotenv.config();
mongoose.connect(process.env.DB_CONNECT,{useNewUrlParser:true},()=>
console.log("database connected")
);
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended:true}));
app.use(express.urlencoded({ extended: true }));
app.use(cors({
origin:'http://localhost:3000',
credentials:true
}))
app.use(session({
secret:'secret',
resave:true,
saveUninitialized:true
}))
app.use(cookieparser("secret"))
app.use(passport.initialize());
app.use(passport.session());
require('./passport-config')(passport);
app.use(express.json())
app.use('/api',routes)
app.listen(4002,()=>console.log("server running on port 4002"))
module.exports = app;
passport config
const User = require("./models/user");
const bcrypt = require("bcryptjs");
const localStrategy = require("passport-local").Strategy;
module.exports = function (passport) {
passport.use(
new localStrategy((username, password, done) => {
User.findOne({ username: username }, (err, user) => {
if (err) throw err;
if (!user) return done(null, false);
bcrypt.compare(password, user.password, (err, result) => {
if (err) throw err;
if (result === true) {
return done(null, user);
} else {
return done(null, false);
}
});
});
})
);
passport.serializeUser((user, cb) => {
cb(null, user.id);
});
passport.deserializeUser((id, cb) => {
User.findOne({ _id: id }, (err, user) => {
const userInformation = {
username: user.username,
};
cb(err, userInformation);
});
});
};
You are getting typeError because username is not being sent/received properly. Change this line in your post route: publisher: request.user.username, for this: publisher: request.body.username,(I assume you are sending it from the front-end as username, if not, it would be useful to include your request from React in your code) that way you are actually receiving the data you're sending from the front end. Also, if you are using a different field other than username, you need to specify it in your passport strategy like this:
passport.use(
"yourStrategyName", //here you are naming the strategy to be used in your route
new localStrategy( {
usernameField: "publisher", //here you are telling passport to use "publisher" as a username
},(publisher, password, done) => {
User.findOne({ publisher }, (err, user) => { //changed to the field name in database
if (err) throw err;
if (!user) return done(null, false);
bcrypt.compare(password, user.password, (err, result) => {
if (err) throw err;
if (result === true) {
return done(null, user);
} else {
return done(null, false);
}
});
});
})
);
And in your route.js:
router.post("/login", (req, res, next) => {
passport.authenticate("yourStrategyName", (err, user, info) => { //include the strategy name as the first parameter
if (err) throw err;
if (!user) res.send("No User Exists");
else {
req.logIn(user, (err) => {
if (err) throw err;
res.status(200).send("Successfully Authenticated");
});
}
})(req, res, next);
});

Passport in node.js

Ok, I edit the question, Can someone help me with fix this code, to work??
I am fallow the guide with youtube about creating login system with using passport, I dont understand here about function
passport.use(new LocalStrategy(
When is she called, And from where it gets an argument login, password.
USER MODEL
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var UserSchema = new Schema({
login: { type: String, maxlength: 20, required: false },
password: { type: String, maxlength: 202, required: false },
updated: { type: Date, default: Date.now },
created: { type: Date, default: Date.now }
});
module.exports = mongoose.model('User', UserSchema);
module.exports.comparePassword = function(candidatePassword, hash, callback){
bcrypt.compare(candidatePassword, hash, function(err, isMatch) {
if(err) throw err;
callback(null, isMatch);
});
}
module.exports.getUserById = function(id, callback){
User.findById(id, callback);
}
User Model
var express = require('express');
var MongoClient = require('mongodb').MongoClient;
var router = express.Router();
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var Product = require('../../model/product.model');
var Category = require('../../model/category.model');
var mongoose = require('mongoose');
var User = require('../../model/user.model');
var _ = require('lodash');
var bcrypt = require('bcrypt');
const saltRounds = 10;
router.post('/create', function (req, res, next) {
var newUser = {
login: req.body.login,
password: req.body.password
}
console.log(req.body)
req.checkBody('login', 'Login is required').notEmpty();
req.checkBody('password', 'Password is required').notEmpty();
var errors = req.validationErrors();
if (errors) {
console.log(errors)
res.send(errors);
} else {
bcrypt.hash(newUser.password, saltRounds, function (err, hash) {
if (err) {
console.log(err)
} else {
newUser.password = hash;
var user = new User(newUser);
user.save()
.then(function (User) {
res.send(User);
})
}
});
req.flash('success_msg', 'You are registered and can now login');
//res.redirect('/');
}
});
passport.use(new LocalStrategy(
function(login, password, done) {
User.findOne({ login: login }, function(err, user){
if(err) {
console.log(err);
}
if(!user) {
return done(null, false, {
message: 'Unkown User'
})
}
});
User.comparePassword(password, user.password, function(err, isMatch){
if(err) throw err;
if(isMatch){
return done(null, user);
} else {
return done(null, false, {message: 'Invalid password'});
}
});
}));
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.getUserById(id, function(err, user) {
done(err, user);
});
});
router.post('/login', function (req, res, next) {
console.log(req.body)
passport.authenticate('local', { successRedirect: '/', failureRedirect: '/', failureFlash: true }),
function (req, res) {
res.redirect('/product/list')
}
})
module.exports = router;
When is she called?
passport.use(new LocalStrategy( will be used when you call passport.authenticate('local'..
In your project it is while getting post request with '/login'.
LocalStrategy provides strategy to authenticate requests. You can choose different strategy. Strategies, and their configuration, are supplied via the use() function. More information: Passport Documentation
From where it gets an argument login, password?
By default, LocalStrategy expects to find credentials in parameters named username and password. If your site prefers to name these fields differently, options are available to change the defaults.
From LocalStrategy Documentataion

Unable to authenticate using email with passport-local nodejs..tried several examples

I have been trying to use email and password to authenticate using passport-local. I had similar code when I was using username and it worked fine. With email, I made some changes however nothing is working. Right at the endpoint '/login' of type 'post' the condition !user condition in users.js (shown below as 2nd code snippet) is somehow executing. Its not even going inside passport.use. Following is the code:-
In user.js(model file),
var mongoose=require('mongoose');
var bcrypt=require('bcryptjs');
//user schema
var UserSchema=mongoose.Schema({
phone: {
type:String,
},
email:{
type: String,
index:{unique:true}
},
password:{
type: String
},
firstname:{
type: String
},
lastname:{
type: String
}
});
var User=module.exports = mongoose.model('User',UserSchema);
module.exports.getUserByUsername=function(email,callback){
var query={email:email};
User.findOne(query, callback);
}
module.exports.comparePassword=function(candidatePassword, hash, callback){
bcrypt.compare(candidatePassword, hash, function(err, isMatch) {
callback(null,isMatch);
});
}
}
In users.js(where i specify routes):
var express = require('express');
var router = express.Router();
var bodyParser=require('body-parser');
var User=require('../models/user');
var passport=require('passport');
var localStrategy=require('passport-local').Strategy;
router.post('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) {
return next(err);
}
if (!user) { /*this is where the problem is this code executes everytime*/
return res.send('User not found');
}
req.logIn(user, function(err) {
if (err) { return next(err); }
return res.json(user);
});
})(req, res, next);
});
passport.serializeUser(function(user, done) {
done(null, user.id);
});
//for sessions
passport.deserializeUser(function(id, done) {
User.getUserById(id, function(err, user) {
done(err, user);
});
});
//this doesnt seem to work
passport.use(new localStrategy({usernameField:'email', passwordField:'password'},function(email,password,done){
User.getUserByUsername(email, function(err,user){
if(err) throw err;
if(!user){
return done(null,false,{message: 'User not found'});
}
User.comparePassword(password, user.password, function(err, isMatch){
if(err) return done(err);
if(isMatch){
return done(null, user);
}
else{
return done(null,false,{message: 'Password doesnt match our records'});
}
});
});
}));
Note that there is no front end on this. I am just using postman to test my apis.
This code works fine with emails. The issue was I also have another file called admin.js and admins.js which do the same task as user and users. However, admin makes use of username. I had the same passport.use code for admin however, users was trying to access that file instead of the current one. Once I removed the admin code it all worked.

Resources