TypeError: [function] is not a function in Passport local strategy - mongodb

I'm trying to authenticate users locally with Passport.js while not keeping session and using my own JWTokens.
I was following this tutorial:
Learn using JWT with Passport authentication
While also reading Passport.js documentation. I don't know what went wrong, but passport doesn't seem to notice that some functions are indeed functions.
I've got a Load function to select a User from the DB(mongo) given certain criteria(a user might logIn with e-mail or phone number).
load: function(options, cb) {
options.select = options.select || 'email phone';
return this.findOne(options.criteria)
.select(options.select)
.exec(cb);
}
I'm calling passport.authenticate in my routes:
// Controllers //
const Users = require('../../app/controllers/users');
...
...
app.post('/api/login', passport.authenticate('local', { failureRedirect: '/api/login' }), Users.login);
And here's my local strategy:
const mongoose = require('mongoose');
const User = mongoose.model('User');
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
passport.use(new LocalStrategy(
{
usernameField: 'email',
phoneField: 'phone',
passwordField: 'password',
session: false
},
function(email, phone, password) {//cb == callback
const options = {
criteria: { email: email, phone: phone },
select: 'name username email hashed_password salt'
};
User.load(options, function(err, user) {
if (err || !user){
return res.status(400).json({
type: 'failure',
message: "User creation failed",
data: [err]
});
};
if (!user.authenticate(password)) {
return res.status(400).json({
type: 'failure',
message: "User creation failed",
data: [err]
});
};
req.login(user, {session: false}, (err) => {
if (err) {
res.send(err);
}
// generate a signed son web token with the contents of user object and return it in the response
const token = jwt.sign(user.id, 'your_jwt_secret');
return res.json({user, token});
});
});
}
));
I'm getting the following error:
TypeError: res.status is not a function
Before trying to get stuff back from passport with responde. I was trying to do it with cb(callback), as done in the tutorial, but I keep getting the same error.
Thanks in advance for any help!

There are a few issues with how you are implementing Passport's local strategy that are causing problems.
You are trying to use two fields as the username when Passport's local startegy only accepts one. (see: http://www.passportjs.org/packages/passport-local/)
function(username, password, done){}
If you want to use both as a username, you might want to consider creating your own custom strategy. This is going to be a little more in-depth, but you can start learning on the Passport Github page (https://github.com/jaredhanson/passport-strategy)
The second issue is that you are trying to get Passport to send a response in the local strategy which is not what it is intended to do. Instead, you should be passing the errors and return values to Passport's done() function, which will process them accordingly.
Here is an example of what your local strategy should look like:
passport.use(
new LocalStrategy(async (email, phone, password, done) => {
const options = {
criteria: { email, phone },
select: 'name username email hashed_password salt',
};
try {
const user = await User.load(options);
/**
* If null is returned meaning there was no user found, send the done call
* with the false flag. This tells passport to redirect to the failure URL.
*/
if (!user) {
return done(null, false);
}
/**
* If the user's password is incorrect, also return the done function with the false
* flag. This tells passport to redirect to the failure URL.
*/
if (!user.authenticate(password)) {
return done(null, false);
}
/**
* If a user is found and their password is verified, send the user object to
* the done function. This will tell Passport to call the next middelware attaching
* the user object.
*/
return done(null, user);
} catch (err) {
/**
* If there is an error with the DB call, return generic message
* for security purposes.
*/
return done('There was an internal server error.');
}
})
);
and an example of what your load function should look like:
load: options => {
return new Promise(async (resolve, reject) => {
options.select = options.select || 'email phone';
try {
const user = await this.findOne(options.criteria)
.select(options.select)
.exec();
resolve(user);
} catch (err) {
reject(err);
}
});
};
As a general best practice, I changed your callbacks to the newer method of promises (https://developers.google.com/web/fundamentals/primers/promises).
This should work in the way you are intending to use Passport.

Related

What does it mean if a post with status 200 is left waiting?

I am doing a POST to an enpoint to authenticate users. The endopoint is "/user/login". I make a post and receive status code 200 but the Postman, and also my client, are waiting for the RES object that does not arrive.
This is a screenshot of the API call through Postman:
This is controler in server side:
router.post(
'/user/login',
passport.authenticate('local'),
UserCtrl.getLogin,
)
getLogin = (req, res, next) => {
console.log("req: ", req.body)
console.log('logged in', req.user);
var userInfo = {
username: req.user.username
};
res.json(userInfo)
}
The console prints the lines in the controller, and the user is effectively authenticated, for example:
req: { username: 'fedex', password: 'fedex' }
logged in {
_id: new ObjectId("62a8b00f468c563699d7dfc2"),
username: 'fedex',
password: '$2a$10$cdbh0oCBNpHxxwebsvArLOAFwetVAh5LTnQwk1Lg9kjWkjAWhfxym',
__v: 0
}
probably the problem is in the invocation of the local passport strategy, but I only do the standard:
const strategy = new LocalStrategy(
function(username, password, done) {
User.findOne({ username: username }, (err, user) => {
if (err) {
return done(err)
}
if (!user) {
return done(null, false, { message: 'Incorrect username' })
}
if (!user.checkPassword(password)) {
return done(null, false, { message: 'Incorrect password' })
}
return done(null, user)
})
}
)
EDITED: If I remove the middleware where the passport.authenticate('local') is invoked, and incorporate the authentication functionality directly in the controller, it works. But what is wrong with calling passport in the route?
BEFORE (does not work):
router.post(
'/user/login',
passport.authenticate('local'),
UserCtrl.getLogin
)
AFTER (adding passport authentication inside the controller, it work)
router.post(
'/user/login',
UserCtrl.getLogin
)

Setup error handlers in express/mongoose/mongoDB

I'm currently part of a web dev Bootcamp and my current project is requesting I create error handlers in a specific manner that I do not understand. Below is a screenshot of the directions . . .
Here are my current files in hopes that it makes sense . . .
/* app.js */
const express = require('express');
const mongoose = require('mongoose');
const userRouter = require('./routes/users');
const cardRouter = require('./routes/cards');
const { PORT = 3000 } = process.env;
const app = express();
mongoose.connect('mongodb://localhost:27017/aroundb', {
useNewUrlParser: true,
useCreateIndex: true,
useFindAndModify: false,
useUnifiedTopology: true,
});
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use((req, res, next) => {
req.user = { _id: '60c4e0e2a80be4c8c2de5474' };
next();
});
app.use('/users', userRouter);
app.use('/cards', cardRouter);
app.listen(PORT, () => logMsg(`listening on port ${PORT} . . .`));
/* routes/users.js */
const express = require('express');
const { getUsers, getUser, createUser } = require('../controllers/users');
const router = express.Router();
router.get('/', getUsers);
router.get('/:id', getUser);
router.post('/', createUser);
module.exports = router;
/* controllers/users.js */
const User = require('../models/user');
module.exports.getUsers = (req, res) => {
User.find({})
.then((users) => res.status(200).send({ data: users }))
.catch((err) => res.status(500).send({ message: err }));
};
module.exports.getUser = (req, res, next) => {
User.findById(req.params.id)
.then((user) => res.send({ data: user }))
.catch((err) => res.status(404).send({ message: err }));
};
module.exports.createUser = (req, res) => {
const { name, about, avatar } = req.body;
User.create({ name, about, avatar })
.then((user) => res.status(201).send({ data: user }))
.catch((err) => res.status(400).send({ message: err }));
};
My questions are:
Where should the code example provided (in the screenshot) go? Am I creating a separate controller or middleware? Or maybe it goes in the already coded controller?
Would I be creating my own errors and using a conditional to read the message?
I already thought I was handling errors, as seen in controllers/users.js, is that not the case?
NOTE: My apologies, I know that since it's from a course it might not make sense outside the context of the lesson(s). I also know there are various ways projects can be coded/solved. Unfortunately, my Bootcamp does not have live instruction, just a slack channel where 97% of responses come from Alumni. Please do not hesitate to ask questions that may help clarify things.
It seems you're directly sending an error in the last two cases, without knowing which type of it is, however it looks fine for fetching all users (1st case).
The workaround that might help you is,
Get user :
User.findById(req.params.id), function(err, user) {
if(err) {
return res.status(500).send({ message: "Default error" });
} else if (!user) {
return res.status(404).send({ message: "User not found" });
}
}
For creating a user you need to manually verify all the fields that are required in schema for ex.,
createUsers : {
const { name, about, avatar } = req.body;
if (name === null || about === null || avatar === null) {
return res.status(400).send({
message : "Required data missing in request"
})
}
... // create user
}

Understanding session in sailsJs with Passport

I have had many problems, when I want to get information from user model. I read some solutions, but I didnt understand.
This is my code:
* AuthController
var passport = require('passport');
module.exports = {
_config: {
actions: false,
shortcuts: false,
rest: false
},
login: function(req, res) {
passport.authenticate('local', function(err, user, info) {
if ((err) || (!user)) {
return res.send({
message: info.message,
user: user
});
}
req.logIn(user, function(err) {
if (err) res.send(err);
return res.send({
message: info.message,
user: user
});
});
})(req, res);
},
logout: function(req, res) {
req.logout();
res.redirect('/');
},
signup: function (req, res) {
var data = req.allParams();
User.create({email:data.email,password:data.password,name:data.name}).exec(function(error,user){
if(error) return res.negotiate(err);
if(!user)return res.negotiate(err);
return res.ok();
});
}
};
*view
<h1>List of my dates</h1>
<h1><%= email %></h1>
<h1><%= req.user.name %></h1>
*model
attributes: {
email: {
type: 'email',
required: true,
unique: true
},
password: {
type: 'string',
minLength: 6,
required: true
},
toJSON: function() {
var obj = this.toObject();
delete obj.password;
return obj;
}
},
beforeCreate: function(user, cb) {
bcrypt.genSalt(10, function(err, salt) {
bcrypt.hash(user.password, salt, function(err, hash) {
if (err) {
console.log(err);
cb(err);
} else {
user.password = hash;
cb();
}
});
});
}
};
Only works if I use res.render('view', {email: req.user.email}) but, I would like to use the user data in many views. I cant write methods with Current user params, becouse dont work.
Thanks.
It is unclear to me what your actual problem is or what the question actually is but I will try to help.
Look here:
login: function(req, res) {
passport.authenticate('local', function(err, user, info) {
if ((err) || (!user)) {
return res.send({
message: info.message,
user: user
});
}
...
})(req, res);
},
There you are adding data (locals) to the ejs and the values are message and user so in the ejs you must reference it as this, so you will use user.name and not req.user.name? I'm not sure why you're binding the (req, res) either.
It's confusing because your ejs uses the email value but I don't see it there as a local so maybe thats your problem, it must be defined?
Consider the following simple example:
// User Controller
// GET request /signin
// The signin form
signin(req, res) {
// Load the view from app/views/*
return res.view('signin', {
title: 'Sign In'
});
},
// POST request to /signin
// This was posted from the signin form
// Use io.socket.post(...) to do this from the signin form
// Can use window.location.replace('/account') on successful request
authenticate(req, res) {
// The data posted, email and password attempt
var data = req.allParams();
// Does it match?
User.findOne({
email: data.email,
// This is stupid, don't ever use plain text passwords
password: data.password
})
.exec(function(err, user) {
// Server related error?
if (err) res.serverError(err.message);
// No user was found
if (!user) res.badRequest('Username or password not found');
// Sign the user in
req.session.userId = user.id;
// User was found
res.ok();
});
},
// GET request to /account
// Displays the users information
// Can use policies to ensure that only an authenticated user may access their own account information
account(req, res) {
// If the user is not signed in
// This is an alternative to using the sails policy isLoggedIn
if (!req.session.userId) res.redirect('/signin');
// Get the users details
User.findOne({
id: req.session.userId
})
.exec(function(err, user) {
// Server related error?
if (err) res.serverError(err.message);
// No user was found
if (!user) res.redirect('/signin');
// Load the ejs file that displays the users information
return res.view('account/index', {
title: 'Account Information',
user: user
});
});
},
// Account View
<p>Email: {{user.email}}</p>
<p>Password: {{user.password}}</p>
Check this out if you want to deal with password encryption: http://node-machine.org/machinepack-passwords
And this if you want to deal with the strength tests (when the user sets the password): https://www.npmjs.com/package/owasp-password-strength-test
This is as passport seems overkill if you're only doing local authentication?

Waterlocks authentication from server side form

I am having a problem with waterlock-local-auth. Basically I've been playing around with waterlock all day trying to figure out how to create a new user (with hashed password and all), and also how to authenticate the user from a form on a server side sails.js view. But have been completely unsuccessful. Below is the code in my LoginController that my login form is posting to. Any help will be greatly appreciated. Thanks!
module.exports = {
login: function(req, res) {
var isAuthenticated = function(){...} <-- Authenticated by waterlocks
if(isAuthenticated) {
res.view('home');
}
else {
res.view('login', {errorMessage: "Invalid username or password"});
}
}
};
Ok, so basically I went with the solution posted here (Sails.js Waterlock /auth/register causes error 500). ;0)
module.exports = require('waterlock').waterlocked({
// Endpoint for registering new users. Taken from: https://stackoverflow.com/questions/29944905/sails-js-waterlock-auth-register-causes-error-500/29949255#29949255
register: function (req, res) {
var params = req.params.all(),
def = waterlock.Auth.definition,
criteria = {},
scopeKey = def.email !== undefined ? 'email' : 'username'; // Determines if the credentials are using username or emailaddess.
var attr = { password: params.password }
attr[scopeKey] = params[scopeKey];
criteria[scopeKey] = attr[scopeKey];
waterlock.engine.findAuth(criteria, function (err, user) {
if (user)
return res.badRequest("User already exists");
else
waterlock.engine.findOrCreateAuth(criteria, attr, function (err, user) {
if (err)
return res.badRequest(err);
delete user.password;
return res.ok(user);
});
});
}
});

sails.js + passport.js : managing sessions

I am trying to implement a facebook connection in sails using passport. Therefore, I've created a passport.js file in my services folder, the code is given below. It looks like the login is done successfully, however the user serialization doesn't seem to work as the console.log that I put in it never appears in the console and I cannot access the user id trhough req.user once the user is supposed to be logged in. Did anyone managed to get passport working with sails?
var passport = require('passport')
, FacebookStrategy = require('passport-facebook').Strategy,
bcrypt = require('bcrypt');
// helper functions
function findById(id, fn) {
User.findOne(id).done( function(err, user){
if (err){
return fn(null, null);
}else{
return fn(null, user);
}
});
}
function findByUsername(u, fn) {
User.findOne({
username: u
}).done(function(err, user) {
// Error handling
if (err) {
return fn(null, null);
// The User was found successfully!
}else{
return fn(null, user);
}
});
}
// Passport session setup.
// To support persistent login sessions, Passport needs to be able to
// serialize users into and deserialize users out of the session. Typically,
// this will be as simple as storing the user ID when serializing, and finding
// the user by ID when deserializing.
passport.serializeUser(function(user, done) {
console.log("utilisateur serilizé!");
done(null, user.uid);
});
passport.deserializeUser(function(id, done) {
//console.log("coucou");
findById(id, function (err, user) {
done(err, user);
});
});
// Use the LocalStrategy within Passport.
// Strategies in passport require a `verify` function, which accept
// credentials (in this case, a username and password), and invoke a callback
// with a user object.
// using https://gist.github.com/theangryangel/5060446
// as an example
passport.use(new FacebookStrategy({
clientID: 'XXX',
clientSecret: 'XXX',
callbackURL: "http://localhost:1337/callback"
},
function(accessToken, refreshToken, profile, done) {
User.findOne({uid: profile.id}, function(err, user) {
if (err) { return done(err); }
if (user) {
//console.log('momo');
User.update({uid : user.uid},{token : accessToken},function(){done(null, user);});
} else {
console.log(profile);
var user_data = {
token : accessToken
, provider: profile.provider
, alias: profile.username
, uid: profile.id
, created: new Date().getTime()
, name: {
first: profile.name.givenName
, last: profile.name.familyName
}
, alerts: {
email: true
, mobile: false
, features: true
}
};
console.log(user_data);
User.create(user_data).done(function(err, user) {
console.log(err);
if(err) { console.log("err");throw err; }
done(null, user);
});
}
});
}
));
While I do not have a direct answer for you, this was extremely useful to when getting it to work with GitHub OAuth: https://github.com/stefanbuck/sails-social-auth-example/blob/master/config/middleware.js
This is an entire, recent, Sails.js application implementing passport so it might be of use to you to side-by-side the two in the debugger and find out what is going on.
Check out this easy and full implementation for sails.js with passport.js supporting both Email, Twitter and Facebook.
https://github.com/bmustata/sails-auth-super-template