I am implementing user authentication and and once they are authenticated, they can CRUD their user data on the database. I can currently authenticate users and have users register a new account, but im not sure what my next step should be. What should my Node server pass back to my front end once my user is authenticated? I read everywhere that I am suppose to use tokens and sessions to check if user is auth for every request.
Auth.js
const router = express.Router()
const passport = require("passport")
router.post("/login", (req, res, next) => {
passport.authenticate("local", function(err, user, info) {
console.log(user)
if(err){
return res.status(400).json({errors: err})
}
if(!user){
return res.status(400).json({errors: "Incorrect username or password"})
}
//figure out what this does
req.logIn(user, function(err){
if(err){
return res.status(400).json({errors: err});
}
return res.status(200).json({success: `Logged in ${user.id}`})
})
})(req, res, next)
})
module.exports = router
Setup.js (for passportjs)
const bcrypt = require("bcrypt")
const User = require('../models/user')
const passport = require("passport");
const LocalStrategy = require("passport-local").Strategy;
passport.serializeUser((user, done) => {
done(null, user.id)
})
passport.deserializeUser((id, done) => {
User.findById(id, (err, user) => {
done(err, user);
})
})
passport.use(
new LocalStrategy({ usernameField: 'email' }, (email, password, done) => {
//Match user
User.findOne({ email: email })
.then(user => {
//match password
bcrypt.compare(password, user.password, (err, isMatch) => {
if (err) throw err;
if (isMatch) {
console.log("successfully auth")
return done(null, user);
} else {
console.log("incorrect password ")
return done(null, false, { message: "Incorrect Password" })
}
})
})
.catch(err => {
console.log("no account found")
return done(null, false, { message: err })
})
})
)
module.exports = passport
index.js
require('dotenv').config()
const express = require('express');
const session = require('express-session');
const mongoose = require("mongoose")
const MongoStore = require("connect-mongo")
const cors = require("cors");
const axios = require('axios');
const User = require('./models/user')
const bcrypt = require('bcrypt')
const passport = require("./passport/setup")
const auth = require("./routes/auth")
const app = express();
app.use(cors());
let port = process.env.PORT;
if (port == null || port == "") {
port = 5000;
}
mongoose.connect(process.env.ATLAS_URI).then(console.log("MongoDB Connection Success")).catch(err => console.log("MongoDB Connection Failed" + err))
// For backend and express init
app.use(express.json());
app.use(express.urlencoded({ extended: false }))
app.use(session({
secret: 'random secret',
resave: false,
saveUninitialized: true,
store: MongoStore.create({ mongoUrl: process.env.ATLAS_URI }),
}));
app.use(passport.initialize())
app.use(passport.session())
app.use("/auth", auth)
app.post('/register', (req, res) => {
const email = req.body.email
const plainTextPassword = req.body.password;
//check if user already exists
User.find({ name: email }, (err, existingUser) => {
if (existingUser.length === 0) {
bcrypt.hash(plainTextPassword, 8, async (err, hash) => {
try {
const user = new User({
email: email,
password: hash
});
let result = await user.save();
if (result) {
console.log("account registered successfully")
res.send(result)
}
} catch (e) {
res.send("Something Went Wrong");
console.log("something went wrong ---" + e)
}
})
} else {
console.log("user account already exists! Login instead. ")
}
})
})
Im not trying to debug an error, just need to know what the approach is. Basically, I want the user to login once, and then be able to navigate around the web app and crud to their database. Thank you!!
My sessions are stored in a different collection in mongodb but idk what to do with this
Related
my problem is that I want to access to the user who is logged in. All users are saved in a database and then SELECTED into a variable.
In the passport-config-js I am checking the login by comparing the email and the hashed password. So I hope you all can help me, I need this for a school project.
severs.js
const express = require('express');
const app = express();
const bcrypt = require('bcrypt');
const passport = require('passport');
const flash = require('express-flash');
const session = require('express-session');
const methodOverride = require('method-override');
const initializePassport = require('./passport-config');
initializePassport(
passport,
email => users.find(user => user["email"] === email),
id => users.find(user => user["id"] === id)
)
const users = [];
app.set('view-engine', 'ejs');
app.use(express.urlencoded({extended: false}))
app.use(flash());
app.use(session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(methodOverride('_method'));
app.use(express.static("public"))
app.get('/' , checkAuthenticated,(req, res) => {
res.render('startseite.ejs')
})
app.get('/login', checkNotAuthenticated ,(req, res) =>{
res.render('login.ejs');
})
app.post('/login', checkNotAuthenticated ,passport.authenticate('local',
{
successRedirect: '/startseite',
failureRedirect: '/login',
failureFlash: true
})
)
app.get('/startseite',checkAuthenticated,(req, res) =>{
res.render('startseite.ejs', {email: req.body.email});
console.log(req.body);
})
app.post('/register', checkNotAuthenticated,async (req, res, next) =>{
try {
const hashedPassword = await bcrypt.hash(req.body.password, 10);
let newUser = {
id: Date.now().toString(),
name: req.body.name,
email: req.body.email,
password: hashedPassword
}
if(users.length){
for(let i = 0; i < users.length; i+
if(req.body.email === users[i].email){
console.log(users[i].email + " email already taken");
alert("Email already taken");
res.render('/register');
}
}
}
let sql = `INSERT INTO Players (email, name, password) VALUES ('${newUser.email}', '${newUser.name}', '${newUser.password}')`;
connection.query(sql, (err, result) =>{
if(err) throw err;
console.log("User with email: " + newUser.email + " inserted")
allPlayers();
});
res.redirect('/login');
} catch {
res.redirect('/register');
}
//console.log(users);
})
app.delete('/startseite', (req, res) =>{
req.logOut();
res.redirect('/login');
})
function checkAuthenticated(req, res, next) {
if(req.isAuthenticated()){
return next()
}
res.redirect('/login');
}
function checkNotAuthenticated(req, res, next) {
if(req.isAuthenticated()){
return res.redirect('/');
}
next();
}
function allPlayers(){
let userList = query("*");
userList
.then()
.then((values) =>{
let i = 0;
while(values[i] != null){
users.push(values[i]);
i++;
}
//console.log(users);
})
}
allPlayers();
app.listen(3000);
passport-config.js
const LocalStrategy = require('passport-local').Strategy;
const bcrypt = require('bcrypt');
const {connection, select, query} = require('./db');
function initialize(passport, getUserByEmail, getUserById){
const authenticateUser = async (email, password, done) =>{
const user = getUserByEmail(email);
if(user == null){
return done(null, false, {message: 'email or password incorrect'});
}
try {
if(await bcrypt.compare(password, user.password)) {
return done(null, user);
} else {
return done(null, false, {message: 'email or password incorrect'})
}
} catch(e) {
return done(e)
}
}
passport.use(new LocalStrategy({usernameField: 'email'},
authenticateUser));
passport.serializeUser((user, done) => done(null, user.id))
passport.deserializeUser((id, done) => {return done(null, getUserById(id))})
}
module.exports = initialize;
I am making a authentication form were users can register and login.
if (process.env.Node_ENV !== 'production') {
require('dotenv').config()
}
const express = require('express');
const app = express();
const bcrypt = require('bcrypt')
const passport = require("passport")
const flash = require("express-flash")
const sessions = require("express-session")
const methodOverride = require("method-override")
const fs = require('fs')
const initializePassport = require("./passport-config")
initializePassport(
passport,
email => users.find(user => user.email === email),
id => users.find(user => user.id === id)
)
const users = []
app.set("view-engine", "ejs")
app.use('/public', express.static('public'))
app.use(express.urlencoded({extended: false}))
app.use(flash())
app.use(sessions({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false
}))
app.use(passport.initialize())
app.use(passport.session())
app.use(methodOverride('_method'))
app.get("/", checkAuthenticated, (req,res)=> {
res.render("index.ejs", { name: req.user.name})
})
app.get("/login", checkNotAuthenticated, (req, res) => {
res.render("login.ejs")
})
app.post("/login", checkNotAuthenticated, passport.authenticate('local', {
successRedirect: "/",
failureRedirect: "/login",
failureFlash: true
}))
app.get("/register", checkNotAuthenticated, (req,res)=> {
res.render("register.ejs")
})
app.post("/register", checkNotAuthenticated, async (req,res) => {
try{
//encrypt the Uses password, so "we" cant see it
const hashedPassword = await bcrypt.hash(req.body.password, 10)
users.push({
id: Date.now().toString(),
name: req.body.name,
email: req.body.email,
password: hashedPassword
})
//if all the above is right, than redirect the user to login page
res.redirect("/login")
}catch{
//if for some reason there is a failure, redirect back to register
res.redirect("/register")
}
//if there is user that is added, it is possible to see ind the console
//console.log(users)
localStorage.setItem('user', JSON.stringify(users));
})
app.delete('/logout', (req,res) => {
req.logOut()
res.redirect('/login')
})
function checkAuthenticated(req, res, next){
if (req.isAuthenticated()) {
return next()
}
res.redirect('/login')
}
function checkNotAuthenticated(req, res, next) {
if (req.isAuthenticated()) {
return res.redirect('/')
}
next()
}
The problem is that I need to implement localStorage, and the user needs to be able to delete the account once they are logged in.
To register the user only needs "Name", "email" and "password"
here is some of my passport-config if necessary:
const LocalStrategy = require('passport-local').Strategy
const bcrypt = require("bcrypt")
function initialize(passport, getUserByEmail, getUserById){
const authenticateUser = async (email, password, done) => {
const user = getUserByEmail(email)
if (user == null) {
return done(null, false, {message: "No user with that Email"})
}
try{
if(await bcrypt.compare(password, user.password)) {
return done(null, user)
} else{
return done(null, false, {message: "Passwrod incorrect"})
}
}catch(error){
return done(error)
}
}
passport.use(new LocalStrategy({usernameField: 'email'}, authenticateUser))
passport.serializeUser((user,done) => done(null, user.id))
passport.deserializeUser((id,done) => {
return done(null, getUserById(id))
})
}
module.exports = initialize;
Everything works fine, so there is no error in the code it's self, I just don't seem to find an answer to what I need to do anywhere, I can only find mongoDB which I don't want to use. The most important for me is to implement the localStorage, then it comes the delete part.
I hope some of you can help me!
Thanks a lot.
There are many similar posts to this one, but I haven't found a qualifying solution in any of the answers to those posts that have helped me with this.
the code
"use strict";
require('dotenv').config();
const auth = require('./auth.js');
const express = require('express');
const passport = require('passport');
const bcrypt = require('bcrypt');
const mongo = require('mongodb');
const session = require('express-session');
const cors = require('cors');
const util = require('util');
const app = express();
const port = process.env.PORT || 8080;
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({extended:true}));
app.use(session({
secret: process.env.SESSION_SECRET,
resave: true,
saveUninitialized: true,
cookie: {
secure: false,
maxAge: 1000 * 60 * 60 * 24 * 7
}
}));
app.use(passport.initialize());
app.use(passport.session());
mongo.connect(process.env.DATABASE, {useNewUrlParser: true, useUnifiedTopology: true}, (err, db) => {
if (err) {
console.log('Database error: ' + err);
} else {
console.log('Database connection successful');
auth(app, db);
app.route('/test').get((req, res) => {
res.send('The connection works!')
});
const ensureAuthenticated = (req, res, next) => {
console.log('isAuth() is: ' + req.isAuthenticated());
console.log('session store: ' + util.inspect(req.session, {showHidden: false, depth: null}));
if (req.isAuthenticated()) return next();
res.send('user not authenticated, begone! >:(');
}
app.route('/profile').get(
ensureAuthenticated,
(req, res) => {
res.render({username: req.user.username});
}
);
app.post('/login',
(request, response, next) => {
console.log(request.session)
passport.authenticate('local',
(err, user, info) => {
if(!user){ response.send(info.message);}
else{
request.login(user, function(error) {
if (error) return next(error);
console.log("Request Login supossedly successful.");
return response.send('Login successful');
});
//response.send('Login successful');
}
})(request, response, next);
}
);
app.route('/register').post((req, res, next) => {
const hash = bcrypt.hashSync(req.body.password, 13);
db.db().collection('users').findOne({username: req.body.username}, (err, user) => {
if (err) {
next(err);
} else if (user) {
res.send('user already exists :(');
} else {
db.db().collection('users').insertOne({
username: req.body.username,
password: hash
},
(err, doc) => {
if (err) {
res.send('registration mongo error');
} else {
next(null, user);
}
}
)
}
})
},
passport.authenticate('local', {failureMessage: 'passport authenticate failure'}),
(req, res, next) => {
console.log('registration successful');
req.logIn(req.user, err => {
if (err) next(err)
return console.log("i'm trying: " + req.user);
});
res.send('registration successful!!! :D');
}
);
app.listen(port, () => {console.log(`Listening on port: ${port}`)});
}
});
auth.js
const passport = require('passport');
const LocalStrategy = require('passport-local');
const ObjectID = require('mongodb').ObjectID;
const bcrypt = require('bcrypt');
module.exports = (app, db) => {
passport.use(new LocalStrategy(
(username, password, done) => {
db.db().collection('users').findOne({username: username}, (err, user) => {
console.log(`${username} attempted to log in`);
if (err) return done(err);
if (!user) return done(null, false);
if (!bcrypt.compareSync(password, user.password)) return done(null, false);
console.log('local strategy successful');
return done(null, user);
})
}
));
passport.serializeUser((user, done) => {
console.log(user.username + " serialized");
done(null, user._id);
});
passport.deserializeUser((id, done) => {
db.db().collection('users').findOne(
{_id: new ObjectID(id)},
(err, doc) => {
done(null, doc);
}
);
});
}
The Question:
All of the functions work just fine, I get all of the success return messages, and even register saves the user to the database, and login loads it successfully from the database. The only problem I'm having is that req.isAuthenticated() in the function ensureAuthenticated is always returning false, and as you can see, I really really really need it to be true so I can send the information to the client for the /profile route, and do all the other things I need with Passport. What am I missing?
Solution: I needed to add credentials: true to cors(), and something similar to the http headers (withCredentials:true for axios) in the client. I'm adding this because I know somebody is going to have this same problem one day, and will probably have as hard of a time finding this answer as I did.
Extra: In most forums that I asked this question in, the answers I got were all people not believing that this code works, and patronizing me by telling me I need to re-learn passport and react (if I got an answer at all lmao).
I've just included Passport js in my project for auth. But now my POST requests to create a new user hang and return an alert on the client saying "Not authorized" after I close the server. My old code (without Passport) still works so I don't think it's an issue with proxy-ing from client to server
The last console log I see is the first log in the user API, console.log('received request ', req.body)
There are no logged error messages, except when I stop the server I'll get
"Proxy error: Could not proxy request /api/user from localhost:3000 to http://localhost:5000/. [1] See https://nodejs.org/api/errors.html#errors_common_system_errors for more information (ECONNRESET).
///app.js (server)
const express = require("express");
const bodyParser = require("body-parser");
const cors = require('cors');
const mongoose = require("mongoose");
const routes = require("./routes"); //Used to end with /api
const path = require("path");
require("dotenv").config();
const passport = require('passport');
const app = express();
const port = process.env.PORT || 5000;
//database
mongoose
.connect(process.env.DB, { useNewUrlParser: true, useUnifiedTopology: true })
.then(() => console.log("Database connected successfully"))
.catch((err) => console.log(err));
mongoose.Promise = global.Promise;
app.use((req, res, next) => {
res.header("Access-Control-Allow-Origin", "*");
res.header(
"Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept"
);
next();
});
require('./config/passport');
app.use(cors());
app.use(bodyParser.json());
app.use(passport.initialize());
app.use("/", routes); //Load API - this folder has an index.js file
app.use((err, req, res, next) => {
console.log("$!$", err);
next();
});
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
///users.js (API for User model)
const passport = require('passport');
const express = require("express");
const router = express.Router();
const User = require("../models/User");
//Old - This works, which makes me think the issue is with Passport js
// router.post("/user", (req, res, next) => {
// try {
// User.create(req.body)
// .then((data) => res.json(data))
// .catch(next);
// } catch {
// res.json({
// error: "Failed to upload new user",
// });
// }
// });
//New
router.post("/user", (req, res, next) => {
console.log('received request ', req.body);
passport.authenticate('register', (err, hashedPassword, info) => {
if (err) {
console.log("Passport err on register ", err);
}
else if (info != undefined) {
console.log("Defined err ", info.message);
res.send(info.message);
} else {
req.login(user, err => {
const newUser = {
firstName: req.body.firstName,
lastName: req.body.lastName,
email: req.body.email,
password: hashedPassword,
};
User.create(newUser)
.then((data) => res.json(data))
.catch(next);
});
}
});
});
//passport.js (for auth)
const bcrypt = require('bcrypt');
const BCRYPT_SALT_ROUNDS = 12;
const passport = require('passport'),
localStrategy = require('passport-local').Strategy,
User = require('../models/User'),
JWTstrategy = require('passport-jwt').Strategy,
ExtractJWT = require('passport-jwt').ExtractJwt;
const opts = {
jwtFromRequest: ExtractJWT.fromAuthHeaderWithScheme('JWT'),
secretOrKey: 'secret key',
};
passport.use(
'register',
new localStrategy(
{
usernameField: 'email',
passwordField: 'password',
},
(email, password, done) => {
try {
console.log('searching for User');
User.findOne({ email })
.then(user => {
if (user != null) {
console.log('email already taken');
return done(null, false, { message: 'email already in use' });
} else {
console.log('email is available');
bcrypt.hash(password, BCRYPT_SALT_ROUNDS).then(hashedPassword => {
console.log('hashed password created for new user');
return done(null, hashedPassword, null);
});
}
})
} catch(err) {
done(err);
}
}
)
)
passport.authenticate in your code isn't used as a middleware but just like a regular function called inside the request handler. As of passport documentation you should call the returning function when used this way, like this:
passport.authenticate('register', (err, hashedPassword, info) => {
// ...
})(req, res, next); // <- ADD ARGUMENTS AND CALL
I have a tremendous headche with a problem when I try to login using Passport.
I'm making a post request to /login with an email and password. Passport authenticates it correctly, err isn't called and then return res.redirect('/user') gets called too but it returns 404 and doesn't redirect.
I don't know why this is happening. Here's my code:
Server (index.js):
const
express = require('express'),
app = express(),
mongoose = require('mongoose'),
passport = require('passport'),
cookieSession = require('cookie-session'),
bodyParser = require('body-parser'),
keys = require('./config/keys'),
user = require('./models/User'),
passportService = require('./services/passport'),
authRoutes = require('./routes/auth');
mongoose.connect(keys.mongoURI);
app.use(bodyParser.json());
app.use(
cookieSession({
maxAge: 15 * 24 * 60 * 60 * 1000,
keys: [keys.cookieKey]
})
);
app.use(passport.initialize());
app.use(passport.session());
passportService(passport);
authRoutes(app);
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => {
console.log(`App listening on port ${PORT}`);
});
passportService (passport.js):
const LocalStrategy = require('passport-local').Strategy;
const mongoose = require('mongoose');
const User = mongoose.model('users');
module.exports = (passport) => {
passport.serializeUser((user, done) => {
done(null, user._id);
});
passport.deserializeUser((id, done) => {
User.findById(id).then(user => {
done(null, user);
});
});
passport.use(
new LocalStrategy(
{
usernameField: 'emailaddr',
passwordField: 'passwd'
},
function(username, password, done) {
User.findOne({ email: username }, function(err, user) {
if(err){
return done(err);
}
if(!user) {
console.log('User not found with email: ', username);
return done(null, false);
}
if(user.password != password) {
console.log('Invalid password');
return done(null, false)
}
return done(null, user);
});
}));
}
Authentication route:
const passport = require('passport');
module.exports = app => {
app.post('/api/login', function(req, res, next) {
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('/user');
});
})(req, res, next);
});
}
There is not route for /user because I'm working with React and React Router in the client.
Please I need your help!!!
I would suggest not using the custom callback on your authenticate function. Instead, check to see if after the api is called and authentication is run if you have a user attached to the request object.
// calling redirects from your api will be problematic with react router
// allow your front end to decide what route to call based on the response
app.post('/api/login', passport.authenticate('local'), function(req, res, next) {
if(req.user) {
res.json(req.user);
} else {
// handle errors here, decide what you want to send back to your front end
// so that it knows the user wasn't found
res.statusCode = 503;
res.send({message: 'Not Found'})
}
});