I don't think React has anything to do with this, but just in case, that's what I'm working in. I'm receiving the error XHR failed loading: POST when submitting an AJAX request to /login. I am trying to create a login route using Passport JS, and I know that the route is receiving the data because it will console.log as { email: 'myemail', password: 'mypassword' } and typeof returns object.
this.handleLoginSubmit = () => {
let xml = new XMLHttpRequest();
xml.open("POST", "/login", true);
xml.setRequestHeader('Content-Type', 'application/json; charset=utf-8');
xml.send(JSON.stringify({email: this.state.email, password: this.state.password}));
xml.onreadystatechange = () => {
if(xml.readyState === 4) {
console.log('here')
console.log(xml.response);
}
}
}
EDIT Here is the route:
router.post('/login', emailToLowerCase, function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) {
console.log('error')
return next(err);
}
if (!user) {
return console.log('no user!')
}
req.logIn(user, function(err) {
if (err) return next(err);
console.log('logging in')
return res.send(req.user)
});
})(req, res, next);
});
EDIT Here is the form:
<form id='login-form' className="small-form" className='nav-div' onSubmit={props.handleLoginSubmit}>
<div className='nav-div'>
<li className="nav-item">
<input type="email" required name="email" placeholder='Email' className='form-control' value={props.email} onChange={(e) => props.handleEmailChange(e.target.value)}/>
</li>
<li className="nav-item">
<input type="password" required name="password" placeholder='Password' className='form-control' value={props.password} onChange={(e) => props.handlePasswordChange(e.target.value)}/>
</li>
<li className='nav-item'>
<input type='submit' value='Login' />
</li>
</div>
In your code:
if (!user) {
return console.log('no user!')
}
You are not sending any response to UI. Not sure how the next(err) processes the response, but at lease in case user is not found you want to send some error back to the client, like:
if (!user) {
console.log('no user!');
return res.status( 404 ).json({ message: 'User not found' });
}
Related
My function is set to find email brought from /login POST method, but I am failing to declare the variable properly, what is the variable to be inserted into the findOne form on app.get('/data')?
I have:
app.post('/login', function (req, res) {
//console.log(req.body);
const uri = "mongodb+srv://<PRIVATE INFO>.eapnyil.mongodb.net/?retryWrites=true&w=majority";
const client = new MongoClient(uri, { useNewUrlParser: true, useUnifiedTopology: true, serverApi: ServerApiVersion.v1 });
const users = client.db("data").collection("users");
users.findOne({email:req.body.email},function(err,data){
if(data){
if(data.password==req.body.password){
//console.log("Logged In.");
console.log('Email in DB is: ' + data.email);
console.log('Email in form is: ' + req.body.email);
//res.send({"Success":"Success!"});
res.redirect('/data');
}else{
res.send({"Failed with":"Wrong password!"});
}
}else{
res.send({"Try again":"Email not registered!"});
}
});
});
app.get('/data', (req, res) => {
const users = client.db("data").collection("users");
users.findOne({unique_id:req.session.id})((err, result) => {
if (err) return console.log(err)
// renders index.ejs
res.render('pages/data.ejs', {users: result})
})
});
and on the login.ejs file the following:
<p>Login</p>
</div>
<div class="form-group">
<form id="form" method="POST" action="/login">
<input type="text" name="email" placeholder="E-mail" required="" class="form-control"><br/>
<input type="password" name="password" placeholder="Password" required="" class="form-control"><br/>
<input type="submit" value="Login" class="btn btn-success">
</form>
</div>
Not sure why you are redirecting to the /data method when you already have the user to pass to the view.
Try to redirect in /login directly:
app.post('/login', function (req, res) {
//console.log(req.body);
const uri =
'mongodb+srv://<PRIVATE INFO>.eapnyil.mongodb.net/?retryWrites=true&w=majority';
const client = new MongoClient(uri, {
useNewUrlParser: true,
useUnifiedTopology: true,
serverApi: ServerApiVersion.v1,
});
const users = client.db('data').collection('users');
users.findOne({ email: req.body.email }, function (err, data) {
if (data) {
if (data.password === req.body.password) {
res.render('pages/data.ejs', {users: data})
} else {
res.send({ 'Failed with': 'Wrong password!' });
}
} else {
res.send({ 'Try again': 'Email not registered!' });
}
});
});
Also, I suggest you hash the password that you store in the database using libraries like bcrypt.
Storing credentials in plain text is a bad security practice.
app.get('/data', (req, res) => {
const users = client.db("data").collection("users");
users.findOne({unique_id:req.session.id},((err, result) => {
if (err) return console.log(err)
// renders index.ejs
res.render('pages/data.ejs', {users: result})
}))
});
there is a syntax error after {unique_id:req.session.id}, replace ')' for ',' and close ')' correctly
i have a signup and login form,am using ejs to render as view,
i have been given some api endpoint to call, and i have added them to their ejs form action views
when i fill in the user details in my ejs view form i get this response.
i get this when am trying to register a user
{
"status": 200,
"message": "Account created successfully.",
"data": {
"name": "name of user",
"email": "user email",
"password": "$2b$10$0IPgBNCJhjjkasdkGb2y0CviOuxRe/bAfhFqFDqrofMaCz5f0SMtwkgBxIzm"
}
}
i get this when am trying to login a registered user
{
"status": 200,
"message": "Account logged in successfully.",
"data": [
{
"id": 9,
"name": "username",
"email": "useremail#gmail.com",
"password": "$2b$10$v3/zhn0pP6BMaTlfcoaMUuMlaHPGht6v2sf03e6uB3OPIQ3ggdpdi",
"date_created": "2020-02-21T13:15:33.000Z"
}
]
}
i get this when i post the sign in form with the details of an already registered user
{
"status": 400,
"message": "Provided email address already exists, try another",
"data": null
}
i get this when am trying to login with bad credentials
{
"status": 400,
"message": "Incorrect password provided, try again.",
"data": null
}
please my question is how do i have access to this details so i can send them to the client in my ejs view
you can use the npm package request, so you install it with this
npm i request
now you will need one function that you will always call whenever you want to use the api endpoint
var request = require("request");
module.exports = {
// DATA IN { name: 'aaa', email: 'pass#gmail.com', password: '123' } FORMAT
callAPI:function(url, data, callback){
var options = { method: 'POST',
url: url,
headers:
{
'cache-control': 'no-cache',
'content-type': 'application/json'
},
body: data,
json: true };
request(options, function (error, response, result) {
if (error){
return callback(error);
}else{
callback(null, result);
};
});
}
}
and you can use this as an example to call the function
app.post('/signup', function (req, res) {
var data = {
"name": req.body.name,
"email": req.body.email,
"password": req.body.password
}
functions.callAPI("https://api/endpoin/signup", data, function (error, result) {
if (error) {
var response = {
"status": 400,
"message": error,
"data": null
}
res.render('signup', {response: response});
} else {
var response = result;
if (response.status === 400) {
res.render('signup', {response: response});
} else {
res.redirect('/login');
}
}
});
});
First step install axios (npm i axios )
In folder(controllers/authController.js),write this code
require('dotenv').config()
const request = require('request')
const axios = require('axios')
const createErrors = require('http-errors')
module.exports = {
callAPI: function(url, body, callback) {
axios.post(process.env.BASE_URL, body, {
headers: {
'cache-control': 'no-cache',
'Authorization': 'Token token=\"1234455433222222\"',
'Content-Type': 'application/json'
},
})
.then(function(response) {
console.log(response, "istheresponse");
callback(null, response)
})
.catch(function(error) {
if (error.response) {
callback(error.response)
// Request made and server responded
console.log(error.response.data);
console.log(error.response.status);
console.log(error.response.headers);
} else if (error.request) {
callback(error.request)
// The request was made but no response was received
console.log(error.request);
} else {
callback(error.message)
// Something happened in setting up the request that triggered an Error
console.log('Error', error.message);
}
});
}
}
Now,create routes folder (routes/Auth.routes.js)
require('dotenv').config()
const express = require('express')
const router = express.Router()
const axios = require('axios')
const authController = require('../controllers/Auth.controllers')
router.get('/', (req, res, next) => {
res.render('pages/auth/index')
})
router.get('/signin', (req, res, next) => {
res.render('pages/auth/signin', { title: 'website-name | Signin', value: 'Signin' })
})
router.post('/signin/', function(req, res) {
var data = {
"name": req.body.username,
"email": req.body.email,
"password": req.body.password,
}
console.log(data)
authController.callAPI(process.env.BASE_URL, data, function(error, result) {
if (error) {
var response = {
"status": 400,
"message": error,
}
res.render('pages/auth/signin', { response: result });
console.log(error)
} else {
// console.log(error, ' is the error');
// console.log(result, 'is the result')
if (result.data.status == 200) {
res.redirect('/');
} else {
res.render('pages/auth/signin', { response: result, title: 'website-name | Signin', value: 'Signin' });
}
}
});
});
module.exports = router
In (pages/auth/signin.ejs) folder
<form id="signin" name="signin" class="default-form" action="/signin" method="post">
<div class="login-form">
<div class="sign-in-htm">
<div class="group">
<input id="user" type="text" name="name" class="input" placeholder="name">
</div>
<div class="group">
<input id="pass" type="password" name="password" class="input" data-type="password" placeholder="Password">
</div>
<div class="group">
<input id="pass" type="email" name="email" class="input" data-type="email" placeholder="email">
</div>
<div class="group">
<button type="submit" value="submit" class="button">Login</button>
</div>
</form>
I'm trying to search for a user/shop account in my database (mongoDB) and then either refresh the login page but with some errors, or send them to their required page.
So far sending them to their required page works, however I get this error message from my Node console when trying to res.redirect to the /signin page.
throw err;
^
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
Here is the actual code
//Rendering Signin Page
app.get('/signin', function(req, res) {
res.render('signin', {
err: false,
})
});
//Rendering Signin Page with err
app.get('/signin/err', function(req, res) {
res.render('signin',{
err: true,
})
});
app.post('/signin', function(req, res) {
let data = req.body;
User.find({email: data.email},'_id email password', function(err, docs) {
if(err) {
console.log(err);
}
else {
//Finding the matching user
for(i = 0; i < docs.length; i++) {
if(data.password == docs[i].password) {
res.redirect('/'+docs[i]._id + '/userhome')
}
}
if(docs.length === 0) {
console.log('no user found')
res.redirect('/signin/err');
return;
}
}
})
Shop.find({email: data.email}, '_id email password', function(err,docs) {
if(err) {
console.log(err);
}
else {
//Finding the matching user
for(i = 0; i < docs.length; i++) {
if(data.password == docs[i].password) {
res.redirect('/'+docs[i]._id + '/shophome')
}
}
if(docs.length === 0) {
console.log('no shop found')
res.redirect('/signin/err')
break;
}
}
})
})
Also here is the Pug file I'm trying to render (I don't think this is the issue)
doctype html
html
head
title uShop
//Bootstrap CSS
link(rel="stylesheet", href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous")
script(src='https://code.jquery.com/jquery-3.3.1.slim.min.js', integrity='sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo', crossorigin='anonymous')
script(src='https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js', integrity='sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1', crossorigin='anonymous')
script(src='https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js', integrity='sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM', crossorigin='anonymous')
body
nav(class="navbar navbar-expand-md navbar-dark bg-dark sticky-top")
a(class="navbar-text" style="font-size:175%; color:white; padding:0px" href="/") uShop.com
h1(class="display-2 text-center position-relative" style="top: 3rem; font-size: 400%") Sign In
form(action="/signin" method="POST")
div(class="form-row position-relative" style="top:7rem")
label(class="col-lg-4 col-form-label position-relative text-right" for="inputEmail" style="font-size: 150%; top:-5px; left: -5px;") Email:
input(class="form-control col-lg-4" type="email" name="email" id="inputEmail")
//- if err == true
//- div(class="invalid-feedback") email incorrect
br
div(class="form-row position-relative" style="top:7rem")
label(class="col-lg-4 col-form-label position-relative text-right" for="inputPassword" style="font-size: 150%; top:-5px; left: -5px;") Password:
input(class="form-control col-lg-4" type="password" name="password" id="inputPassword")
div(class="form-row position-relative" style="top:8rem")
input(class="btn btn-primary btn-lg offset-lg-4 " type="submit" value="Sign In")
app.post('/signin', function (req, res) {
let data = req.body;
User.find({ email: data.email }, '_id email password', function (err, docs) {
res.redirect('/' + ...);
})
Shop.find({ email: data.email }, '_id email password', function (err, docs) {
res.redirect('/' + ...);
});
})
res.redirect cannot run twice in each request, but, from your couse, at least called twice
How about this?
app.post('/signin', function (req, res) {
let data = req.body;
User.find({ email: data.email }, '_id email password', function (err, docs) {
// res.redirect('/' + ...);
Shop.find({ email: data.email }, '_id email password', function (err, docs) {
res.redirect('/' + ...);
});
});
})
Also, ???
You need redirect to
'/' + docs[i]._id + '/shophome'
and
'/' + docs[0]._id + '/userhome'
in signin API at onece(one request)?
I think, this is impossable with http(rest api) request,
and nodejs and other language is same.
Also, I think, this is purpose of normal user.
As you want to search in either user or shop collection.
Add async/await it will make your code more readable
app.post('/signin', async function(req, res) {
....
})
You need to use findOne for both collection search and usingemail and password both in search params so no further password check needed in code
const userResult = await User.findOne({ email: data.email, password: data.password }, '_id email password');
const shopResult = await Shop.findOne({ email: data.email, password: data.password }, '_id email password');
As findOne returns object as a result if email and password matches.
You can check that if both results are empty then redirect as below
if(!userResult && !shopResult) {
return res.redirect('/signin/err');
}
if(userResult) {
return res.redirect('/'+userResult._id + '/userhome')
}
if(shopResult) {
return res.redirect('/'+shopResult._id + '/shophome')
}
Adding return on reach redirect will ensure that code execution has ended at that point.
Use try/catch to log unwanted errors.
MND link for async/await : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
I am sending a simple register form (POST fetch) to the backend for processing. However, the request body is not being received how it should.
I expect to see request.body = {"username": "john", "password": "password"}
But I when I am console logging it I see
{ '{"username":"car","password":"car"}': '' }
Here is my fetch:
fetchRegister = (e) => { //triggered when submitting Register Info
e.preventDefault();
const registerOptions = {
method: "POST",
mode: "no-cors",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: JSON.stringify({
username: this.state.regUsername, //The inputChange function will set these to whatevr the user types
password: this.state.regPassword,
}),
}
fetch("http://localhost:5000/register", registerOptions)
.then(response => response.json())
.then(data =>
console.log(data)
)
}
And here is my endpoint:
app.use(bodyParser.urlencoded({ extended: true}))
app.use(bodyParser.json());
app.post('/register', (req, res) => {
console.log(req.body);
const inputUsername = req.body.username;
const inputPassword = req.body.password;
let newUser = new User({
userName: inputUsername,
password: inputPassword
})
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(newUser.password, salt, function(err, hash) {
if(err) {
res.send(err);
}
newUser.password = hash;
newUser.save((err) => {
if (err) {
console.log(err)
res.send(JSON.stringify({'message': 'Username is already taken'}));
}else {
res.send(JSON.stringify({'message': 'you were successful'}));
}
})
});
})
})
<div id="registerForm">
<form>
<legend>Register:</legend>
Choose a Username:<input className="reg" value={this.state.regUsername} onChange={this.inputChange("regUsername")} required></input>
Choose your Password:<input className="reg" type="password" value={this.state.regPassword} onChange={this.inputChange("regPassword")} required></input>
<button className="FORMbtn" onClick={this.fetchRegister} type="submit">Register Me</button>
</form>
</div>
I just want a standard object being sent to my backend. I have no idea whether CORS or my application type is messing it up. It works perfectly fine in postman.
It was a CORS issue. I had to install 'cors' with NPM, and bring it into Express. I could then take out 'mode': 'no-cors' and it worked.
I'm working with node 4.3.1, express 4.13.1 and an authentication tool is the local-passport. I'm trying to post login form data by ajax. so I want to show error message without page refresh. but the first step is killing me.
view:
<form id="login_form" action="#">
<input type="hidden" id="csrf" name="_csrf" value="{{_csrfToken}}">
<input id="email" name="email" type="email" placeholder="Email">
<input id="password" name="password" type="password" placeholder="Password">
<button type="" id="forLogin">Login</button>
</form>
//POST Ajax
$('#forLogin').on('click', function(e){
e.preventDefault();
var data= {};
data.email = $('#email').val();
data.password = $('#password').val();
data._csrf = $('#csrf').val();
$.ajax({
type: 'POST',
url: '/login',
data: JSON.stringify(data), // node.js accepts request but responds 403
// data: $('#login_form').serialize(),
// don't accept any request
dataType: 'json',
// headers: {'X_CSRF_TOKEN':'{{_csrfToken}}'},
// not working too
success: function(result){
console.log('success');
if(!result){
console.log('No result');
}
},
error: function (xhr, ajaxOptions, thrownError) {
console.log(xhr.status);
console.log(thrownError);
}
});
});
If I use data: JSON.stringify(data) , the firefox inspector shows very suspicious parameter:
{"email":"test#gmail.com","password":"test","_csrf":"wOEsa4s2-I9dmSQuT9djm0kyrrp9WcZWj6U0"}:""
Even if this parameter passes well, I'm not sure it will work.
and In these cases: data: $('#login_form') or data:data
I don't get any response. but parameters seems plausible and key: attr pairs look neatly.
router.js
router.post('/login', function(req, res, next){
passport.authenticate('local-login', function(err, user, info){
console.log(req.body);
if (err) { return next(err); }
if (!user) {
console.log('authentication fail');
return res.json({ajaxMsg: 'authentication fail'});
}
req.logIn(user, function(err) {
if (err) { return next(err); }
// Redirect if it succeeds
return res.redirect('/');
});
})
});
passport.js
var passport = require('passport'),
LocalStrategy = require('passport-local').Strategy,
User = require('../models/User.js'),
bcrypt = require('bcrypt');
passport.serializeUser(function(user, done){
done(null, user.email);
});
passport.deserializeUser(function(email, done){
User.findOne(email, function(err, user){
done(err, user);
});
});
passport.use('local-login',
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){
console.log('wrong email');
req.flash('email', req.body.email);
return done(null, false, req.flash('error_messages', 'No user found'));
}
if(!user.authenticate(password)){
console.log('wrong password');
req.flash('email', req.body.email);
return done(null, false, req.flash('error_messages', 'Password does not Match'));
}
console.log('Login Success');
return done(null, user);
});
})
);
module.exports = passport;
I realized that there is no way to use connect-flash message without page refresh so I trying to replace other logics It's hard to understand how done() method works
Instead of
if (err) { return next(err); }
Try
if (err) { return res.json({error: err}); }
And If you get an error, then you'll have to sort that out but I believe that's your problem. Is your request returning a 404 or 500 on your logger?
Done is the callback it uses which populates the req.session object.
Also I believe you're not using next() properly. Unless your /login is some sort of middleware and you're expecting something else to happen after it ends
I solved the problem.
router.post('/login', function(req, res, next) {
if(req.body.email.length == 0 || req.body.password.length == 0) {
return res.json({error: 'enter a id and a pw'});
}
passport.authenticate('local-login', function(err, user, info) {
if (err) {
return res.json({srverror: err});
}
// Redirect if it fails
if (!user) {
return res.json({error: info.msg});
}
if (user) {
req.logIn(user, function(err) {
if (err) { return res.json({srverror: err}); }
// Redirect if it succeeds
res.send({redirect: '/'});
});
}
})(req, res, next);
});
(req, res, next) is the code I needed.
but I cannot fully understand why It works.