Local and Google strategy in passport.js: issue when serializing user - node.js

I've been trying to understand why I couldn't keep an user logged in once authenticated even though authentication itself was working. I even posted a question here: Passport.js - Local strategy doesn't authenticate
By trying to fix the issue, I finally worked out what's wrong.
The issue is the following: I have two different passport strategy, so I am serializing and deserializing the user twice. If I serialize the user with the local strategy first, local strategy will work, but Google's won't. And vice versa.
I put a comment to highlight the problem in app.js.
Here's the files:
app.js
const express = require("express"),
mongoose = require("mongoose"),
bodyParser = require("body-parser"),
cookieSession = require("cookie-session"),
localStrategy = require("passport-local"),
passport = require("passport");
const LocalUser = require("./models/localuser");
const keys = require("./config/keys"); // requiring keys
const authRoutes = require("./routes/auth"); // requiring auth routes
const mainRoutes = require("./routes/main");
//Initialize express app
const app = express();
mongoose.connect("mongodb://localhost/thoughtApp"); // connectiong database
app.use(express.static(__dirname + "/public"));
app.set("view engine", "ejs");
app.use(bodyParser.urlencoded({extended: true}));
app.use(cookieSession({
maxAge: 24 * 60 * 60 * 1000,
keys: [keys.session.cookieKey]
}));
//initialize passport
app.use(passport.initialize());
app.use(passport.session());
passport.use(new localStrategy(LocalUser.authenticate()));
passport.serializeUser(LocalUser.serializeUser());
passport.deserializeUser(LocalUser.deserializeUser());
app.use(function(req, res, next){
res.locals.user = req.user;
next();
});
app.use("/", mainRoutes); //main routes
app.use("/auth", authRoutes); // setup auth routes
const passportSetup = require("./config/passport-setup"); /// THIS IS THE ISSUE
// IF BeFORE LINE 33 ( passport.use(new localStrategy(LocalUser.authenticate()));, GOOGLE LOGIN WORKS BUT LOCAL DOESNT; IF AFTER, LOCAL WORKS BUT GOOGE DOESN'T; PROBABLY DUE TO SERIALIZE AND DESARIALIZE BEING USED ALREADY
app.listen(process.env.PORT || 3000, () => {
console.log("Server started.")
});
auth.js (auth routes)
const router = require("express").Router();
const passport = require("passport");
const LocalUser = require("../models/localuser");
const authCheck = function (req, res, next) {
if (!req.user) {
next();
} else {
res.redirect("/");
}
};
//login
router.get("/login", authCheck, (req, res) => {
res.render("login", {user: req.user});
});
router.post("/login", passport.authenticate("local", {
successRedirect: "/",
failureRedirect: "/login"
}), (req, res) => {
})
// logout
router.get("/logout", (req, res) => {
//handle with passport
req.logout();
res.redirect("/");
});
//register
router.get("/signup", authCheck, (req, res) => {
res.render("signup", {user: req.user});
});
router.post("/signup", (req, res) => {
LocalUser.register(new LocalUser({username: req.body.username}), req.body.password, (err, user) => {
if (err) {
console.log(err);
res.redirect("/auth/signup")
}
passport.authenticate("local")(req, res, () => {
console.log(user)
res.redirect("/");
})
})
})
// google auth
router.get("/google", authCheck, passport.authenticate("google", {
scope: ["profile"]
}))
//goes to google consent screen
// callback for google to redirect to
router.get("/google/redirect", passport.authenticate("google"), (req, res) => {
res.redirect("/profile");
});
module.exports = router;
passport-setup.js (google strategy setup)
const passport = require("passport");
const GoogleStrategy = require("passport-google-oauth20");
const keys = require("./keys");
const User = require("../models/user")
passport.serializeUser((user, done) => {
done(null, user.id);
});
passport.deserializeUser((id, done) => {
User.findById(id).then((user) => {
done(null, user);
});
});
passport.use(new GoogleStrategy({
//options for the google strategy
callbackURL: "/auth/google/redirect",
clientID : keys.google.clientID,
clientSecret : keys.google.clientSecret
}, (accessToken, refreshToken, profile, done) => {
//passport callback function
// check if user exists already
User.findOne({googleID: profile.id}).then((currentUser) => {
if (currentUser) {
console.log("user is: " + currentUser);
done(null, currentUser);
} else {
new User({
username: profile.displayName,
googleID: profile.id
}).save().then((newUser) => {
console.log("new user created: " + newUser);
done(null, newUser);
})
}
})
})
)
localuser.js
const mongoose = require("mongoose");
const passportLocalMongoose = require("passport-local-mongoose");
const localUserSchema = new mongoose.Schema({
username: String,
password: String
});
localUserSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model("localUser", localUserSchema);
How can I solve this?

So I have been struggling with the same and I don't know if you found a solution but I stumbled upon this link,
Linking all accounts together
So basically, you first need to check is in the request is req.user exists, if so add the fields you want to serialize and call done(null,newUser)
This should do the trick,
I hope i was clear enough

Related

Passport Local Strategy returns not found

I'm trying to make a register/login with node, express and mongoose. I want to register and login users and if they aren't logged in they can't retrieve data from the api.
To make the login and register i have been watching this tutorial: Link it isn't as addecuate for my needs because half of the efforts and code goes into the ejs but i have been okay until the passport login.
I have created my local strategy in config/passport.js:
const LocalStrategy = require("passport-local").Strategy;
const bcrypt = require("bcryptjs");
const User = require("../models/user");
module.exports = function (passport) {
passport.use(
new LocalStrategy({ usernameField: "email" }, (email, password, done) => {
User.findOne({ email: email })
.then((user) => {
if (!user) {
return done(null, false, {
message: "That email is not registered",
});
}
bcrypt.compare(password, user.password, (err, isMatch) => {
if (err) console.log(err);
if (isMatch) {
return done(null, user);
} else {
done(null, false, { message: "Incorrect Password" });
}
});
})
.catch((err) => console.log(err));
})
);
passport.serializeUser((user, done) => {
done(null, user.id);
});
passport.deserializeUser((id, done) => {
User.findById(id, (err, user) => {
done(err, user);
});
});
};
My app.js looks like this:
const express = require("express");
const cors = require("cors");
const usersRouter = require("./routes/users");
const passport = require("passport");
const session = require("express-session");
require("dotenv").config();
const app = express();
require("./config/passport")(passport);
const connectDB = require("./db/connect");
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(
session({
secret: "*****",
resave: true,
saveUninitialized: true,
})
);
app.use(passport.initialize());
app.use(passport.session());
app.use(cors());
app.use("/api/v1/users", usersRouter);
const PORT = process.env.PORT || 5000;
const start = async () => {
try {
await connectDB(process.env.MONGO_URI);
app.listen(PORT, console.log(`Server is listening on port: ${PORT}....`));
} catch (error) {
console.log(error);
}
};
const notFound = require("./middleware/notFound");
app.use(notFound);
start();
My users router looks like this:
const express = require("express");
const router = express.Router();
const { postRegister } = require("../controllers/register");
router.route("/register").post(postRegister);
const { postLogin } = require("../controllers/login");
router.route("/login").post(postLogin);
module.exports = router;
The register works perfectly, but the login controller doesn't. Each time i post to that endpoint instead of getting a succesMessage or failureMessage i get the page not found router that is called at the end of app.js, i checked if the router calls the login function via a console log and it does. Here is the code for the login controller:
const passport = require("passport");
const postLogin = (req, res, next) => {
console.log("The function has been triggered");
passport.authenticate("local", {
successMessage: "Login authenticated",
failureMessage: "Login failed",
})(req, res, next);
};
module.exports = {
postLogin,
};
Why don't I get the responses from the login controller or my local strategy and instead i get page not found when the Login controller actually works?

Bad Request in Node Js using passport Js

I am trying to build a registration and login page using passport-local-mongoose.
When I click on submit signup button I get an error which says bad request.
I am getting "Bad Request" while registering, but the details are being stored in MongoDB. Not sure where I am making a mistake.
Please help me out.
Here is my register POST API.
let express = require('express');
let mongoose = require('mongoose');
const passport = require('passport');
const LocalStrategy = require('passport-local');
const passportLocalMongoose = require('passport-local-mongoose');
var expressValidator = require('express-validator');
const bodyParser = require('body-parser')
const { check, validationResult } = require('express-validator');
const Admin = require('../models/admin-model');
let router = express.Router();
const app = express();
//Creating a Secret Key to Hash Password
router.use(require('cookie-session')({
secret: 'jdkjhLGUL#^&%^%(*)&^%#!gkjh', // Encode/Decore Session
resave: false,
saveUninitialized: false
}));
passport.use(new LocalStrategy({
usernameField: 'email',
passwordField: 'password'
}, Admin.authenticate()));
//Encrypting and Decrypting the Password for Security
passport.serializeUser(Admin.serializeUser()); //session Encoding
passport.deserializeUser(Admin.deserializeUser());
// passport.use(new LocalStrategy(Admin.authenticate()));
//Setting the View Engine to take EJS Pages
app.set('view engine', "ejs");
app.set('views', "./views")
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.use(passport.initialize());
app.use(passport.session());
app.use(express.static(__dirname + '/public'));
mongoose.connect("mongodb://localhost:27017/node-auth-db", { useNewUrlParser: true, useUnifiedTopology: true })
.then(() => console.log('Connected to node-auth-db successfully....!'))
.catch(err => console.log(err))
router.get('/', (req, res) => {
res.render('home');
});
router.get('/login', (req, res) => {
res.render('signin');
});
router.post('/login', passport.authenticate("local", {
successRedirect: '/admin/addnews',
failureRedirect: '/login'
}));
//Registration
router.get('/register', (req, res) => {
res.render('signup');
});
router.post('/register', (req, res) => {
Admin.findOne({ username: req.body.email }, (err, result) => {
if (err) throw err;
if (!result) {
Admin.register(new Admin({ name: req.body.name, username: req.body.email }),
req.body.password, function (err, admin) {
if (err) throw err;
passport.authenticate("local")(req, res, function () {
res.redirect('/admin/login');
})
}
)
}
else {
res.redirect('/admin/register');
}
});
});
//Add News
router.get('/addnews', isLoggedIn, (req, res) => {
res.render('addnews');
});
router.post('/addnews', (req, res) => {
News.create(req.body, (err, data) => {
if (err) throw err;
const htmlMsg = encodeURIComponent('Added News DONE !');
res.redirect('/admin/addnews');
})
});
//Creating a Authentication Token to secure the logging and Logout.
function isLoggedIn(req, res, next) {
if (req.isAuthenticated()) {
return next();
}
res.redirect('/admin/login');
}
router.get('/logout', (req, res) => {
req.logOut();
res.redirect('/admin');
});
module.exports = router;[]
Might be the issue is here you are passing req.body.password outside admin module
Admin.register(new Admin({ name: req.body.name, username: req.body.email, req.body.password}), function (err, admin) {
if (err) throw err;
passport.authenticate("local")(req, res, function () {
res.redirect('/admin/login');
})
}
)

Passport is not serializing my user in login

I am practicing of using passport.js for authentication but there is a problem in login part. I've tried to solve the problem by different way but nothing was successful, the problem is even after changing the passport.serialize to (user, done) the code worked but if I enter any fake password and username, it authenticate me.
How do I solve it?
// npm i passport passport-local passport-local-mongoose express-session
const express = require("express");
const app = express();
const bodyParser = require("body-parser");
app.use(express.urlencoded({ extended: false }));
app.use(express.static("public"));
const session = require("express-session"); // session first
const passport = require("passport"); //then passport
const passportLocalMongoose = require("passport-local-mongoose"); // last passport-local-mongoose
// no need to passport-local
app.use(session({ // use the session first and set it up
secret: "this is our secrete",
resave: false,
saveUninitialized: false
}));
app.use(passport.initialize()); // then use passport to initialize
app.use(passport.session()); // then use passport to deal with session
const mongoose = require("mongoose");
mongoose.connect("mongodb://localhost:27017/authentication", { useNewUrlParser: true, useUnifiedTopology: true })
mongoose.set("useCreateIndex", true); // to prevent the deprecation message
const authSchema = new mongoose.Schema({
email: String,
password: String
})
authSchema.plugin(passportLocalMongoose); //plug passportLocalMongoose in the schema
const Auth = new mongoose.model("Auth", authSchema);
passport.use(Auth.createStrategy());
passport.serializeUser(Auth.serializeUser()); // put a message in a cookie
passport.deserializeUser(Auth.deserializeUser()); // destroy the cookie and reveal the message
app.get("/", (req, res) => {
res.sendFile(__dirname + "/index.html");
})
app.get("/register", (req, res) => {
res.sendFile(__dirname + "/register.html");
})
app.get("/login", (req, res) => {
res.sendFile(__dirname + "/login.html");
})
app.get("/success", (req, res) => {
res.sendFile(__dirname + "/success.html");
})
app.get("/secret", (req, res) => {
console.log(req.isAuthenticated());
if (req.isAuthenticated()) {
res.sendFile(__dirname + "/secret.html")
} else {
res.sendFile(__dirname + "/login.html")
}
})
app.post("/register", (req, res) => {
var regEmail = req.body.email;
var regPassword1 = req.body.password1;
var regPassword2 = req.body.password2;
if (regPassword1 === regPassword2) {
Auth.register({ username: regEmail }, regPassword1, (err, user) => {
if (err) throw err;
passport.authenticate('local')
res.redirect("/success")
})
}
})
app.post("/login", (req, res) => {
const user = new Auth({
email: req.body.email,
password: req.body.password
});
req.login(user, (err) => {
if (err) throw err;
passport.authenticate("local")
res.redirect("/secret")
})
})
var port = 3000;
app.listen(port, () => {
console.log(`listening to ${port}`);
})
Currently the /login route is creating a new user by calling new Auth() and logging in with it regardless of if the password does not match the one being stored.
You should be able to use the passport.authenticate method on your route to verify that the user is registered in your Mongo database. This method also automatically calls req.login.
app.post("/login", passport.authenticate("local",{
successRedirect: "/secret",
failureRedirect: "/login"
}));
Here is a fully example that uses passport-local-mongoose for reference: https://gist.github.com/yukeehan/23fbbe53f1ca94be440161c1562b489a

express | node.js | oauth2 not authenticating

so, im using express, and passport-oauth2 / passport-discord to create a website for my discord bot, but the checkauth function always returns false, and during the auth process, i just get redirected to the main auth route and nothing happens. (i never get redirected to the dashboard)
heres the code: (i tried to include every file relative to the problem):
// strategy file
const DiscordStrategy = require('passport-discord').Strategy;
const passport = require('passport');
const DiscordUser = require('.././models/DiscordUser');
var session = require('express-session')
passport.serializeUser((user, done) =>{
done(null, user.id)
})
passport.deserializeUser(async (id, done) =>{
const user = await DiscordUser.findById(id);
if(user){
done(null, user);
}
})
passport.use(new DiscordStrategy({
clientID: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET,
callbackURL: process.env.CLIENT_REDIRECT,
scope: ['identify', 'guilds', 'email', 'bot']
}, async (accessToken, refreshToken, profile, done) => {
try{
const user = await DiscordUser.findOne({ discordId: profile.id});
if(user) {
console.log('a known user entered!')
done(null, user);
}else{
console.log('a new user entered!')
const newUser = await DiscordUser.create({
discordId: profile.id,
username: profile.username,
discriminator: profile.discriminator,
email: profile.email,
guilds: profile.guilds
});
const savedUser = await newUser.save();
done(null, savedUser);
}
}catch(err) {
console.log(err);
done(err, null);
}
}));
//checkauth file
module.exports = async (req, res, next) => {
if(req.isAuthenticated()){
return next();
} else {
return res.redirect("/auth");
}
};
//auth router
const router = require('express').Router();
const passport = require('passport');
router.get('/', passport.authenticate('discord'));
router.get('/redirect', passport.authenticate('discord', {
failureRedirect: '/forbidden',
successRedirect: '/dashboard'
}), (req, res) => {
res.sendStatus(200);
})
module.exports = router;
//dashboard router
const router = require('express').Router();
var session = require('express-session');
const CheckAuth = require('../CheckAuth');
router.get('/', CheckAuth, (req, res) => {
res.sendFile('../views/dashboard/index')
})
module.exports = router;
in the main file i just create a random cookie, define the routes, and use app.use(passport.initialize()) and app.use(passport.session()).
if you need anything else let me know, ty :)
In the documentation they use the callback method instead of successRedirect.
router.get('/redirect', passport.authenticate('discord', {
failureRedirect: '/'
}), function(req, res) {
res.redirect('/secretstuff') // Successful auth
});

Setting up passport for the first time. isAuthenticated() always returning false

I'm working through a learning module on authentication and security and I'm trying to get passport up and running but I seem to be having trouble. The code included below all works as you'd expect, except that when users are redirected from the /register post route to the /secrets route, they are not authenticated, in spite of .register() having worked (otherwise the logic in the route would have redirected me back to the /register get route instead of the login page via the secrets route).
require("dotenv").config();
const express = require("express");
const bodyParser = require("body-parser");
const mongoose = require("mongoose");
const ejs = require("ejs");
const session = require("express-session");
const passport = require("passport");
const passportLocalMongoose = require("passport-local-mongoose");
const app = express();
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(express.static("public"));
app.set("view engine", "ejs");
app.use(
session({
secret: "Our little secret.",
resave: false,
saveUninitialized: false
})
);
app.use(passport.initialize());
app.use(passport.session());
mongoose.connect("mongodb://localhost:27017/userDB", { useNewUrlParser: true });
mongoose.set("useCreateIndex", true);
const userSchema = new mongoose.Schema({
username: String,
password: String
});
userSchema.plugin(passportLocalMongoose);
const Users = new mongoose.model("Users", userSchema);
passport.serializeUser(Users.serializeUser());
passport.deserializeUser(Users.deserializeUser());
app.listen(3000, (req, res) => {
console.log("Listening on port 3000.");
});
app.get("/", (req, res) => {
res.render("home");
});
app.get("/login", (req, res) => {
res.render("login");
});
app.get("/register", (req, res) => {
res.render("register");
});
app.get("/secrets", (req, res) => {
console.log(req.isAuthenticated())
if (req.isAuthenticated()) {
res.render("secrets");
} else {
res.redirect("/login");
}
});
app.post("/register", (req, res) => {
console.log(req.body.username)
console.log(req.body.password)
Users.register(
{ username: req.body.username },
req.body.password,
(error, user) => {
if (error) {
console.log('there was an error: ', error);
res.redirect("/register");
} else {
passport.authenticate("local")(req, res, () => { //////////////not authenticating
res.redirect("/secrets");
});
}
}
);
});
app.post("/login", (req, res) => {});
any help figuring out why isAuthenticaed() is returning false would be greatly appreciated. thank you :)
You have to define your local strategy :
var passport = require('passport')
, LocalStrategy = require('passport-local').Strategy;
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);
});
}
));
here it has queried User information from the database ( Mongodb for instance) in case of successful response it will pass.

Resources