I'm doing some experimenting in using pure JS/jQuery to handle client-side logic (rather than a framework, like Angular). The problem I'm running into right now is how to log a user in.
Here is my login.hbs file:
<div align="center">
<h2 class="page-header">Account Login</h2>
<form>
<div class="form-group">
{{!-- Username --}}
<input type="text" class="form-control" name="username" placeholder="Username" style="width: 20%">
</div>
<div class="form-group">
{{!-- Password --}}
<input type="password" class="form-control" name="password" placeholder="Password" style="width: 20%">
</div>
<button type="button" class="btn btn-success" onclick="login()">Login</button>
</form>
</div>
This submit request goes to a JS file: login.js:
$(document).ready(function() {
login = () => {
var username = $("[name='username']").val()
var password = $("[name='password']").val()
$.ajax({
type: "PUT",
url: '/login',
data: {
username: username,
password: password
},
success: function(response) {
console.log('Success:')
console.log(response)
},
error: function(error) {
console.log("Error:")
console.log(error)
}
})
}
})
On the server side, I'm accepting this PUT request in index.js:
router.put('/login', function(req, res) {
// Password is not encrypted here
console.log('req.body')
console.log(req.body)
User.findOne({ username: req.body.username }, function(err, user) {
// Password is encrypted here
if (err) throw err
console.log('user')
console.log(user)
bcrypt.compare(req.body.password, user.password, function(err, result) {
if (result) {
var token = jwt.encode(user, JWT_SECRET)
return res.status(200).send({ user: user, token: token })
} else {
return res.status(401).send({error: "Something is wrong."})
}
})
})
})
The current flow is: a user enters their credentials, and those credentials get returned to the success method in my ajax request. The credentials show up in the browser's console (confirming that the server and client are communicating). The question is, how do I route this request to the profile page (or any other page, for that matter)? That is, the user is at http://localhost:3000/login, and after successfully logging in, they are routed to http://localhost:3000/profile, for example, where their personal profile information appears. I'd love to learn both ways of doing this type of routing (server-side and client-side). Thanks!
Related
I am writing system for login and registration. When I post correct details in my register Post method, my request is pending and I am unable to find error. The one error it is giving data and salt arguments required and I am getting status 500 and pending my fetch method in ejs file is producing error "Error: data and salt arguments required"
const users = [];
let status = "";
router
.route("/register")
.get((req, res) => {
res.render('register', {message: ''})
})
.post((req, res) => {
console.log([req.body.password, req.body.email]);
const hashPassword = bcrypt.hashSync(req.body.password, 10);
const checkUser = {
email: req.body.email,
password: req.body.password,
};
const { value, error } = validateUser(checkUser);
if (error) {
status = "error"
res.status(404).render('register', {message : error.message});
} else {
const user = users.find((u)=>{
if(u.email === req.body.email){
status = "user exist"
res.status(404).render('register',{ message : "user exist"})
}else{
const newUser = {
email : req.body.email,
password: hashPassword
}
users.push(newUser)
console.log("users is" + users)
res.status(200).render('ok')
}
})
}
});
the errors i am getting in console
[ undefined, undefined ]
Error: data and salt arguments required
at Object.hashSync (C:\Users\USER\Desktop\nodejs\login\node_modules\bcrypt\bcrypt.js:91:15)
at C:\Users\USER\Desktop\nodejs\login\routes\login.js:24:33
at Layer.handle [as handle_request] (C:\Users\USER\Desktop\nodejs\login\node_modules\express\lib\router\layer.js:95:5)
at next (C:\Users\USER\Desktop\nodejs\login\node_modules\express\lib\router\route.js:137:13)
at next (C:\Users\USER\Desktop\nodejs\login\node_modules\express\lib\router\route.js:131:14)
at Route.dispatch (C:\Users\USER\Desktop\nodejs\login\node_modules\express\lib\router\route.js:112:3)
at Layer.handle [as handle_request] (C:\Users\USER\Desktop\nodejs\login\node_modules\express\lib\router\layer.js:95:5)
at C:\Users\USER\Desktop\nodejs\login\node_modules\express\lib\router\index.js:281:22
at Function.process_params (C:\Users\USER\Desktop\nodejs\login\node_modules\express\lib\router\index.js:335:12)
at next (C:\Users\USER\Desktop\nodejs\login\node_modules\express\lib\router\index.js:275:10)
[ '113245', 'sambhav#gmail.com' ]
this is the frontend system i am using
<body>
<h1>register</h1>
<p><%=message%></p>
<form action="/api/register" method="POST">
<input type="text" name="email" id="email" placeholder="enter email" /><br />
<input
type="password"
name="password"
id="password"
placeholder="enter password"
/><br />
<button type="submit">register</button>
</form>
login
<script>
const form = document.querySelector('form')
const email = document.querySelector('#email')
const password = document.querySelector('#password')
form.addEventListener("submit", function (e) {
e.preventDefault()
fetch("/api/register", {
// Adding method type
method: "POST",
// Adding body or contents to send
body: JSON.stringify({
email: email.value,
password: password.value,
}),
})
// Converting to JSON
.then(response => response.json())
// Displaying results to console
.then(json => console.log(json));
})
</script>
</body>
Values req.body.password and req.body.email are clearly undefined, so bcrypt.hashSync is complaining that it cannot hash a null/undefined value.
It looks like the values are not being POSTed correctly. Check your code to make sure you are using body-parsing middleware (e.g. router.route("/register").use(bodyParser.json())).
You should also look at your frontend code or in devtools for a clue as to why they are not being passed.
I can't tell if your request is hanging, but additionally, you may want to define an error handling route in Express if you do not want the requests to hang when there is a server error (if you have not already).
Remember also that any sync function on a server will block incoming requests until it completes
I am trying to get the token value from the following URL http://localhost:3000/users/reset/e3b40d3e3550b35bc916a361d8487aefa30147c8. I have a get request that checks if the token is valid and redirects the user to a reset password screen. I also have a post request but when I console req.params.token, it outputs :token instead of e3b40d3e3550b35bc916a361d8487aefa30147c8. I am wondering if the form action is correct but don't know how to get the token value from it.
Reset Password Get Request
router.get('/reset/:token', (req, res) => {
console.log(req.params.token) // e3b40d3e3550b35bc916a361d8487aefa30147c8
User.findOne({
resetPasswordToken: req.params.token,
resetPasswordExpires: {
$gt: Date.now()
}
}, (err, user) => {
if (!user) {
req.flash('error_msg', 'The password reset token is invalid or has expired.')
return res.redirect('/users/forgot')
}
res.render('reset')
})
})
reset.ejs
<% include ./partials/messages %>
<form action="/users/reset/:token" method="POST">
<div class="form-group">
<label for="password">Password</label>
<input type="password" id="password" name="password" class="form-control" placeholder="Please enter a password."
value="<%= typeof password != 'undefined' ? password : '' %>" />
</div>
<button type="submit" class="btn btn-primary btn-block">Register</button>
</form>
Reset Password Post Request
router.post('/reset/:token', (req, res) => {
console.log(req.params.token) // :token
User.findOne({
resetPasswordToken: req.params.token,
resetPasswordExpires: {
$gt: Date.now()
}
}, (err, user) => {
if (!user) {
req.flash('error_msg', 'The password reset token is invalid or has expired.')
return res.redirect('/users/forgot')
}
user.password = req.body.password;
user.resetPasswordToken = undefined;
user.resetPasswordExpires = undefined;
user.save(function (err) {
req.flash('success_msg', 'Working.')
return res.redirect('/users/login')
})
})
})
In your form in your HTML, you have this:
<form action="/users/reset/:token" method="POST">
That's going to make the actual URL that gets requested when the form is posted be:
/users/reset/:token
There's no code doing any substitution for the :token here. That's just getting sent directly to the server as the URL.
So, when you then have:
router.post('/reset/:token', (req, res) => {
console.log(req.url); // "/user/reset/:token"
console.log(req.params.token); // ":token"
});
What req.params.token is showing you is whatever is in the URL that's after /users/reset. In your case, that is the literal string ":token". For req.params.token to actually have to token in it, you would have to insert the actual token into the URL so your form tag looks like this:
<form action="/users/reset/e3b40d3e3550b35bc916a361d8487aefa30147c8" method="POST">
Or, you will have to get access to the token some other way such as from the express session, from a cookie, from a field in the form, etc...
To get a URL parameter's value
app.get('/reset/:token', function(req, res) {
res.send("token is " + req.params.token);
});
To get a query parameter ?token=Adhgd5645
app.get('/reset/?token=Adhgd5645', function(req, res) {
res.send("token is " + req.query.token);
});
I am trying to practice creating apps with socket.io and node.js. This is a simple "log in" app, but it doesn't seem to be working, whenever I start the app a 404 not found message pops up on the chrome developer console with this adress: http://localhost:3000/socket.io/?EIO=3&transport=polling&t=1469215104245-2
in my index.hjs file
<div class="input-group">
<span class="input-group-addon" id="basic-addon1"></span>
<input type="text" class="form-control username" placeholder="Username" aria-describedby="basic-addon1">
</div>
<div class="input-group">
<span class="input-group-addon" id="basic-addon1"></span>
<input type="text" class="form-control password" placeholder="Password" aria-describedby="basic-addon1">
</div>
<button type="button" class="btn btn-primary">Primary</button>
<h2></h2>
<script src="https://cdn.socket.io/socket.io-1.2.0.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" integrity="sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS" crossorigin="anonymous"></script>
<script type="text/javascript">
var socket = io();
$('.btn-primary').click(function(){
var data = {username: $('.username').val(), password: $('.password').val()};
socket.emit('log-in',data);
socket.on('log-in success', function(){
$('h2').append('success')
});
socket.on('log-in fail', function(){
$('h2').append('fail')
});
});
</script>
on my node js server (there is more code there but these are the rlevant bits):
var app = express();
var server = require('http').Server(app);
var io = require('socket.io')(server);
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/userDB');
var userSchema = mongoose.Schema({
name: String,
password: String
});
var User = mongoose.model('User', userSchema);
io.on('log-in',function(data){
User.findOne({ 'username': data.username, 'password': data.password}, function (err, person) {
if (err) {io.emit('log-in fail')
return handleError(err)};
if (!person) io.emit('log-in success');
})
});
Whenever I try to log in as a premade user just don't get any response back.
Any help would be much appreciated.
You don't listen to messages from a given connection on the server with io.on(). Instead, you do this where you listen for a given socket connecting and then you add event handlers to that specific socket and when you want to send back to that socket, you also use that socket object to send back to. This allows you to send back to the same socket that just sent you a message:
io.on('connection', function (socket) {
socket.on('log-in', function (data) {
User.findOne({'username': data.username, 'password': data.password}, function (err, person) {
if (err) {
socket.emit('log-in fail')
return handleError(err)
};
if (!person) socket.emit('log-in success');
})
});
});
But, you may also want to know that there's a framework for socket.io connection authentication that makes it a little more of a built-in part of accepting a connection. You can do your own like this if you want, but it will likely have some issues you haven't thought of.
P.S. This logic looks a bit flawed to me. If you find the person in your database, you emit no response on the socket.
I am trying to adapt some code taken from:
https://github.com/expressjs/express/blob/master/examples/auth/index.js
and incorporate into a login form. The issue is that when running the code and selecting the login button the following is logged in the console:
POST /login
Authenticating undefined:foobar
Authentication failed, please check your username and password. (use "tj" and "foobar")
I'm unsure as to why the user is being returned as undefined when I am inputting tj and foobar as the login and password.
HTML:
<div class="login">
<form method="post" action="/login">
<p><input type="text" name="login" value="" placeholder="Username"></p>
<p><input type="password" name="password" value="" placeholder="Password"></p>
<p class="remember_me">
<label>
<input type="checkbox" name="remember_me" id="remember_me">
Remember me on this computer
</label>
</p>
<p class="submit"><input type="submit" name="commit" value="Login"></p>
</form>
</div>
<div class="login-help">
<p>Forgot your password? Click here to reset it.</p>
</div>
JS:
/**
* Module dependencies.
*/
var express = require('express');
var hash = require('./pass').hash;
var bodyParser = require('body-parser');
var session = require('client-sessions');
var app = module.exports = express();
// dummy database
var users = {
tj: { name: 'tj' }
};
// when you create a user, generate a salt
// and hash the password ('foobar' is the pass here)
hash('foobar', function (err, salt, hash){
if (err) throw err;
// store the salt & hash in the "db"
users.tj.salt = salt;
users.tj.hash = hash;
});
// check if user is logged in, if not redirect them to the index
function requireLogin (req, res, next) {
if (!req.user) {
res.redirect('/');
} else {
next();
}
};
// Authenticate using our plain-object database
function authenticate(name, pass, fn) {
if (!module.parent) console.log('Authenticating %s:%s', name, pass);
var user = users[name];
// query the db for the given username
if (!user) return fn(new Error('cannot find user'));
// apply the same algorithm to the POSTed password, applying
// the hash against the pass / salt, if there is a match we
// found the user
hash(pass, user.salt, function(err, hash){
if (err) return fn(err);
if (hash == user.hash) return fn(null, user);
fn(new Error('invalid password'));
});
}
app.post('/login', function (req, res){
console.log("POST /login")
authenticate(req.body.username, req.body.password, function(err, user){
if (user) {
// Regenerate session when signing in
// to prevent fixation
req.session.regenerate(function(){
// Store the user's primary key
// in the session store to be retrieved,
// or in this case the entire user object
req.session.user = user;
/*req.session.success = 'Authenticated as ' + user.name
+ ' click to logout. '
+ ' You may now access /restricted.';*/
res.redirect('/queryInterface.html');
});
} else {
console.log('Authentication failed, please check your '
+ ' username and password.'
+ ' (use "tj" and "foobar")');
res.redirect('/');
}
});
});
You are seeing the user as undefined because you are using req.body to access an undefined input field.
You are authenticating with req.body.username, but your input field has the name login.
req.body is populated with the names of your input fields. Your input should look like this when trying to access the username by req.body.username.
<input type="text" name="username" value="" placeholder="Username">
I'm learning Node.js and all its functionalities and I'm using scotch's tutorial (https://scotch.io/tutorials/easy-node-authentication-setup-and-local), I built what he proposed and it was really good, but then I wanted to get more informations for user, like name and a special number called SIAPE as my user model code shows: user.js
var mongoose = require('mongoose');
var bcrypt = require('bcrypt-nodejs');
var userSchema = mongoose.Schema({
local : {
name : String,
email : String,
password : String,
siape : String
}
});
userSchema.methods.generateHash = function(password) {
return bcrypt.hashSync(password, bcrypt.genSaltSync(8),null);
};
userSchema.methods.validPassword = function(password) {
return bcrypt.compareSync(password, this.local.password);
};
module.exports = mongoose.model('User', userSchema);
And then set up my passport.js to update those thing to my mangodb database with mongoose, but it dosen't work and I dont know why.
// config/passport.js
// load all the things we need
var LocalStrategy = require('passport-local').Strategy;
// load up the user model
var User = require('../app/models/users');
// expose this function to our app using module.exports
module.exports = function(passport) {
// =========================================================================
// passport session setup ==================================================
// =========================================================================
// required for persistent login sessions
// passport needs ability to serialize and unserialize users out of session
// used to serialize the user for the session
passport.serializeUser(function(user, done) {
done(null, user.id);
});
// used to deserialize the user
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
// =========================================================================
// LOCAL SIGNUP ============================================================
// =========================================================================
// we are using named strategies since we have one for login and one for signup
// by default, if there was no name, it would just be called 'local'
passport.use('local-signup', new LocalStrategy({
// by default, local strategy uses username and password, we will override with email
usernameField : 'siape',
passwordField : 'password',
passReqToCallback : true // allows us to pass back the entire request to the callback
},
function(req, siape,password, email,name, done) {
// asynchronous
// User.findOne wont fire unless data is sent back
process.nextTick(function() {
console.log('USERNAME:' + name );
console.log('EMAIL:' + email );
console.log('PASSWORD:' + password );
console.log('SIAPE:' + siape );
console.log('DONE:' + done);
console.log('REQ:' + req);
// 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.siape' : siape }, function(err, user) {
// if there are any errors, return the error
if (err)
return done(err);
// check to see if theres already a user with that email
if (user) {
return done(null, false, req.flash('signupMessage', 'Email já existe'));
} else {
// if there is no user with that Email
// create the user
var newUser = new User();
// set the user's local credentials
newUser.local.name = name;
newUser.local.email = email;
//newUser.local.password = newUser.generateHash(password);
newUser.local.password = password;
newUser.local.siape = siape;
// save the user
newUser.save(function(err) {
if (err)
throw err;
return done(null, newUser);
});
}
});
});
}));
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('loginMessage', 'No 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('loginMessage', 'Oops! Wrong password.')); // create the loginMessage and save it to session as flashdata
// all is well, return successful user
return done(null, user);
});
}));
};
Don't know if it helps, but I'll update also the html form:
<!-- views/signup.ejs -->
<!doctype html>
<html>
<head>
<title>Cadastro de professor</title>
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.2/css/bootstrap.min.css"> <!-- load bootstrap css -->
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.min.css"> <!-- load fontawesome -->
<style>
body { padding-top:80px; }
</style>
</head>
<body>
<div class="container">
<div class="col-sm-6 col-sm-offset-3">
<h1><span class="fa fa-sign-in"></span> Cadastro De Professor</h1>
<!-- show any messages that come back with authentication -->
<% if (message.length > 0) { %>
<div class="alert alert-danger"><%= message %></div>
<% } %>
<!-- LOGIN FORM -->
<form action="/signup" method="post">
<div class="form-group">
<label>Nome</label>
<input type="text" class="form-control" name="username">
</div>
<div class="form-group">
<label>Email</label>
<input type="text" class="form-control" name="email">
</div>
<div class="form-group">
<label>Senha</label>
<input type="password" class="form-control" name="password">
</div>
<div class="form-group">
<label>SIAPE</label>
<input type="text" class="form-control" name="siape">
</div>
<button type="submit" class="btn btn-warning btn-lg">Cadastrar Professor</button>
</form>
<hr>
<p>Professor ja cadastrado?? Entre</p>
<p>Ou volte.</p>
</div>
</div>
</body>
</html>
A lot of things are happening that I don't quite understand, for example, I've put a console.log to print all the information sent in the html form, and this was what was print:
USERNAME:undefined
EMAIL:function verified(err, user, info) {
if (err) { return self.error(err); }
if (!user) { return self.fail(info); }
self.success(user, info);
}
PASSWORD:senha
SIAPE:SIAPE
DONE:undefined
REQ:[object Object]
And for the last part, the error printed in my terminal server:
/home/aluno/28882/naest/AdmnistrationModule/node_modules/mongoose/lib/utils.js:413
throw err;
^
TypeError: undefined is not a function
at Promise.<anonymous> (/home/aluno/28882/naest/AdmnistrationModule/config/passport.js:83:31)
at Promise.<anonymous> (/home/aluno/28882/naest/AdmnistrationModule/node_modules/mongoose/node_modules/mpromise/lib/promise.js:177:8)
at Promise.emit (events.js:98:17)
at Promise.emit (/home/aluno/28882/naest/AdmnistrationModule/node_modules/mongoose/node_modules/mpromise/lib/promise.js:84:38)
at Promise.fulfill (/home/aluno/28882/naest/AdmnistrationModule/node_modules/mongoose/node_modules/mpromise/lib/promise.js:97:20)
at handleSave (/home/aluno/28882/naest/AdmnistrationModule/node_modules/mongoose/lib/model.js:133:13)
at /home/aluno/28882/naest/AdmnistrationModule/node_modules/mongoose/lib/utils.js:408:16
at /home/aluno/28882/naest/AdmnistrationModule/node_modules/mongoose/node_modules/mongodb/lib/mongodb/collection/core.js:128:9
at /home/aluno/28882/naest/AdmnistrationModule/node_modules/mongoose/node_modules/mongodb/lib/mongodb/db.js:1197:7
at /home/aluno/28882/naest/AdmnistrationModule/node_modules/mongoose/node_modules/mongodb/lib/mongodb/db.js:1905:9
at Server.Base._callHandler (/home/aluno/28882/naest/AdmnistrationModule/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/base.js:453:41)
at /home/aluno/28882/naest/AdmnistrationModule/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/server.js:488:18
at MongoReply.parseBody (/home/aluno/28882/naest/AdmnistrationModule/node_modules/mongoose/node_modules/mongodb/lib/mongodb/responses/mongo_reply.js:68:5)
at null.<anonymous> (/home/aluno/28882/naest/AdmnistrationModule/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/server.js:446:20)
at emit (events.js:95:17)
at null.<anonymous> (/home/aluno/28882/naest/AdmnistrationModule/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/connection_pool.js:207:13)
aluno#lab2208-pc32:~/28882/naest/AdmnistrationModule$ clear
Just solved my problem, the problem was that local-signup only accepts 2 parameters as default, username and password for authentication, other variable are to be catched using the "req.body" prefix. This link helped me a lot
Does passportjs LocalStrategy allow more parameters than the default username and password?