I'm trying to implement authentication into my node-express-mongod app. To start with, I made an app.post in my server.js but having trouble getting result back using this curl:
curl -X POST -H "Content-Type: application/json" -d '{"username": "joe", "password": "passwordtest"}' http://localhost:8080/users
What I get is:
{"message":"Internal server error"}
My current server.js looks like this:
var express = require('express');
var bodyParser = require('body-parser');
var jsonParser = bodyParser.json();
var mongoose = require('mongoose');
var config = require('./config');
var Item = require('./models/item');
var User = require('./models/user-model');
// Mongoose Connect
var app = express();
app.use(bodyParser.urlencoded({
extended: true
}));
app.use(bodyParser.json());
app.use(express.static('public'));
var runServer = function(callback) {
mongoose.connect(config.DATABASE_URL, function(err) {
if (err && callback) {
return callback(err);
}
console.log("Connected to mongodb!");
app.listen(config.PORT, function() {
console.log('Listening on localhost:' + config.PORT);
if (callback) {
callback();
}
});
});
};
if (require.main === module) {
runServer(function(err) {
if (err) {
console.error(err);
}
});
};
// Route for passport test
app.post('/users', jsonParser, function(req, res) {
if (!req.body) {
return res.status(400).json({
message: "No request body"
});
}
if (!('username' in req.body)) {
return res.status(422).json({
message: 'Missing field: username'
});
}
var username = req.body.username;
if (typeof username !== 'string') {
return res.status(422).json({
message: 'Incorrect field type: username'
});
}
username = username.trim();
if (username === '') {
return res.status(422).json({
message: 'Incorrect field length: username'
});
}
if (!('password' in req.body)) {
return res.status(422).json({
message: 'Missing field: password'
});
}
var password = req.body.password;
if (typeof password !== 'string') {
return res.status(422).json({
message: 'Incorrect field type: password'
});
}
password = password.trim();
if (password === '') {
return res.status(422).json({
message: 'Incorrect field length: password'
});
}
var user = new User({
username: username,
password: password
});
user.save(function(err) {
if (err) {
return res.status(500).json({
message: 'Internal server error'
});
}
return res.status(201).json({});
});
});
// routes for crud operations
app.get('/items', function(req, res) {
Item.find(function(err, items) {
if (err) {
return res.status(500).json({
message: 'Internal Server Error'
});
}
res.json(items);
});
});
app.post('/items', jsonParser, function(req, res) {
console.log("req:", req.body);
if (!req.body) {
return res.sendStatus(400);
}
else {
var favoritesList = req.body;
res.status(201).json(favoritesList);
}
console.log("res:", res.status);
// console.log("name", name);
Item.create({
name: req.body.name,
wineResults: req.body.wineResults
},
function(err, item) {
if (err) {
message: err
}
else {
message: "done"
}
});
});
app.delete('/items/:name', function(req, res) {
Item.findOneAndRemove(req.params.name, function(err, items) {
if (err) {
return res.status(404).json({
message: 'Internal Server Error'
});
}
Item.remove({
name: req.body.name,
wineResults: req.body.wineResults
},
function(err, item) {
if (err) {
message: err
}
else {
message: "done"
}
});
});
});
app.put('/items/:id', function(req, res) {
var data = req.body; // data should be validated
Item.findOneAndUpdate({ name: req.params.id }
, { $set: { name: data.name }}
, { returnOriginal: false }
, function (err, items) {
if (err) {
return res.status(500).json({
message: 'Internal Server Error.'
});
}
return res.json(items);
}
);
});
app.use('*', function(req, res) {
res.status(404).json({
message: 'Not Found'
});
});
exports.app = app;
exports.runServer = runServer;
This is user-model.js:
var mongoose = require('mongoose');
var UserSchema = new mongoose.Schema({
username: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
}
});
var User = mongoose.model('User', UserSchema);
module.exports = User;
I'm just learning about authentication and node and just not sure what other steps I can take to troubleshoot this. Any help is appreciated. Thanks.
Related
i am creating a REST API for my users, and i used express-validator for the validation before creating the user in the database.. but when i chain my middleware together in the router.py file it gives the error Error: Route.post() requires a callback function but got a [object Undefined]
i imported my middleware and controller from thier respective files.
here is the code in my router.py
const express = require('express');
const authMiddleware = require('../middlewares/authMiddleware');
const authController = require('../controllers/authController');
const router = express.Router();
router.post(
'/signup',
authMiddleware.verifySignUpInit(),
authMiddleware.verifySignUp,
authController.signup
);
module.exports = router;
in my middleware file i added this..
const jwt = require('jsonwebtoken');
const { authentication } = require('../config');
const { User } = require('../models');
const { body } = require('express-validator');
const verifySignUpInit = () => {
return [
body('email', 'email is required').exists().normalizeEmail().isEmail(),
body('phone', 'phone is required').exists().isInt(),
body('first_name', 'first_name is required').exists().isString(),
body('last_name', 'last_name is required').exists().isString(),
body('password', 'password is required').exists(),
];
};
const verifySignUp = (req, res, next) => {
const { email, phone, password } = req.body;
User.findOne({
email,
}).exec((err, user) => {
if (err) {
res.status(500).json({
status: 'error',
message: err,
});
}
if (user) {
return res.status(400).json({
status: 'failed',
message: 'Email Provided Already Exists',
});
}
});
User.findOne({
phone,
}).exec((err, user) => {
if (err) {
res.status(500).json({
status: 'error',
message: err,
});
}
if (user) {
return res.status(400).json({
status: 'failed',
message: 'Phone Provided Already Exists',
});
}
});
const password_is_valid = authentication.passwordSchema.validate(password);
if (!password_is_valid) {
return res.status(400).json({
status: 'failed',
message: 'password requirements not met',
});
}
next();
};
module.exports = {
verifySignUpInit,
verifySignUp,
};
and finally in my controller i have this..
const config = require('../config');
const db = require('../models');
var jwt = require('jsonwebtoken');
var bcrypt = require('bcryptjs');
const { validationResult } = require('express-validator');
const { User, Role } = db;
const { token_expiry_time, refresh_token_expiry_time } = config.authentication;
const signUp = (req, res) => {
const { email, phone, first_name, last_name, password } = req.body;
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({
status: 'failed',
message: errors.array(),
});
}
bcrypt.hash(password, 8, (err, hash) => {
if (err) {
return res.status(500).json({
status: 'error',
message: err,
});
}
const user = new User({
first_name: first_name,
last_name: last_name,
email: email,
phone: phone,
password: hash,
});
Role.findOne({ name: 'is_user' }, (err, role) => {
if (err) {
res.status(500).json({
status: 'error',
message: err,
});
return;
}
user.roles = [role._id];
user.save((err) => {
if (err) {
return res.status(500).json({
status: 'error',
message: err,
});
}
return res.status(201).json({
status: 'success',
data: {
user,
},
});
});
});
});
};
module.exports = {
signUp,
};
i cant tell what i am doing wrong :(
authController.signup
should be:
authController.signUp
You have to use the same capitalization for the property name you exported.
I am currently implementing a login page via Node JS Express and Passport using Passport's local strategy: http://www.passportjs.org/packages/passport-local/.
The database is using MongoDB.
The problem that is occurring is I am unable to login successfully (sometimes I can, but it is not consistent) on an android phone and android tablet with User A.
The response being returned is a 401 (unauthorized error).
I have verified that I can log in successfully consistently with User A from a desktop computer, and also verified I can login on iOS devices (iPhone and iPad).
Does anyone have any suggestions, and / or know what the issue is?
Below is the code that executes the authentication
app_api/config/passport.js
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var mongoose = require('mongoose');
var User = mongoose.model('User');
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(new LocalStrategy({
usernameField: 'email'
},
function(username, password, done) {
User.findOne({ email: 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_api/controllers/authentication.js
var passport = require('passport');
var mongoose = require('mongoose');
var User = mongoose.model('User');
var sendJSONresponse = function(res, status, content) {
res.status(status);
res.json(content);
};
module.exports.test = function(req, res) {
sendJSONresponse(res, 200, { 'status' : 'success' });
};
module.exports.register = function(req, res) {
if(!req.body.name || !req.body.email || !req.body.password) {
sendJSONresponse(res, 400, {
"message": "All fields required"
});
return;
}
var user = new User();
user.name = req.body.name;
user.email = req.body.email;
user.setPassword(req.body.password);
user.setShowResume(false);
user.save(function(err) {
var token;
if (err) {
sendJSONresponse(res, 404, err);
} else {
token = user.generateJwt();
sendJSONresponse(res, 200, {
'token' : token
});
}
});
};
module.exports.login = function(req, res) {
if(!req.body.email || !req.body.password) {
sendJSONresponse(res, 400, {
'message': 'All fields required'
});
return;
}
passport.authenticate('local', function(err, user, info){
var token, showResume;
if (err) {
sendJSONresponse(res, 404, err);
return;
}
if(user){
token = user.generateJwt();
showResume = user.showResume;
sendJSONresponse(res, 200, {
'token' : token,
'showresume': showResume
});
} else {
sendJSONresponse(res, 401, info);
}
})(req, res);
};
module.exports.logout = function(req, res) {
req.logout();
res.redirect('/');
};
Below is the controller app_server/controllers/index.js and the function logincontinue which calls the app_api/controllers/authentication.js file and its function login:
app_server/controllers/index.js
var request = require('request');
var apiOptions = {
server : "http://localhost:3000"
};
if (process.env.NODE_ENV === 'production') {
apiOptions.server = "https://siteDomain.com";
}
module.exports.home = function(req, res) {
renderLoginPage(req, res);
};
/* Other routes / functions ... */
module.exports.logincontinue = function(req, res) {
var path = '/api/authentication/login', sess;
//var connectSid = res.req.cookies['connect.sid'];
var requestOptions = {
url : apiOptions.server + path,
method : "POST",
json : { email: req.body.username, password: req.body.password }/*,
headers : { 'set-cookie': connectSid }*/
};
request(requestOptions, function(err, response, body) {
if(err) {
renderPage(req, res, 'siteDomain.com', 'loginerror');
} else if(response.statusCode === 200) {
switch(body.showresume) {
case true:
res.render('loginsuccessresume', { title: 'siteDomain.com', page: 'loginsuccessresume', showresume: true });
break;
default:
renderPage(req, res, 'siteDomain.com', 'loginsuccess');
break;
}
} else if(response.statusCode === 401) {
res.render('loginunauthorized', { title: 'siteDomain.com', page: 'loginunauthorized', errormessage: body.message });
} else {
renderPage(req, res, 'siteDomain.com', 'loginerror');
}
});
};
var renderPage = function(req, res, titleValue, pageValue) {
res.render(pageValue, { title: titleValue, page: pageValue });
};
var renderLoginPage = function(req, res) {
res.render('index', { title: 'siteDomain.com', page: 'login' });
};
As an update, this issue has been resolved.
The issue was related to case-sensitivity with respect to the username field.
Changing req.body.username to req.body.username.toLowerCase() resolved the issue within app_server/controllers/index.js and the function logincontinue.
Thank you.
I'm trying to secure an API endpoint on a node.js express app that uses passport.
This is my route:
router.post('/api/devices', authController.isAuthorized, catchErrors(deviceController.getDevicesAPI));
This is my authorization method:
exports.isAuthorized = (req, res, next) => {
passport.authenticate('local', {session: false}, (err, user, info) => {
if (err || !user) {
return res.json({ message: 'Something is not right ', err, info });
}
req.login(user, {session: false}, (err) => {
if (err) {
res.send(err);
}
next();
});
})(req, res);
};
From Postman or a separate local server, I get the response:
{
"message": "Something is not right ",
"err": null,
"info": {
"message": "Missing credentials"
}
}
This is the Postman configuration:
What am I missing?
How is your local strategy configured? It seems like a database query problem
As the sample in http://www.passportjs.org/docs/username-password/, please see my comments below
var passport = require('passport')
, LocalStrategy = require('passport-local').Strategy;
passport.use(new LocalStrategy(
function(username, password, done) { //<--- Here is where you pass the UN&PASS
User.findOne({ username: username }, function(err, user) { //<--- Here is the sample code that should find you a user
if (err) { return done(err); } //<--- Here could be where the response is coming from
if (!user) {
return done(null, false, { message: 'Incorrect username.' });
}
if (!user.validPassword(password)) {
return done(null, false, { message: 'Incorrect password.' });
}
return done(null, user); //<--- Here is the sample code that should let you return that user
});
}
));
I finally dug it out from here. User.authenticate() is the method I was looking for.
exports.isAuthorized = async (req, res, next) => {
const username = req.body.username;
const password = req.body.password;
const user = await User.findOne({ email: username });
if (!user) {
res.sendStatus(403);
return;
}
user.authenticate(password, function(err, result) {
if (result) {
next();
return;
}
res.sendStatus(403);
});
};
I am creating an bill tracking application that is having users create bills based on criteria I have created using javascript.
I am tasked with performing acceptance testing.
This is my code so far:
My index.js file
const express = require('express')
const app = express()
const port = 3000
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/bill');
var MongoClient = require('mongodb').MongoClient
var ObjectID = require('mongodb').ObjectID
app.use(express.json())
app.use(express.urlencoded({ extended: true }));
app.use(express.static('public'))
app.set('view engine', 'pug')
app.set('views', __dirname + '/views');
var billSchema = new mongoose.Schema(
{
type: { type: String, required: true },
dueDate: { type: Date, required: true },
company: { type: String, required: true },
amtDue: { type: Number, required: true },
paidStatus: { type: String, required: true }
});
var bill = mongoose.model('bill', billSchema);
var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function()
{
app.get('/', (req, res) =>
{
bill.find({}, function(err, bills)
{
if (err)
{
console.log(err)
res.render('error', {})
}
else
{
res.render('index', { bills: bills })
}
});
});
app.get('/bills/new', (req, res) =>
{
res.render('bill-form', { title: "New bill", bill: {} })
});
app.get('/bills/:id/update', (req, res) =>
{
let id = ObjectID.createFromHexString(req.params.id)
bill.findById(id, function(err, bill)
{
if (err)
{
console.log(err)
res.render('error', {})
}
else
{
if (bill === null) {
res.render('error', { message: "Not found" })
} else {
res.render('bill-form', { title: "Update bill", bill: bill })
}
}
});
});
app.post('/bills/new', function(req, res, next) {
let newbill = new bill(req.body);
newbill.save(function(err, savedbill)
{
if (err)
{
console.log(err)
res.render('bill-form', { bill: newbill, error: err })
}
else
{
res.redirect('/bills/' + savedbill.id);
}
});
});
app.get('/bills/:id', (req, res) =>
{
let id = ObjectID.createFromHexString(req.params.id)
bill.findById(id, function(err, bill)
{
if (err)
{
console.log(err)
res.render('error', {})
}
else
{
if (bill === null)
{
res.render('error', { message: "Not found" })
}
else
{
res.render('bill-detail', { bill: bill})
}
}
});
});
app.post('/bills/:id/update', (req, res, next) =>
{
let id = ObjectID.createFromHexString(req.params.id)
bill.updateOne({"_id": id}, { $set: req.body }, function(err, details)
{
if(err)
{
console.log(err)
res.render('error', {})
}
else
{
res.redirect('/bills/' + id)
}
});
});
app.post('/bills/:id/delete', (req, res) =>
{
let id = ObjectID.createFromHexString(req.params.id)
bill.deleteOne({_id: id}, function(err, product)
{
res.redirect("/")
});
});
app.post('/api/bills', (req, res) =>
{
let newbill = new bill(req.body)
newbill.save(function (err, savedbill)
{
if (err)
{
console.log(err)
res.status(500).send("There was an internal error")
}
else
{
res.send(savedbill)
}
});
});
app.post('/api/bills', (req, res) =>
{
bill.find({}, function(err, bills)
{
if(err)
{
console.log(err)
res.status(500).send("Internal server error")
}
else
{
res.send(bills)
}
});
});
app.get('/api/bills', (req, res) =>
{
bill.find({}, function(err, bills)
{
if(err)
{
console.log(err)
res.status(500).send("Internal server error")
}
else
{
res.send(bills)
}
});
});
app.get('/api/bills/:id', (req, res) =>
{
let id = ObjectID.createFromHexString(req.params.id)
bill.findById(id, function(err, bill)
{
if (err)
{
console.log(err)
res.status(500).send("Internal server error")
}
else
{
if (bill === null)
{
res.status(404).send("Not found")
}
else
{
res.send(bill)
}
}
});
});
app.put('/api/bills/:id', (req, res) =>
{
let id = ObjectID.createFromHexString(req.params.id)
bill.updateOne({"_id": id}, { $set: req.body }, function(err, details)
{
if (err)
{
console.log(err)
res.status(500).send("Internal server error")
}
else
{
res.status(204).send()
}
});
});
app.delete('/api/bills/:id', (req, res) =>
{
let id = ObjectID.createFromHexString(req.params.id)
Review.deleteOne({"_id": id}, function(err)
{
if (err)
{
console.log(err)
res.status(500).send("Internal server error")
}
else
{
res.status(204).send()
}
});
});
});
app.listen(port, () => console.log(`Example app listening on port ${port}!`))
module.exports.app = app;
module.exports.schema = bill;
Then I have a javscript dedicated to testing
let assert = require('assert');
let chai = require('chai');
let chaiHttp = require('chai-http');
let serverAndSchema = require('../index');
let server = serverAndSchema.app
let Bill = serverAndSchema.schema
let should = chai.should();
chai.use(chaiHttp);
describe('Bills', function() {
describe('/GET bill', function() {
it('should get the specified bill', function(done) {
let expectedBill = new Bill({
type: "Test Type",
dueDate: "12/3/2018T06:00:00.000Z",
company: "Test Company",
amtDue: "100",
paidStatus: "Test Status"
});
expectedBill.save(function(err, savedBill) {
chai.request(server)
.get('/api/bills/'+savedBill.id)
.end((err, res) => {
res.should.have.status(200);
res.body.should.be.a('object');
res.body.should.have.property('type').eql(savedBill.type)
res.body.should.have.property('dueDate').eql(savedBill.dueDate)
res.body.should.have.property('company').eql(savedBill.company)
res.body.should.have.property('amtDue').eql(savedBill.amtDue)
res.body.should.have.property('paidStatus').eql(savedBill.paidStatus)
res.body.should.have.property('_id').eql(savedBill.id)
done();
})
});
});
});
});
The data is added to my database, but I get this error when I try to run the test:
Uncaught TypeError: Cannot read property 'id' of undefined
at
C:\Users\Martae\Documents\Github\BillTracker\test\bill.js:29:46
Any help with this is appreciated!
When I checked the Bill schema, I found that the issue is caused by your due date format. So, the mongo saved is failed and you got undefined for savedBill and when you try to access id, it throws the error that you got.
Here is the solution:
let expectedBill = new Bill({
type: "Test Type",
dueDate: "2018-03-13T06:00:00.000Z", // change date to standardized format
company: "Test Company",
amtDue: "100",
paidStatus: "Test Status"
});
expectedBill.save(function(err, savedBill) {
const id = savedBill._id; // store id, it is always returned as _id
chai.request(server)
.get('/api/bills/'+id)
.end((err, res) => {
// ...
done();
});
});
I'm trying to validate username before save it to mongodb. But instead saving or validation message i see the following message in terminal:
" if(user.path(username)){
TypeError: user.path is not a function"
What does it means?
I am newbie.
Here is my user.js
var User = require('models/user').User;
var HttpError = require('error').HttpError;
var async = require('async');
exports.get = function(req, res) {
res.render('login', { title: 'Login'});
};
exports.post = function(req, res, next) {
var username = req.body.username;
var password = req.body.password;
async.waterfall([
function(callback) {
User.findOne({username: username}, callback);
},
function(user, callback) {
if (user) {
if (user.checkPassword(password)) {
callback(null, user);
} else {
next(new HttpError(403, "wrong password"));
}
} else {
var user = new User({username: username, password: password});
if(user.path(username)){
callback(null, user);
user.save(function(err) {
console.log(err.message)
if (err)
return next(err);
callback(user);
});
}else{ next(new HttpError(403, "Incorrect username"));
};
}
}
], function(err, user){
if (err) return next(err);
req.session.user = user._id;
res.send({});
});
and here is my login.js
var crypto = require('crypto');
var mongoose = require('lib/mongoose'),
Schema = mongoose.Schema;
var schema = new Schema({
username: {
type: String,
unique: true,
required: true
},
hashedPassword: {
type: String,
required: true
},
salt: {
type: String,
required: true
},
created: {
type: Date,
default: Date.now
}
});
schema.path('username').validate(function (value, respond) {
return /[0-9]{6,15}[a-zA-Z]/.test(value, function(){
respond(false, 'this message gets to the validation error');
});
}, '{VALUE} is not a valid login - [0-9]{6,15}[a-zA-Z]')
schema.methods.encryptPassword = function(password) {
return crypto.createHmac('sha1', this.salt).update(password).digest('hex');
};
schema.virtual('password')
.set(function(password) {
this._plainPassword = password;
this.salt = Math.random() + '';
this.hashedPassword = this.encryptPassword(password);
})
.get(function() { return this._plainPassword; });
schema.methods.checkPassword = function(password) {
return this.encryptPassword(password) === this.hashedPassword;
};
schema.path('username').validate(function (value) {
return /[0-9]{6,15}[a-zA-Z]/.test(value);
}, 'Invalid color');
exports.User = mongoose.model('User', schema);
You don't need to call anything before saving to vaildate, mongoose will do it for you. Also don't call callback until you are done.
Also check if error occur before doing console.log(err.message) because err is null if now error happens.
So this should work:
} else {
var user = new User({username: username, password: password});
user.save(function(err) {
if (err) {
console.log(err.message);
return next(err);
}
callback(user);
});
}