I am working on an authentication API system using NodeJS. The /Signup API endpoint is working fine, but the /authenticate is not. Everytime I call the /authenticate endpoint, I get the error message: 'Could not authenticate user' even when a valid user is provided;
Below is my code. Please tell me what I am doing wrong here
var express = require("express");
var mongoose = require("mongoose");
var User = require("../models/user");
module.exports = function (router) {
router.post('/signup', function (req,res) {
var user = new User();
user.local.username = req.body.username;
user.local.email = req.body.email;
user.local.password = req.body.password;
if (req.body.username == null || req.body.username == '' || req.body.email == null || req.body.email == '' || req.body.password == null || req.body.password == '') {
res.json({success:false, message:'Ensure username, email and password were provided'});
} else {
user.save(function (err, data) {
if (err) res.json({success:false, message:'Username or Email already exists!'});
// console.log(err.errors)
res.json({success:true, message:'New user created', data:data});
console.log(data)
});
}
})
router.post('/authenticate', function (req,res) {
User.findOne({username: req.body.username}).exec(function (err,user) {
if(err)
return res.send(err);
if (!user) {
res.json({success:false, message: 'Could not authenticate user'});
} else if(user){
var validPassword = user.comparePassword(req.body.password)
if (!validPassword) {
res.json({success:false, message: 'Could not authenticate password'});
} else{
res.json({success:true, message: 'User authenticated'});
}
}
});
});
}
EDIT
User Model:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var bcrypt = require('bcrypt-nodejs');
// define the schema for our user model
var userSchema = new Schema({
local : {
username : {type:String,unique:true,required:true, lowercase:true},
email : {type:String,unique:true,required:true, lowercase:true},
password : String
},
created_at : {type:Date, default:Date.now},
updated_at : {type:Date, default:Date.now}
});
userSchema.pre('save', function(next){
var user = this;
var now = new Date();
user.updated_at = now;
if(!user.created_at){
user.created_at = now
}
bcrypt.hash(user.local.password, null, null, function (err, hash) {
if(err) return next(err)
user.local.password = hash;
next(); })
});
// checking if password is valid
userSchema.methods.comparePassword = function(password) {
return bcrypt.compareSync(password, this.local.password); };
// create the model for users and expose it to our app
module.exports = mongoose.model('User', userSchema);
just saw the bug its you have your username inside the local.
router.post('/authenticate', function (req,res) {
User.findOne({'local.username': req.body.username}).exec(function (err,user) {
if(err)
return res.send(err);
else{
}
});
Related
I'm trying to make a login/sign up API with nodejs, express and mysql.
When testing it i don't get any errors and i get the "Succesful Sign Up!" message. When i check the database though, the user table is still empty.
Here's the query i'm trying to execute.
con.query("INSERT INTO user (unique_id, email, encrypted_password, salt, created_at, updated_at) VALUES (?,?,?,?,NOW(),NOW())",[uid, email, password, salt], function (err, result, fields) {
con.on('error', function (err) {
console.log('[MySQL ERROR]',err);
res.json('Resgister Error: ',err);
});
res.json('Succesful Sign Up!');
})
And here's the full code.
//Libraries
var crypto = require('crypto');
var uuid = require('uuid');
var express = require('express');
var mysql = require('mysql');
var bodyParser = require('body-parser');
//connection with MySQL
var con = mysql.createConnection({
host: "localhost",
user: "user",
password: "password",
database: "database",
});
//Encrypting password
var genRandomString = function (length) {
return crypto
.randomBytes(Math.ceil(length / 2))
.toString('hex')
.slice(0, length);
};
var sha512 = function (password, salt) {
var hash = crypto.createHmac('sha512', salt);
hash.update(password);
var value = hash.digest('hex');
return {
salt: salt,
passwordHash: value,
};
};
function saltHashPassword(userPassword) {
var salt = genRandomString(16);
var passwordData = sha512(userPassword, salt);
return passwordData;
}
var app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
//Sign up
app.post('/register/', (req, res, next) => {
var post_data = req.body;
var uid = uuid.v4();
var plaint_password = post_data.password;
var hash_data = saltHashPassword(plaint_password);
var password = hash_data.passwordHash;
var salt = hash_data.salt;
var email = post_data.email;
con.query("SELECT * FROM user where email=?", [email], function (err,result,fields) {
con.on('error', function (err) {
console.log('[MySQL ERROR]', err);
});
if (result && result.length)
res.json('User already exists');
else
{
con.query("INSERT INTO user (unique_id, email, encrypted_password, salt, created_at, updated_at) VALUES (?,?,?,?,NOW(),NOW())",[uid, email, password, salt], function (err, result, fields) {
con.on('error', function (err) {
console.log('[MySQL ERROR]',err);
res.json('Resgister Error: ',err);
});
res.json('Succesful Sign Up!');
})
}
});
})
//Login
app.post('/login/', (req, res, next) =>{
var post_data = req.body;
var user_password = post_data.password;
var email = post_data.email;
con.query("SELECT * FROM user where email=?", [email], function (err,result,fields) {
con.on('error', function (err) {
console.log('[MySQL ERROR]', err);
});
if (result && result.length)
{
var salt = result [0].salt;
var encrypted_password = result[0].encrypted_password;
var hashed_password = checkHashPassword(user_password,salt).passwordHash;
if(encrypted_password==hashed_password)
res.end(JSON.stringify(result[0]))
else
res.end(JSON.stringify('Wrong Credentials'))
}
else
{
res.json('Wrong Credentials')
}
});
})
app.listen(3000, () => {
console.log("RESTFul API running in port 3000");
});
Can try this and let me know if it works
con.query("INSERT INTO user (unique_id, email, encrypted_password, salt, created_at, updated_at) VALUES (?,?,?,?,NOW(),NOW())",[uid, email, password, salt], function (err, result, fields) {
if(err) {
console.log('[MySQL ERROR]',err);
res.json('Resgister Error: ',err);
}else {
res.json('Succesful Sign Up!');
}
})
I am currently implementing a Google login and Google calendar implementation and testing out a function when a Google user logs in it pulls their events and prints to the console. however, I am getting the following error in the console
Error: Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup.
and when I console.log the calendar, it gives me a null.
I have made sure Google+ API and Google calendar API are both enabled in the console as well.
I am new to web development in general, I followed a tutorial to get me started and began trying to use the Google API's. I am using the MEAN stack for this project. Here is the relevant code.
var FacebookStrategy = require('passport-facebook').Strategy;
var User = require('../models/user');
var session = require('express-session');
var jwt = require('jsonwebtoken');
var secret = 'check123';
var GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;
var gcal = require('google-calendar');
var google = require('googleapis');
module.exports = function(app,passport, auth) {
app.use(passport.initialize());
app.use(passport.session());
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: true,
cookie: { secure: false }
}));
passport.serializeUser(function(user, done) {
token = jwt.sign({ username: user.username, email: user.email }, secret, {expiresIn: '24h'});
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
passport.use(new FacebookStrategy({
clientID: '422080801499546',
clientSecret: '34f91021248106dea49daa111bb7ff89',
callbackURL: "http://localhost:8000/auth/facebook/callback",
profileFields: ['id', 'displayName', 'photos', 'email']
},
function(accessToken, refreshToken, profile, done) {
console.log(profile._json.email);
User.findOne({ email: profile._json.email }).select('username password email').exec(function(err, user){
if(err) done(err);
if (user && user !== null) {
done(null, user);
} else {
done(err);
}
});
//done(null, profile);
}
));
// Use the GoogleStrategy within Passport.
// Strategies in Passport require a `verify` function, which accept
// credentials (in this case, an accessToken, refreshToken, and Google
// profile), and invoke a callback with a user object.
passport.use(new GoogleStrategy({
clientID: '655984940226-ob15jvq3hvqha4969tlb3oco9tun1i9t.apps.googleusercontent.com',
clientSecret: 'hsZtBDsfiCvdPz0gT-tqdUZ_',
callbackURL: "https://frozen-woodland-75947.herokuapp.com/auth/google/callback"
},
function(accessToken, refreshToken, profile, done) {
User.findOne({ email: profile.emails[0].value }).select('username password email').exec(function(err, user){
if(err) done(err);
if (user && user !== null) {
done(null, user);
} else {
done(err);
}
});
var google_calendar = google.calendar('v3');
console.log('im here');
google_calendar.events.list({
auth: auth,
calendarId: 'primary',
timeMin: (new Date()).toISOString(),
singleEvents: true,
orderBy: 'startTime'
}, function(err, calendarList){
console.log(calendarList);
if(err){
console.log('bad list: ' + err);
return;
}
var events = calendarList.items;
if(events.length ==0){
console.log('No items found');
} else {
console.log('Upcoming events:');
console.log('%s -%s', start, event.summary);
}
})
}
Next is my authentication functions, my api.js:
var User = require('../models/user');
var jwt = require('jsonwebtoken');
var secret = 'check123';
module.exports = function(router){
//USER REGISTRATION
router.post('/users', function(req, res){
var user = new User();
user.username = req.body.username;
user.password = req.body.password;
user.email = req.body.email;
if(req.body.username == null || req.body.username == ''|| req.body.password == null || req.body.email == null || req.body.password == '' || req.body.email == ''){
res.json({ success: false, message: 'Ensure username, email, and password were provided'}); //false if the route is null empty etc.
} else {
user.save(function(err){
if(err) {
res.json({success: false, message: 'Username or email already exists'});
} else {
res.json({success: true, message: 'user created!'});
}
});
}
});
//USER LOGIN ROUTE
//http://localhost:port/api/authenticate
router.post('/authenticate', function(req,res){
User.findOne({ username: req.body.username }).select('email username password').exec(function(err,user){
if(err) throw err;
if(!user){
res.json({success: false, message: 'Could not authenticate user'});
} else if (user) {
if( req.body.password) {
var validPassword = user.comparePassword(req.body.password);
} else {
res.json({success: false, message: 'No password Provided'});
}
if(!validPassword) {
res.json({ success: false, message: ' Could not Authenticate password'});
} else {
var token = jwt.sign({ username: user.username, email: user.email }, secret, {expiresIn: '24h'}); // create json token when they successfully log in with secret encryption and expires in 24 hours
res.json({ success: true, message: ' User Authenticated!', token: token}); // respond to user with that token
}
}
});
});
router.use(function(req, res, next){
var token = req.body.token || req.body.query || req.headers['x-access-token'];
console.log(token);
if(token) {
//varify token
jwt.verify(token,secret,function(err,decoded){
if(err) {
res.json({success: false, message: 'Token invalid'});
} else {
req.decoded = decoded; //decrpyts token - user/email
next();
}
});
}else {
res.json({success: false, message: 'No token provided'});
}
});
router.post('/me', function(req, res) {
res.send(req.decoded);
});
return router;
}
Login controllers:
//controls main index - runs when main page loads
angular.module('mainController', ['authServices'])
.controller('mainCtrl', function(Auth,$timeout, $location,$rootScope, $window){
var app = this; // so we can access outside of scoper
app.loadme = false;
$rootScope.$on('$routeChangeStart', function() {
if(Auth.isLoggedIn()) {
app.isLoggedIn = true;
Auth.getUser().then(function(data){
app.username = data.data.username;
app.useremail = data.data.email;
app.loadme = true;
});
} else {
app.isLoggedIn = false;
app.username = '';
app.loadme = true;
}
});
this.google = function() {
$window.location = $window.location.protocol + '//' + $window.location.host + '/auth/google';
};
this.doLogin = function(loginData) { //when the register button is pressed controller
app.loading = true;
app.errorMsg = false;
Auth.login(app.loginData).then(function(data){
if(data.data.success){
app.loading = false;
app.successMsg = data.data.message + '...redirecting';
$timeout(function() {
$location.path('/about');
app.loginData = '';
app.successMsg = false;
}, 2000);
} else {
app.loading = false;
app.errorMsg = data.data.message;
}
});
};
this.logout = function() {
Auth.logout();
$location.path('/logout');
$timeout(function(){
$location.path('/');
}, 2000);
};
})
So I'm trying to build a very basic user login. I'm trying to create a user, then login with those credentials and get back a JSON Web Token. Where I'm stuck is trying to compare the passwords then send a response.
Steps:
Create User:
enter email and password
salt/hash user password
store user into database
return success
Login
find user by request email value
if found compare passwords
passwords good send JSON Web Token
User Model
email:{
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
}
User Routes
var express = require('express');
var router = express.Router();
var jwt = require('jsonwebtoken');
var bcrypt = require('bcryptjs');
// Create User
...
bcrypt.genSalt(10, function(err, salt) {
bcrypt.hash("superSecret", salt, function(err, hash) {
user.password = hash;
user.save();
res.json({success: true, message: 'Create user successful'});
});
});
...
// Login
...
bcrypt.compare(req.body.password, 'superSecret', function(err, res) {
if(req.body.password != user.password){
res.json({success: false, message: 'passwords do not match'});
} else {
// Send JWT
}
});
So the two problems here is that, I can't send a response nor can I compare the password. Just completely stuck on this, any help would be greatly appreciated.
As described in the doc, you should use bcrypt.compare like that:
bcrypt.compare(req.body.password, user.password, function(err, res) {
if (err){
// handle error
}
if (res) {
// Send JWT
} else {
// response is OutgoingMessage object that server response http request
return response.json({success: false, message: 'passwords do not match'});
}
});
And here is a nice post about Password Authentication with Mongoose (Part 1): bcrypt
//required files
const express = require('express')
const router = express.Router();
//bcryptjs
const bcrypt = require('bcryptjs')
//User modal of mongoDB
const User = require('../../models/User')
//Post request for login
router.post('/', (req, res) => {
//email and password
const email = req.body.email
const password = req.body.password
//find user exist or not
User.findOne({ email })
.then(user => {
//if user not exist than return status 400
if (!user) return res.status(400).json({ msg: "User not exist" })
//if user exist than compare password
//password comes from the user
//user.password comes from the database
bcrypt.compare(password, user.password, (err, data) => {
//if error than throw error
if (err) throw err
//if both match than you can do anything
if (data) {
return res.status(200).json({ msg: "Login success" })
} else {
return res.status(401).json({ msg: "Invalid credencial" })
}
})
})
})
module.exports = router
If we you to use bcryptjs in browser(HTML) then you can add bcryptjs CDN to do this.
CDN - https://cdn.jsdelivr.net/npm/bcryptjs#2.4.3/dist/bcrypt.js
Example-
HTML- (Add above CDN in tag)
JS-
var bcrypt = dcodeIO.bcrypt;
/** One way, can't decrypt but can compare */
var salt = bcrypt.genSaltSync(10);
/** Encrypt password */
bcrypt.hash('anypassword', salt, (err, res) => {
console.log('hash', res)
hash = res
compare(hash)
});
/** Compare stored password with new encrypted password */
function compare(encrypted) {
bcrypt.compare('aboveusedpassword', encrypted, (err, res) => {
// res == true or res == false
console.log('Compared result', res, hash)
})
}
If you want to do same in Nodejs
/** Import lib like below and use same functions as written above */
var bcrypt = require('bcryptjs')
From what I can see your logic is correct.
If you are using mongoose I suggest you to use the pre 'save' hook.
User Schema
userSchema.pre('save', function(next) {
// only hash the password if it has been modified (or is new)
if (!this.isModified('password')) {
return next();
}
// generate a salt
return bcrypt.genSalt(10, function(error, salt) {
if (error) {
return next(error);
}
// hash the password using the new salt
return bcrypt.hash(this.password, salt, function(error, hash) {
if (error) {
return next(error);
}
// override the cleartext password with the hashed one
this.password = hash;
return next();
});
});
});
userSchema.methods.comparePassword = function(passw, cb) {
bcrypt.compare(passw, this.password, function(err, isMatch) {
if (err) {
return cb(err, false);
}
return cb(null, isMatch);
});
};
And in your routes:
Login
...
return user.comparePassword(password, function(error, isMatch) {
var payload = {
iat: Math.round(Date.now() / 1000),
exp: Math.round((Date.now() / 1000) + 30 * 24 * 60),
iss: 'Whatever the issuer is example: localhost:3000',
email: user.email
};
var token = jwt.encode(payload, 'secret');
if (isMatch && !error) {
// if user is found and password is right create a token
return res.json({
success: true,
token: `JWT ${token}`,
user: user,
msg: 'Authentication was succesful'
});
}
return next({code: 401, msg: 'Password is incorrect'});
});
});
Create user
// Pre hook will take care of password creation
return user.save()
.then(function(user) {
var payload = {
iat: Math.round(Date.now() / 1000),
exp: Math.round((Date.now() / 1000) + 30 * 24 * 60),
iss: 'Whatever the issuer is example: localhost:3000',
email: user.email
};
var token = jwt.encode(payload, 'secret');
return res.status(201).json({user, token: `JWT ${token}`, msg: 'User was succesfully created'});
})
.catch((err) => next(err));
bcrypt.compare(req.body.password, user.password, function(err, results){
if(err){
throw new Error(err)
}
if (results) {
return res.status(200).json({ msg: "Login success" })
} else {
return res.status(401).json({ msg: "Invalid credencial" })
}
})
const bcrypt = require("bcryptjs");
const salt = bcrypt.genSaltSync(10);
const hashPassword = (password) => bcrypt.hashSync(password, salt);
const comparePassword = (password, hashedPassword) =>
bcrypt.compareSync(password, hashedPassword);
bcrypt.compare(req.body.password, user.password)
.then(valid => {
if (!valid) {
return res.status(401).json({ message: 'Paire login/mot de passe incorrecte' });
}
res.status(200).json({
userId: user._id,
token:jwt.sign(
{userId: user._id},
process.env.ACCESS_TOKEN_SECRET_KEY,
{expiresIn:'24h'}
),
message: 'connected'
});
})
.catch(error => res.status(500).json({ error }));
enter code here
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);
});
}
I'm trying to test my local-login. I've implemented with passport.js, following its guide and following this MEAN skeleton.
I'm pretty sure that the implementation is fine, but there is something wrong with the test that always fails authentication.
If authentication fails it should be redirect to "/signin"if authentication is correct it should be go to "/"
But when I test, the authentication always fails.
This is routes.js:
module.exports = function(app, passport, auth) {
var users = require('../app/controllers/users');
app.get('/signin', users.signin);
app.post('/users/session', passport.authenticate('local', {
failureRedirect: '/signin',
failureFlash: 'Invalid email or password.'
}), users.session);
var index = require('../app/controllers/index');
app.get('/', index.render);
passport.js:
var mongoose = require('mongoose'),
LocalStrategy = require('passport-local').Strategy,
User = mongoose.model('User'),
config = require('./config');
module.exports = function(passport) {
//Serialize sessions
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findOne({
_id: id
}, '-salt -hashed_password', function(err, user) {
done(err, user);
});
});
//Use local strategy
passport.use(new LocalStrategy({
usernameField: 'email',
passwordField: 'password'
},
function(email, password, done) {
User.findOne({
email: email
}, function(err, user) {
if (err) {
return done(err);
}
if (!user) {
return done(null, false, {
message: 'Unknown user'
});
}
if (!user.authenticate(password)) {
return done(null, false, {
message: 'Invalid password'
});
}
return done(null, user);
});
}
));
}
and test: api.js:
var User, app, mongoose, request, server, should, user;
should = require("should");
app = require("../server");
mongoose = require("mongoose");
User = mongoose.model("User");
request = require("supertest");
server = request.agent("http://localhost:3000");
describe("<Unit Test>", function() {
return describe("API User:", function() {
before(function(done) {
user = new User({
email : "user#user.com",
firstName: "Full Name",
lastName : "Last Name",
password : "pass11"
});
user.save();
return done();
});
describe("Authentication", function() {
return it("Local login", function(done) {
return server.post("/users/session").send({
email : "user#user.com",
password: "pass11"
}).end(function(err, res) {
res.headers.location.should.have.equal("/");
return done();
});
});
});
return after(function(done) {
User.remove().exec();
return done();
});
});
});
This is what the terminal displays:
<Unit Test>
API User:
Authentication
1) Local login
0 passing (24ms)
1 failing
1) <Unit Test> API User: Authentication Local login:
actual expected
/signin
I'm trying using this code and work as expected
var User, app, mongoose, request, server, should, user, agent;
should = require("should");
app = require("../server");
mongoose = require("mongoose");
User = mongoose.model("User");
request = require("supertest");
agent = request.agent(app)
describe('User', function () {
before(function(done) {
user = new User({
email : "user#user.com",
firstName: "Full Name",
lastName : "Last Name",
password : "pass11"
});
user.save(done)
});
describe('Login test', function () {
it('should redirect to /', function (done) {
agent
.post('/users/session')
.field('email', 'user#user.com')
.field('password', 'pass11')
.expect('Location','/')
.end(done)
})
after(function(done) {
User.remove().exec();
return done();
});
})
})
for more reference check this test-user
here my screenshot