I want to understand how does LocalStrategy work.
Here is a part of my server file:
var passport = require('passport');
var express = require('express');
/* other initializations */
var app = express();
passport.use = new LocalStrategy(
function(email, password, done) {
module.exports.findByUsername(email, function(err, user){
if (err) throw err;
if(!user) {
done(null, false, { message: 'Incorrect username.' });
}
else if(user.password != password) {
done(null, false, { message: 'Incorrect password.' });
}
else {
return done(null, user);
}
});
}
)
app.post("/login"
, passport.authenticate('local',{
successRedirect : "/",
failureRedirect : "/login",
}) ,
function(){
console.log("post /login");
}
);
Now, from a client browser, I'm sending a http post request to http://localhost:8000/login . If authentication is success then user will be redirected to the root page "/" and if failure, user will be redirected to login page again.
The question is, when we are defining a new LocalStrategy, I define a function(email,password, done){...}. However, when I'm calling this function at app.post("/login", ...){...} how do I pass the email and password parameters?
passport assumes by default that you POST a form with input name='username' input name='password'. override it as described in passport docs:
passport.use(new LocalStrategy({
usernameField: 'email',
passwordField: 'password'
},
function(email, password, done) {
// ...
}
));
Related
When I send a request via axios to my Express app, req.isAuthenticated() is always false and req.user does not exist, even after logging in. But when I send a request via Postman to the app, it works. It seems that deserializeUser() is never called so the req.session.passport field is never populated.
I've tried all of the suggestions online, any help is appreciated.
External request:
async tweet(content) {
try {
await axios.post(this.url + '/tweets/new', {
content: content,
withCredentials: true,
headers: {
'Content-Type': 'application/json',
}
});
} catch (err) {
console.log(err);
}
}
index.js
const passport = require('passport');
const cookieParser = require('cookie-parser');
const session = require('express-session');
const RedisStore = require('connect-redis')(session)
const redisCookie = require('heroku-redis-client');
require('./config/passport')(passport);
// required for passport
app.use(cookieParser());
app.use(session({
// secret: process.env.SECRET || 'enteryoursecrethere',
secret: 'enteryoursecrethere',
cookie: { maxAge: 3600000 },
resave: true,
store: new RedisStore({client: redisCookie.createClient()}),
saveUninitialized: true
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(function(req, res, next) {
res.header('Access-Control-Allow-Credentials', true);
res.header('Access-Control-Allow-Origin', req.headers.origin);
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.header('Access-Control-Allow-Headers', 'X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept');
next();
});
router.js
var tweets = require('../controllers/tweets');
var router = express.Router();
var isLoggedIn = require('../middleware/isLoggedIn');
router.post('/tweets/new', isLoggedIn, tweets.tweet);
middleware/isLoggedIn.js
module.exports = (req, res, next) => {
// If user is authenticated in the session, carry on.
if (req.isAuthenticated()) {
next();
return
}
// If they aren't redirect them to the home page.
res.redirect('/');
}
passport.js
const LocalStrategy = require('passport-local').Strategy;
const User = require('../models').User;
const Sequelize = require('sequelize');
module.exports = function(passport) {
// The login request establishes a session maintained in a browser cookie.
// Requests after the login request not contain credentials,
// but rather the unique cookie that identifies the session. The user object
// is constructed to and from the ID in the cookie.
// Converts user to user id.
passport.serializeUser(function(user, done) {
done(null, user.id);
});
// Converts user id to user, stored in req.user.
passport.deserializeUser(function(id, done) {
User.findById(id).then(function(user) {
done(null, user);
}).catch(function(err) {
done(err);
});
});
/* ============Login============ */
passport.use('local-login', new LocalStrategy({
usernameField: 'username',
passwordField: 'password',
passReqToCallback : true // Send entire request for flash message.
}, loginCallback));
passport.use('local-signup', new LocalStrategy({
usernameField: 'username',
passwordField: 'password',
passReqToCallback : true
}, signupCallback));
};
function loginCallback(req, username, password, done) {
if (req.isAuthenticated()) {
return done(null, req.user);
}
// Look up the user by username.
User.findOne({
where: {
username: username
}
}).then(function(user) {
if (!user) {
return done(null, false, req.flash('loginUsernameMessage', 'Wrong username.'));
}
if (!user.validatePassword(password)) {
return done(null, false, req.flash('loginPasswordMessage', 'Wrong password.'));
}
return done(null, user.get());
}).catch(function(err) {
return done(err);
});
}
function signupCallback(req, username, password, done) {
// Asynchronous. User.findOne wont fire unless data is sent back.
process.nextTick(function() {
if (password != req.body.password_confirm) {
return done(null, false, req.flash('signupMessage', 'Passwords don\'t match.'));
}
// Find a user whose email is the same as the forms email.
// We are checking to see if the user trying to login already exists.
User.findOne({
where: {
[Sequelize.Op.or]: [ { username: username }, { email: req.body.email }]
}
}).then(function(user) {
// Check to see if theres already a user with that username or email.
if (user) {
return done(null, false, req.flash('signupMessage', 'That email or username is already taken.'));
}
// Create the user.
var data = {
fname: req.body.fname,
lname: req.body.lname,
username: username,
email: req.body.email,
password: User.generateHash(password)
}
User.create(data).then(function(newUser) {
return done(null, newUser);
}).catch(function(err) {
return done(err);
});
}).catch(function(err) {
return done(err);
});
});
}
It works only with postman because the session is being set at the server(express) but not at the client(axios).
So when you request from axios, It doesn't know if the session is already set at the server. So you need to send the credentials origin along with the request. Modify your request like this:
await axios.post(this.url + '/tweets/new', {
content: content,
withCredentials: true,
headers: {
'Content-Type': 'application/json',
},
credentials: "same-origin"
});
Refer this for more info.
This question is a year old now but I had a heck of a time solving this problem using Node/Express/Passport/React with a local-strategy and express-session.
For me, adding this to the header:
withCredentials:true
wasn't enough. That did trigger a CORS problem but did not actually make axios send the session cookie with the request.
This causes a situation where the login would create a session, but the deserialize function would never be called and req.user would always be empty.
This is what fixed the issue:
In my React component constructor I added this line of code:
axios.defaults.withCredentials = true;
That fixed it.
In fact, it fixed it so well that I was able to remove all other headers in Axios. Everything just started working.
Figuring this out took me a solid two hours today, I hope this answer helps someone.
I am trying to authenticate user ,when i try to login user after successful verification using req.logIn but it doesn't worked
router.post('/login', function(req, res, next) {
passport.authenticate('login',function (cb,data) {
//user verfication success
if(data){
req.logIn({user:"shamon"},function(err,result){
console.log("result",result,err)
res.send('login success');
});
}
})(req,res,next);
});
this console.log("result",result,err) gives me undefined,undefined
when i log req.user after logged i got undefined error
UPDATE
var LocalStrategy = require('passport-local').Strategy
module.exports = function(passport){
passport.use('local',new LocalStrategy({
usernameField: 'email',
passwordField: 'password'
},function (username,password,done) {
console.log('inside passport');
return done(null,true);
}));
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(user, done) {
done(null,user);
});
}
Just grab my implementation of password local-strategy. This is working for sure, but you will need to slightly modify it:
The strategy:
// Serialize
passport.serializeUser(function (user, done) {
done(null, user.id);
});
// Deserialize
passport.deserializeUser(function (id, done) {
User.findById(id, function (err, user) {
done(err, user);
});
});
passport.use('local-login', new LocalStrategy({
// by default, local strategy uses username and password, we will override with email
usernameField : 'email',
passwordField : 'password',
passReqToCallback : true // allows us to pass back the entire request to the callback
},
function(req, email, password, done) { // callback with email and password from our form
// find a user whose email is the same as the forms email
// we are checking to see if the user trying to login already exists
User.findOne({ 'local.email' : email }, function(err, user) {
// if there are any errors, return the error before anything else
if (err)
return done(err);
// if no user is found, return the message
if (!user)
return done(null, false, req.flash('loginError', 'No such user found.')); // req.flash is the way to set flashdata using connect-flash
// if the user is found but the password is wrong
if (!user.validPassword(password))
return done(null, false, req.flash('loginError', 'Oops! Wrong password.')); // create the loginMessage and save it to session as flashd
// all is well, return successful user
return done(null, user);
});
}
));
And the route:
router.post('/login', function(req, res, next) {
passport.authenticate('local-login', function(err, user, info) {
if (err) {
return next(err);
}
if (!user) {
return res.send({alert: req.flash('loginError')});
}
req.logIn(user, function(err) {
if (err) {
return next(err);
}
return res.send({redirect: '/'});
});
})(req, res, next);
});
I have a new Sails.js project using Passport.js to authenticate users. I have the basic authentication working (meaning a user can sign up and successfully log in), but would like to display the appropriate error message in the login view if they don't enter the correct credentials. I can't figure out how to print any error messages in the view.
Here's my setup. I have config/passport.js, which contains the following:
var passport = require('passport'),
LocalStrategy = require('passport-local').Strategy,
bcrypt = require('bcrypt');
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findOne({ id: id } , function (err, user) {
done(err, user);
});
});
passport.use(new LocalStrategy({
usernameField: 'email',
passwordField: 'password',
passReqToCallback : true
},
function(req, email, password, done) {
User.findOne({ email: email }, function (err, user) {
if (err) { return done(err); }
if (!user) {
return done(null, false, { message: 'Please enter a valid email address.' });
}
if (!req.body.username) {
return done(null, false, { message: 'Please enter your username.' });
}
bcrypt.compare(password, user.password, function (err, res) {
if (!res) {
return done(null, false, {
message: 'Invalid Password'
});
}
var returnUser = {
username: user.username,
email: user.email,
createdAt: user.createdAt,
id: user.id
};
return done(null, returnUser, {
message: 'Logged In Successfully'
});
});
});
}
));
Then I have api/controllers/AuthController.js, which contains the following:
var passport = require('passport');
module.exports = {
_config: {
actions: false,
shortcuts: false,
rest: false
},
login: passport.authenticate('local', { successRedirect: '/',
failureRedirect: '/login',
failureFlash: true
}),
logout: function(req, res) {
req.logout();
res.redirect('/login');
}
};
Again, this is working properly if the user fills in their correct credentials. I'm using Handlebars as my templating engine, and would like to display the error message in the login view like so:
<div class="alert">{{ message }}</div>
So far I've tried {{ failureMessage }} {{ message }} {{ req.flash.failureMessage }} {{ req.flash.err }} {{ req.flash.message }} to no avail. So all this to say, how do I display the appropriate error message in my view? And better yet, how would I highlight the errant field in my view?
At first glance, looks like sails is using Express 3.0. Per the Passport docs (http://passportjs.org/docs), you will need to explicitly add middleware to support flash (they recommend https://github.com/jaredhanson/connect-flash).
Not a passport expert here, but according to this all you need to do is to re-render the login view with req.flash('error'):
res.render("login", {error: req.flash("error")});
And then in the handlebars template, display the error:
{{ error }}
Don't know if this might help you, Brad.
https://stackoverflow.com/a/25151855/3499069
I think in your serializeUser call you need it to be user[0].id, but I've moved away from Passport recently, so I could be wrong.
In my opinion you didn't include express flashes ?
var flash = require('connect-flash');
app.use(flash());
in your router add
req.flash(type, message);
and using
var t = req.flash(type);
res.render(view, {flashMessage: t});
Pls.. i am completely new Guy to Node js
i try this for my learning purpose
i have created server.js file
var http=require('http');
var flash = require('connect-flash');
var express=require('express');
var fs=require('fs');
var mysql=require('mysql');
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var session = require('express-session')
var app=express();
var server=http.createServer(app);
var User=[];
server.listen(8000);
var connection=mysql.createConnection({
host:'****',
user:'****',
password:'****'
});
connection.connect(function(Error,Res){
if(!Error)
{
connection.query('USE USERS');
connection.query( "select *from USERS", function(err, rows){
if(err) {
throw err;
}else{
User.push(rows);
}
});
}
});
app.get('/', function(request,response){
fs.readFile('login.html', function(Error,Res){
if(!Error)
{
response.writeHead(200,{'content-type':'text/html'});
response.write(Res);
}
});
});
app.use(session({secret: '<mysecret>',saveUninitialized: true,resave: true}));
app.use(passport.initialize());
app.use(passport.session());
app.use(flash());
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(id, done) {
done(err, user);
});
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);
});
}
));
app.post('/login',
passport.authenticate('local', { successRedirect: '/',
failureRedirect: '/login',
failureFlash: true })
);
i dont know how to validate username and password using passport in node js but anyhow i tried this above
My login.html looks like
<html>
<head>
<title>login</title>
<script src="assets/js/Angular.min.js"></script>
<style>
</style>
</head>
<body>
<form action="/login" method="post">
<div>
<label>Username:</label>
<input type="text" name="username"/>
</div>
<div>
<label>Password:</label>
<input type="password" name="password"/>
</div>
<div>
<input type="submit" value="Log In"/>
</div>
</form>
</body>
</html>
And my sql User Table has data like
username:Admin,
password:Admin
i run the server.js file using command prompt like node server.js after running i goto the browser and type http://localhost:8000 i got the following screen
And then i type username and password is Admin , Admin when i submit i get Cannot GET /login i think it means username password is wrong because i specified failureRedirect: '/login'. but i provide correct username and password why i got Cannot GET /login instead of Cannot GET /. where can i do the mistake??? pls Help
What is this, validPassword() method? Do you have it defined?
My bet is that you do not.
So, try to add more logging to the Passport middleware and track the console. Something like bellow, I suspect you'll get no error log at all, because the app will fail before validPassword():
passport.use(new LocalStrategy(
function(username, password, done) {
User.findOne({ username: username }, function(err, user) {
if (err) {
console.log('MySQL Error: ', err);
return done(err);
}
if (!user) {
console.log('Incorrect username error.');
return done(null, false, { message: 'Incorrect username.' });
}
console.lg('Trying user.validPassword() call');
if (!user.validPassword(password)) {
console.log('Incorrect password.');
return done(null, false, { message: 'Incorrect password.' });
}
console.log('All correct, user found: ', user);
return done(null, user);
});
}
));
I am logging in a user with passport local:
// route to log in
app.post('/login', passport.authenticate('local-login'), function(req, res) {
res.send(req.user);
});
This will only work in the case of success, i.e. when req.user is defined. How do I check for an error in the request? Essentially I want to take the error that was pulled from my passport authentication code in the done() call and send that back to the client to display the proper error message:
passport.use('local-login', new LocalStrategy({
username : 'email',
password : 'password',
passReqToCallback : true
},
function(req, email, password, done) {
console.log('logging in user: ' + email);
// find a user whose email is the same as the forms email
// we are checking to see if the user trying to login already exists
Landlord.findOne({ 'local.email' : email }, function(err, user) {
if (err) return done(err);
if (!user) return done(null, false, { message: 'Incorrect username.' });
if (!user.validPassword(password)) {
console.log('wrong pw');
return done(null, false, { message: 'Incorrect password.' });
}
return done(null, user);
});
}));