Does the passport.js support ajax? - node.js

I want to make ajax login with the passport.js. I have the usual code for setting the passport.js:
//route
app.post('/api/auth/login', passport.authenticate('local-login', {
successRedirect: '/',
failureRedirect: '/login'
}));
//config strategy
passport.use('local-login', new LocalStrategy({
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true
}, loginUser));
var loginUser = function(req, email, password, done) {
UserRepo.getOne({
'local.email': email
}).done(function(user) {
if (!user || !user.validPassword(password)) {
return done(null, false, {
message: 'user or password is incorrect'
});
}
return done(null, user);
},
function(err) {
return done(err);
});
};
This is my react component:
var Login = React.createClass({
//...
handleSubmit: function (e) {
e.preventDefault();
var email = this.state.email.trim();
var password = this.state.password.trim();
var data = {
email: email,
password: password
};
api.auth.login(data, function (result) {
console.log(result);
});
},
render: function () {
return (
<form className="login-form" onSubmit={this.handleSubmit}>
<section>
<label>email</label>
<input name="email" type="text" />
<label>password</label>
<input name="password" type="password" />
</section>
<section>
<input type="submit" value="send"/>
</section>
</form>
);
}
//...
})
But, it doesn't work, because redirects (successRedirect and failureRedirect) do their work. If I delete failureRedirect I get 401 status. I understand that my code for passport for server side rendering and page refresh, but I cannot find any documentation for ajax login.

You can use a custom callback to return JSON data.
app.post('/api/auth/login', function(req, res, next) {
passport.authenticate('local-login', function(error, user, info) {
if(error) {
return res.status(500).json(error);
}
if(!user) {
return res.status(401).json(info.message);
}
res.json(user);
})(req, res, next);
});

Related

Login form not redirecting to home page - even though login credentials appear to match those in database

I've got a login-form that is supposed to redirect to the homepage of my React app, if the user's login detail match those already in the MongoDB database (I've got a seperate sign-up form that is working fine).
The issue is, when I'm testing out my login-form, and purposefully typing in credentials that don't match what's in my database, I get this error :
Login.js:44 POST http://localhost:3001/login 401 (Unauthorized)
However, when I type in credentials that do exist - that error no longer appears, but I get the console.log error I've set up - ("login failed")
Why am I getting this error, instead of the app redirecting to the homepage?
It's clearly able to connect to the database and check if the login details stored in there match the user's input.
What else could be causing this issue?
I've also tried altering the url endpoints, but no luck.
This is the login form code
import React from "react";
import Input from "./Input";
import { useForm } from "react-hook-form";
import { yupResolver } from "#hookform/resolvers/yup";
import * as yup from "yup";
import {
useLocation,
useNavigate,
useParams
} from "react-router-dom";
function withRouter(Component) {
function ComponentWithRouterProp(props) {
let location = useLocation();
let navigate = useNavigate();
let params = useParams();
return (
<Component
{...props}
router={{ location, navigate, params }}
/>
);
}
return ComponentWithRouterProp;
}
const schema = yup.object({
username: yup.string().required("Username is a required field"),
password: yup.string().min(6, "Password must be at least 6 characters"),
});
function Login(props) {
const {
handleSubmit,
register,
formState: { errors },
} = useForm({
resolver: yupResolver(schema),
});
const formSubmit = (data) => {
fetch("http://localhost:3001/login", {
method: "POST",
body: JSON.stringify({username: data.username, password: data.password}),
headers: { "Content-Type": "application/json" },
})
.then((response) => {
if (response.status === 200 && props.history) {
props.history.push("/");
} else {
console.log("login failed");
}
})
.catch((error) => {
console.error("Error:", error);
});
console.log(data);
};
return (
<div className="sign-up">
<h1>Log in</h1>
<p>Lorem ipsum dolor, sit amet consectetur</p>
<form onSubmit={handleSubmit(formSubmit)}>
<Input
id="username"
label="Username"
type="text"
placeholder="Enter Username"
register={{ ...register("username") }}
errorMessage={errors.username?.message}
/>
<Input
id="password"
label="Password"
type="password"
placeholder="Enter Password"
register={{ ...register("password") }}
errorMessage={errors.password?.message}
/>
<button>Log in</button>
</form>
<button className="button-link" onClick={() => props.onFormSwitch("signup")}>
Don't have an account? Register here.
</button>
</div>
);
}
export default withRouter(Login);
And this is the server-side
const express = require("express");
const app = express();
const cors = require("cors");
const mongoose = require("mongoose");
const session = require("express-session");
const passport = require("passport");
const Signup = require("./db/dbModel");
const LocalStrategy = require("passport-local").Strategy;
app.use(cors());
app.use(express.json());
mongoose.connect("..removed for StackOverflow Question");
app.use("/", require("./routes/signupRoute"));
app.use(session({ secret: "secret", resave: true, saveUninitialized: true }));
app.use(passport.initialize());
app.use(passport.session());
passport.use(
'local',
new LocalStrategy(function (username, password, done) {
Signup.findOne({ username: username }, function (err, user) {
if (err) {
return done(err);
}
if (!user) {
return done(null, false);
}
if (user.password != password) {
return done(null, false);
}
return done(null, user);
});
})
);
passport.serializeUser((user, done) => {
done(null, user.id);
});
passport.deserializeUser((id, done) => {
Signup.findById(id, (err, user) => {
done(err, user);
});
});
app.post("/login", (req, res, next) => { // test tomorrow
passport.authenticate("local", (err, user, info) => {
if (err) {
return next(err);
}
if (!user) {
return res.status(401).json({ message: "Incorrect username or password" });
}
req.logIn(user, (err) => {
if (err) {
return next(err);
}
return res.status(200).json({ message: "Successfully logged in" });
});
})(req, res, next);
});
/*
app.post("/login", passport.authenticate("local", {
successRedirect: "/",
failureRedirect: "/login",
failureFlash: true
}));
*/
app.listen(3001, function () {
console.log("express server is running on port 3001");
});
I've changed the function in the login form to this.
fetch("http://localhost:3001/login", {
method: "POST",
body: JSON.stringify({ username: data.username, password: data.password }),
headers: { "Content-Type": "application/json" },
})
.then((response) => {
if (response.status === 200) {
props.router.navigate("/");
} else {
console.log("login failed");
}
})
.catch((error) => {
console.error("Error:", error);
});
console.log(data);
};
For some reason, the props.history.push was causing the error - so it's been altered to props.router.navigate

Problem with logIn page With authentcation

When i sign up a new mail it's redirect to logIn page, in router.post('/login') I added successRedirect: '/' and
failureRedirect: '/test' but it in anyway it redirect to /test even if it's success!
index.js
var express = require('express');
var router = express.Router();
const users = require('../model/db');
const { check, validationResult } = require('express-validator');
const passport = require('passport');
//GET login
router.get('/login', (req, res, next)=>{res.render('login')});
router.post('/login', passport.authenticate('local', {
successRedirect: '/',
failureRedirect: '/test',
failureFlash: true,
}));
router.get('/signup', function (req, res, next){
const msg = req.flash('error')
res.render('signup');
});
router.post('/signup', [
check('password').isLength({ min: 5 }).withMessage('Please enter Password with more than 5 letters!'),
check('password').not().isEmpty().withMessage('Please fill password fie'),
check('repassword').custom((val, {req})=>{
if(val !== req.body.password){
throw new Error('Password is not equal to confirm password');
}
return true;
})
],
function (req, res, next){
const newUser = new users({
email : req.body.email,
password : new users().hashPassword(req.body.password)
});
users.findOne({email : req.body.email}, (err, doc)=>{
if(err){
console.log('ERR while getting username =>' + err);
return ;
}
if(doc){
res.send('this email is already registered before!');
return ;
}
newUser.save((err, doc)=>{
if(err){
console.log('err' + err)
}else{
res.redirect('/login')
}
});
});
// Finds the validation errors in this request and wraps them in an object with handy functions
const errors = validationResult(req);
if (!errors.isEmpty()) {
var validationMessage = [];
for(var i = 0; i<errors.errors.length; i++){
validationMessage.push(errors.errors[i].msg);
}
req.flash('error', validationMessage)
res.redirect('signup')
}
});
module.exports = router;
According to these two lines of code,
router.post('/login', passport.authenticate('local', {
successRedirect: '/',
failureRedirect: '/test',
failureFlash: true,
}));
It should Redirect me to /test if it failed and To / if it succeeded
login.hbs
<div class="container">
<div class="s-f">
<div class="card">
<div class="card-body">
<div>
<h6>Log in | Coursatak</h6>
</div>
<form action="/login" method="post">
<div class="form-group">
<label for="email">Email</label>
<input type="text" name="email" id="email" class="form-control">
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" name="password" id="password" class="form-control">
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary"> Sign Up</button>
</div>
<div>
<p class='if'></span>Sign Up, <span>If you already registered before.</p>
</div>
</form>
</div>
</div>
</div>
</div>
config/conficuration.js
const LocalStrategy = require('passport-local').Strategy;
const bcrypt = require('bcryptjs');
const User = require('../model/students');
module.exports = function(passport) {
passport.use(
new LocalStrategy('local', { usernameField: 'email' }, (email, password, done) => {
// Match user
User.findOne({email: email}).then(user => {
if (!user) {
return done(null, false, { message: 'That email is not registered' });
}
bcrypt.compare(password, student.password, (err, isMatch) => {
if (err) throw err;
if (isMatch) {
return done(null, user);
} else {
return done(null, false, { message: 'Password incorrect' });
}
});
});
})
);
};
Schema
const mongoose = require('mongoose');
const bcrypt = require('bcrypt-nodejs');
const stDB = mongoose.Schema({
email : {
type: String,
required: true
},
password : {
type: String,
required: true
}
});
stDB.methods.hashPassword = function(password){
return bcrypt.hashSync(password, bcrypt.genSaltSync(10));
}
stDB.methods.comparePasswords = (password, hash) => {
return bcrypt.compareSync(password,hash)
}
module.exports = mongoose.model('db', stDB);
Node Version: 10.16.1
Express Version: 4.16.1
I dont know what you have at your app.js file , however paste the following code snippet at your app.js file and dismiss the config/conficuration.js
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
//after your modules
//passport login local login system
passport.serializeUser(function (user, done) {
//console.log('in serializeUser method user:' + user);
done(null, user.id);
});
passport.deserializeUser(function (id, done) {
//console.log('in deserializeUser method id:' + id);
User.findById(id, function (err, user) {
done(err, user);
});
});
passport.use(
new LocalStrategy('local', { usernameField: 'email' }, (email, password, done) => {
// Match user
User.findOne({email: email}).then(user => {
if (!user) {
return done(null, false, { message: 'That email is not registered' });
}
bcrypt.compare(password, student.password, (err, isMatch) => {
if (err) throw err;
if (isMatch) {
return done(null, user);
} else {
return done(null, false, { message: 'Password incorrect' });
}
});
});
})
);
//after you initialize your sessions
app.use(passport.initialize());
app.use(passport.session());

node js passport js username field error and password field error doesn't display when the form submiting

I'm trying to create a sign up page all the functionalities are working fine. But, if username field and password field submitted empty, the page redirect to the same page but it doesn't show where the error coming from. Can you please anyone help me to fix this. Why is this hapening? I'll put the code below i created.
thank you
passport.use('local-signup', new LocalStrategy({
usernameField : 'username',
passwordField : 'password',
passReqToCallback : true
},
function(req, username, password, done) {
process.nextTick(function() {
if(req.body.first == "" || req.body.last == "" || req.body.email == ""){
return done(null, false, req.flash('signupMessage', 'Fields must be required'));
}
if(username == ""){
return done(null, false, req.flash('signupMessage', 'Username must be required'));
}
User.findOne({'email': req.body.email}, function(err, user1) {
if (err) return done(err);
User.findOne({'local.usernreqame': username}, function(err, user2){
if (err) return done(err);
if (user1) {
return done(null, false, req.flash('signupMessage', 'That email is already exist.'));
}
if (user2) {
return done(null, false, req.flash('signupMessage', 'That username is already exist.'));
}
else {
var newUser = new User();
newUser.local.username = username;
newUser.local.password = newUser.generateHash(password);
newUser.usertype = "592000f0161d63ac334358d3";
newUser.first = req.body.first;
newUser.last = req.body.last;
newUser.email = req.body.email;
newUser.save(function(err) {
if (err)
throw err;
return done(null, newUser);
});
}
})
});
});
}));
all the routes i created for this app
module.exports = function(app, passport){
app.get('/home', function(req, res){
res.render('index');
});
app.get('/sign-up', function(req, res){
res.render('signup', { message: req.flash('signupMessage') });
});
app.post('/sign-up', passport.authenticate('local-signup', {
successRedirect : '/profile',
failureRedirect : '/sign-up',
failureFlash : true
}));
app.get('/profile', isLoggedIn, function(req, res) {
res.render('users/profile', {
user : req.user
});
});
app.get('*', function(req, res){
res.render('404');
});
}
function isLoggedIn(req, res, next) {
if (req.isAuthenticated()) return next();
res.redirect('/');
}
Here you can see the template file also
<form method="post" action="/sign-up">
<input type="text" name="first" placeholder="Enter Firstname"><br/>
<input type="text" name="last" placeholder="Enter Lastname" ><br/>
<input type="email" name="email" placeholder="Enter E-mail" ><br/>
<input type="text" name="username" placeholder="Enter username"><br/>
<input type="password" name="password" placeholder="Enter Password"><br/>
<input type="submit" value="Sign Up">
</form>
<% if (message.length > 0) { %>
<div><%=message%></div>
<% } %>
I'm not sure what is going on. But lets try something.
I believe, your flash message its been erased.
Look:
Here is your code:
app.post('/sign-up', passport.authenticate('local-signup', {
successRedirect : '/profile',
failureRedirect : '/sign-up',
failureFlash : true // you are passing this parameter
}));
Look at the passport code:
...
if (options.failureFlash) {
var flash = options.failureFlash; // flash here is 'true'
if (typeof flash == 'string') { // not 'string'
flash = { type: 'error', message: flash };
}
flash.type = flash.type || 'error'; // 'flash.type' is undefined, 'flash' is 'true', so not is gonna happen
var type = flash.type || challenge.type || 'error'; // 'type' now is 'error'
msg = flash.message || challenge.message || challenge;
if (typeof msg == 'string') {
req.flash(type, msg); // value assigned: ['error', 'yourmessage']
}
}
...
So, I believe, if your call:
app.get('/sign-up', function(req, res){
res.render('signup', { message: req.flash('error') }); // changing 'signupMessage' to error, it would works.
});
Or, you can change the failureFlash parameter to and object:
app.post('/sign-up', passport.authenticate('local-signup', {
successRedirect : '/profile',
failureRedirect : '/sign-up',
failureFlash : { type: 'signupMessage' }
}));
and keep this intact:
res.render('signup', { message: req.flash('signupMessage') });
Let me know if it works.
Cheers!

Nodejs Passport Local Strategy always failing

I am new to the whole user authentication things with node and I am trying to learn how to use Passport's LocalStrategy to add users to a Mongo database.
I'm trying to follow a particular tutorial and for some reason things aren't going to plan. Whenever I submit the registration form the strategy always fails (is redirected to the failure page). I have a feeling it is something to do with the body of he request not being passed (since the log I placed in where the strategy is declared is not run). However it seems like the current infrastructure makes it hard to refactor. Can this code be refactored such that the request can be parsed manually (e.g. request.body.* name *) before handing it over to passport?
Unless the issue is something completely different, in which case I have no idea...
index.js:
// Use middleware
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));
app.use(expressSession({ secret: 'whatkindofgamedoyouthinkthisishey',
cookie: {maxAge:null},
resave: false,
saveUninitialized: false}));
require("./config/passport")(passport);
app.use(passport.initialize());
app.use(passport.session());
app.use(flash());
// Obtain application modules
var userModels = require("./schemas/user")(mongoose);
var loginPage = require("./routes/login")(passport, userModels);
// Initialize Routes
app.use("/", loginPage);
passport.js (should come up with a better name):
var LocalStrategy = require("passport-local").Strategy,
User = require("../schemas/user");
module.exports = function (passport) {
passport.serializeUser(function (user, done) {
done(null, user.id);
});
passport.deserializeUser(function (id, done) {
User.findById(id, function (err, user) {
done(err, user);
});
});
passport.use("local-signup", new LocalStrategy({
email: "email",
password: "password",
passReqToCallback: true
},
function (request, email, password, done) {
console.log("message sent to sign up"); // log not running
process.nextTick(function () {
User.findOne({email: email}, function (err, user) {
if (err) {
return done(err);
}
if (user) {
return done(null, false, request.flash("signupMessage", "That email is already taken"));
} else {
var newUser = new User();
newUser.email = email;
newUser.password = password;
newUser.save(function (err) {
if (err) {
throw err;
} else {
return done(null, newUser);
}
});
}
});
});
}
));
};
login.js (router being exported)
router.post("/register", passport.authenticate("local-signup", {
successRedirect: "/loggedIn",
failureRedirect: "/connectFailed",
failureFlash: false
}));
Html form:
<form class="form-signin" action="/register" method="POST">
<div class="logoContainer">
<img src="images/LogoWithoutText.png" class="image image-responsive" id="loginImage">
</div>
<h2 class="form-signin-heading">Please sign in</h2>
<label for="inputEmail" class="sr-only">Email address</label>
<input type="email" id="inputEmail" class="form-control" name="email" placeholder="Email address" required autofocus>
<label for="inputPassword" class="sr-only">Password</label>
<input type="password" id="inputPassword" class="form-control" name="password" placeholder="Password" required>
<div class="checkbox">
<label>
<input type="checkbox" value="remember-me"> Remember me
</label>
</div>
<button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
</form>
From what I can read in the passportjs.org/docs, it looks like passport.use has the following signature:
var passport = require('passport'),
LocalStrategy = require('passport-local').Strategy;
passport.use(new LocalStrategy({
email: "email",
password: "password",
passReqToCallback: true
},
function(req, email, 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);
});
}
));
So, try changing the way passport.use is written and try you luck.

Strategy not being called by authenticate

I'm new to node and am trying to get a simple local login up and running using passport.js. It looks to me that the strategy that I have set up is not being run when the authenticate function is called.
module.exports = function(app) {
var mongo = require('./mongoose-db.js');
var brands = require('./app/brands.js');
// brand admin page //
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
passport.use(new LocalStrategy( {//this is a strategy but why is it not being run?
usernameField: 'admin_username',
passwordField: 'admin_password'
},
function(username, password, done) {console.log('here 7');
process.nextTick(function () {
console.log('here 8');
// Find the user by username. If there is no user with the given
// username, or the password is not correct, set the user to `false` to
// indicate failure and set a flash message. Otherwise, return the
// authenticated `user`.
mongo.findAdminByUsername(username, function(err, user) { console.log('here 9');
if (err) { return done(err); }
if (!user) { return done(null, false, { message: 'Unknown Admin ' + username }); }
if (user.password != password) { return done(null, false, { message: 'Invalid password' }); }
return done(null, user);
})
});
}
));
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
findById(id, function (err, user) {
done(err, user);
});
});
app.get('/brand_admin', function(req, res){
console.log('here 1');
res.render('admin_login', { user: req.user, message: req.session.messages });
});
app.post('/brand_admin', function(req, res, next) {
console.log('USER: ');
console.log(req.user);
res.send('ok');
passport.authenticate('local', function(err, user, info) {
console.log(user);
if (err) { return next(err) }
if (!user) {console.log(req.session.messages);
req.session.messages = [info.message];
console.log('here 4');
console.log(req.session.messages);
return res.redirect('/brand_admin')
}
console.log('here 5');
req.logIn(user, function(err) {
if (err) { return next(err); console.log('here 6');}
return res.redirect('/');
});
})(req, res, next);
});
the ejs is:
<body>
<head> Admin Login </head>
<form id="brand_edit" action="/brand_admin" method="post" >
<fieldset>
Login: <input type="text" name="admin_login" > <br>
Password: <input type="password" name="admin_password" >
<input type="submit" name="action" value="Login"/>
</fieldset>
Forgotten your password?
</form>
</body>
The output i'm getting from form submission is:
Steve
agrgegerfe
{ id: 1,
username: 'Steve',
password: 'agrgegerfe',
email: 'bob#example.com' }
false
undefined
here 4
[ 'Missing credentials' ]
here 1
The strategy is not running but I can't figure out why? Can anyone help? One thing that could be affecting this is that I am using passport for Facebook login in another section of the application..
I think you are not calling passport properly. It needs to be in the route chain. Try this:
// configure passport with the correct fields
passport.use(new LocalStrategy({
usernameField: 'admin_login',
passwordField: 'admin_password'
}, function (username, password, done) {
...
});
app.post('/brand_admin', passport.authenticate('local'), function (req, res) {
// req.user is now defined
console.log(req.user);
res.send('ok');
});

Resources