Passing objects and variables using `require` in `Node.js` - javascript

I am working on moving routing methods from app.js to separate files.
I managed to do it using (in app.js):
var userRoutes = require('./serverRoutes/userRoutes');
app.use('/userRoutes', userRoutes);
app.post('/user/login', userRoutes);
I can see that routing is being done.
I have some variables in my app.js like:
- tokens array
- jwtSecretKeystring
- jwt encryption module
- User which is mongoose schema
I would like to pass them and make them usable by routing methods inside userRoutes.js file.
I do not know how to do it.
Another question.
I also have some 'helper' type methods I would like to use in different 'routing' files.
I do not know how to pass them either.
This is how I deal with 'routing' files:
var express = require('express');
var router = express.Router();
router.post('/user/login', function (request, response) {
var email = request.body.email;
var password = request.body.password;
User.findOne({ email: email, password: password },
function (err, user) {
if (err)
response.send(err);
if (user) {
var expires = new Date();
expires.setDate((new Date()).getDate() + 5);
var token = jwt.encode({
email: email,
expires: expires
}, jwtTokenSecret);
tokens.push(token);
response.send(200, { access_token: token });
} else {
response.send(401, { message: "User not found" });
}
});
});
module.exports = router;
Thank you :-)

I am unsure what the "resolve" part of your title has to do with your question, but I can answer the rest of what you discuss in your question.
I have some variables in my app.js... I would like to pass them and
make them usable by routing methods inside userRoutes.js file.
The usual way to share some settings with another module is to pass them to that module in a module constructor or a module method:
var userRoutes = require('./serverRoutes/userRoutes')({
tokens: tokens,
jwtSecretKey: jwtSecretKey,
jwt: jwt,
User: User
});
Then, inside that userRoutes module:
var express = require('express');
var router = express.Router();
var jwtSecretKey, jwt, User, tokens;
router.post('/user/login', function (request, response) {
var email = request.body.email;
var password = request.body.password;
User.findOne({ email: email, password: password },
function (err, user) {
if (err)
response.send(err);
if (user) {
var expires = new Date();
expires.setDate((new Date()).getDate() + 5);
var token = jwt.encode({
email: email,
expires: expires
}, jwtSecretKey);
tokens.push(token);
response.send(200, { access_token: token });
} else {
response.send(401, { message: "User not found" });
}
});
});
// define module constructor
module.exports = function(options) {
// save data shared from parent module
jwtSecretKey = options.jwtSecretKey;
jwt = options.jwt;
User = options.User;
tokens = options.tokens;
return router;
};
I also have some 'helper' type methods I would like to use in
different 'routing' files. I do not know how to pass them either.
The usual way to share some common help functions is to put them in their own module and then you just require() in that module in any other module that you need to use them.

Related

How to set the token to local storage or cookies so that i can allow user to access certain web pages

I am trying to build an authentication system so, i used node , mysql,express for that so now i am simply saving and checking user exist in database can access but now i added JWT to it, so now i want this JWT token to store in localstorage or in cookies so, can someone guide me how can i do so
this is my authentication controller.js
var Cryptr = require('cryptr');
cryptr = new Cryptr('myTotalySecretKey');
var express = require('express');
const ap = express();
var jwt = require('jsonwebtoken');
var connection = require('./../config');
module.exports.authenticate = function (req, res) {
var email = req.body.email;
var password = req.body.password;
connection.query('SELECT * FROM users WHERE email = ?', [email], function (error, results, fields) {
if (error) {
res.json({
status: false,
message: 'there are some error with query'
});
} else {
if (results.length > 0) {
decryptedString = cryptr.decrypt(results[0].password);
if (password == decryptedString) {
jwt.sign({ email, password },
'secretkey',
{ expiresIn: '10days' },
(err, token) => {
console.log('token:' + token);
module.exports = token;
console.log(token);
res.redirect('/home.html');
}
);
} else {
res.redirect('/login.html');
console.log("Wrong Input");
}
}
else {
res.redirect('/login.html');
}
}
});
};
now i want to pass the token value to the local-storage or cookies so that i can restrict someone from acessing a page, i am reallly new to node js so any help would be appriciated
First I should notify you that do not put any secret things like password in jwt payload because the values of the payload could be accessed easily, you can try to copy paste a jwt in jwt.io site and see the payload.
set jwt in cookie like below, this will use express cookie method that does set Http Set-Cookie header:
res.cookie('jwt', generated_cookie)
.redirect('/home.html');
Also if you want to use localStorage you can set jwt in header and then in your code get the jwt from the header of login request and save it in localStorage and after that you should pass it as header in all other request, but this approach is a better solution for api calls like when you use react or vue ...
res.set({x-token: generated_token});
// In your code get
// get token from response
localStorage.setItem('token', token);
// now whenever calling api pass token as header
I show you one solution using jwt token, you choose another way:
Back-end file e.g. api.js
let jwt = require('jsonwebtoken')
let secret = 'yourSecret'; //secret key necessary to encode token
let Cryptr = require('cryptr');
let cryptr = new Cryptr('myTotalySecretKey');
module.exports = function(router,upload) {
function tokenAuth(req, res, next){
let token = req.body.token || req.body.query || req.headers['x-access-token']
if(token){
jwt.verify(token, secret, function(err,decoded){
if(err){
res.json({ authenticated: false, message:'Invalid token'})
} else {
req.decoded = decoded;
next()
}
})
} else {
res.json({success:false, message:'No token provided'});
}
}
router.post('/authenticate', function(req, res){
connection.query('SELECT * FROM users WHERE email = ?', [email], function (error, results, fields){
if(error) {
res.json({ success:false, message: err })
}
if(!results.length){
res.json({success:false, message:'User no found'})
} else if (results.length>0){
if(!req.body.password){
res.json({success:false, message:'Password was not provided'});
} else {
var validPassword = cryptr.decrypt(results[0].password);
if(validPassword === req.body.password){
res.json({success:false, message:'Incorrect password'})
} else {
var token = jwt.sign({username: results[0].username, email: results[0].email}, secret, {expiresIn: '24h'})
res.json({success:true, message:'You have logged in correctly!', token: token })
}
}
}
})
})
//If you want create a route for authenticated users for example comment posts, you can use our `tokenAuth function`
router.post('/post/comment',tokenAuth,function(req,res){
//access only for authenticated users
}
return router
}
This tokenAuth function we'll be use in paths restricted to authenticated users
server file e.g. server.js
const express = require('express');
const app = express();
const port = process.env.PORT || 80;
const http = require('http').Server(app);
const routes = require(path_to_api.js)(router);
app.use('/myApi', routes)
//***Here you should implement more details about your project such as routes, body parsers and other middlewares*****//
//Connect to your database
http.listen(port, ()=> console.log(`Server running on ${port}`))
Front-end file e.g. controller.js
function(login){
return fetch('/myApi/authenticate',{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(login)
}).then(result=>result.json()).then(data=> window.localStorage.setItem('token', data.token))
}
//`login` argument should be an object and should be like {username: 'user username', password: 'user password'}
In order to make a user store cookies, you can use the Set-Cookie header. From MDN:
Set-Cookie: <cookie-name>=<cookie-value>
In order to pass a header using Express, you can use res.set(), e.g. res.set("Set-Cookie", "Token=" + token). I also suggest you use the HttpOnly cookie directive, since it seems from your post that you don't access this token directly via Javascript and you simply want to check it when the client requests a webpage: res.set("Set-Cookie", "Token=" + token + "; HttpOnly").
The client will send the Cookie header to you when it requests a resource. You can check this header using req.header('Cookie'), and the output will be "Token=<token>" if the user is authenticated. You can then check this token for authenticity.

Save user decoded in database

I have a aplication where the user can take some pictures and send to the database, just as simple as that.
Everytime the user login he get a token, if everything fine with the token(he doesn't need to login).
I followed this tutorial to do the jwt authentication, now i want to check on every request except(/login / register) that token and decode it to get the user info ( i am just saving the username, its unique so its fine).
So imagine i am routing to /flower?flowerName (random route) so in this route i want to create a register and save in my database some data, but before that as i said, i should enter a middleware that checks the permission.
This is my middleware:
var jwt = require('jsonwebtoken');
var jwtConfig = require('../config/jwt');
module.exports = function(req, res, next) {
console.log("entered");
// check header or url parameters or post parameters for token
var token = req.body.token || req.query.token || req.headers['x-access-token'];
console.log(req.headers['x-access-token']);
// decode token
if (token) {
// verifies secret and checks exp
jwt.verify(token,jwtConfig.secret, function (err, decoded) {
if (err) {
return res.json({ success: false, message: 'Failed to authenticate token.' });
} else {
console.log("HEREE");
// if everything is good, save to request for use in other routes
req.decoded = decoded;
console.log(req.decoded);
next();
}
});
} else {
// if there is no token
// return an error
return res.status(403).send({
success: false,
message: 'No token provided.'
});
}
}
my problem is, how can i get the userID for my middleware and then save it in my next route? can i pass it trough the next? like next(userID)????
How can i get the parameter then.
this is where i save the register:
var express = require('express');
var User = require('../models').User;
var Foto = require('../models').Foto;
var router = express.Router();
var jwt = require('jsonwebtoken');
var fs = require('fs');
var fsPath = require('fs-path');
module.exports = {
sendPicture: function (req, res,next) {
var bitmap = new Buffer(req.body.base64, 'base64');
var dummyDate = "25/04/14-15:54:23";
var lat = req.params.lat;
var lon = req.params.lon;
var alt = req.params.alt;
var path = __dirname + "/../public/images/" + req.params.flowerName + "/example3.png";
var fotoPath = ""
var userId = 1;
console.log(lat);
console.log(lon);
console.log(alt);
console.log(req.query.token);
fsPath.writeFile(path, bitmap, function (err) {
if (err) {
console.log(err.stack);
return err;
}
Foto.create({
image: path,
userId: userId
}).then(function () {
return res.status(200).json({ message: "foto created" });
}).catch(function(err){
console.log(err.stack);
})
});
}
}
You should be able to pass any state variables through the whole chain through res.locals, i.e.
res.locals.decoded = decoded;
next();
you can find the details on res.locals here

Node.js + Express: file structure and communication

I have a login application but I am having trouble communicating the credentials of the form (email and password) to my existing .js files, where it performs some logic and retrieves some info.
On my login page I have a form with a POST method. Then, I have a main.js that performs the login:
main.js
module.exports = {
returnSessionToken: function(success, error) {
var email = email_X;
var pwd = password_Y;
[...]
function(error, response, body) {
var login = JSON.parse(body);
success(login.sessionToken)
}
And then I have a index.js where I retrieve some info of the logged user:
index.js
var authToken = require("./main");
authToken.returnSessionToken((result) => {
'my_website_token': result,
[...]
}
In my express project, I have these two files in a js folder. What I am trying to do is getting the email and password of the form of the login page and pass it to my main.js file (email_X and password_Y) and then call index.js. I know how to get the req.body.email and req.body.password in the routes folder, but stil can't figure out how to make these files communicate.
I have also tried to edit the app.js file into:
app.js
var login = require('./js/main');
and then,
app.use('/myaccount', login);
But no success.
Any help will be very much appreciated. Thanks!
I'm slightly confused by the details and what looks like some missing steps, but it looks like the problem may be caused by the way you're setting up and referencing your main.js module.
app.use('/myaccount', login) will send your main.js module two objects, generally referenced as req and res. req contains information about the http request. I believe the data from a form HTTP post is sent in req.body.
See example below:
index.js
var express = require('express');
var login = require('./login');
var app = express();
app.use('/login', login); // mount the sub app
login.js
var express = require('express');
var login = express();
login.post('/', function (req, res) {
console.log(req.body); // should print your form data
// do your login logic here
res.status(200).end(); // Sends http response back
});
module.exports = login;
For future reference, here's a working example. Thank you Ryan Villanueva for the help!
main.js
module.exports = {
returnSessionToken: function(email, pwd, success, fail) {
var email = email;
var pwd = pwd;
success(token)
[...]
}
index.js
module.exports = {
returnUserData: function(authToken, success) {
[...]
'my_website_token': authToken,
}
app.js
var myaccount = require('./routes/myaccount');
app.use('/myaccount', myaccount);
routes/myaccount.js
var express = require('express');
var router = express.Router();
var login = require('../js/main')
router.post('/', function(req, res) {
var email = req.body.email
var password = req.body.password
login.returnSessionToken(email, password, (token) => {
console.log("return token: ", token)
var logged = require('../js/index')
logged.returnUserData(token, (myData) => {
res.render('myaccount', { myData: myData });
})
},
(fail) => {console.log(fail)})
})
module.exports = router;

invalidate a validation from a nested schema virtual mongoose

I have my parent schema defined like this:
User.js:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var PasswordSchema = require('./Password');
var UserSchema = new Schema({
name: { type: String, required: true },
password: PasswordSchema
});
mongoose.model('User', UserSchema);
My children schema defined like this:
Password.js:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var crypto = require('crypto');
var PasswordSchema = new Schema ({
_id: false,
hashedPassword: { type: String, required: true },
salt: { type: String, default: '' }
});
var passwordRegex = /^(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9]).{8,24}$/;
PasswordSchema.virtual('password')
.set(function (password) {
if (passwordRegex.test(password))
{
this.invalidate('password', 'Invalid password format');
}
});
mongoose.model('Password', PasswordSchema);
module.exports = PasswordSchema;
Now I used these Models schema in my controller like this:
user.js:
require('../models/User');
var express = require('express');
var router = express.Router();
var mongoose = require('mongoose');
var User = mongoose.model('User');
var Password = mongoose.model('Password');
router.post('/register', function (req, res, next) {
var user = new User(req.body);
var password = new Password({ password: 'abcd1234' });
console.log(password.$__.validationError.errors['hashedPassword']); // Here it works I got the validation error
user.password = password;
user.password.$__.validationError = password.$__.validationError; // WORKAROUND
console.log(user.password.$__.validationError.errors['hashedPassword']); // Here it doesn't work no validation error anymore ...
user.save(function (err) {
if (err)
console.log(":(");
else
console.log(":)");
});
});
module.exports = router;
Question:
So my problem now is that no matter what password I send to my children virtual it doesn't invalidate the process. How could I invalidate the mongoose save action from a children virtual ? Is there an other better option ?
Question Updated:
In user.js why the variable password has the validation error and when I assign it to user.password I don't have the validation error anymore ? How can I correct it ?
** Update 2:**
I have found a workaround see user.js: I just assign the required property to generate validation error. But it looks really not clean is there another way?
Here is one good example https://gist.github.com/swaj/1350041, refactor it as below
PasswordSchema.virtual('password')
.get(function(){
return this._password;
})
.set(function (password) {
this._password = password;
// invoke crypto to hash and encrypt password, then assign it to hashedPassword
this.hashedPassword = password; // this is just for test
});
PasswordSchema.path('hashedPassword').validate(function(v) {
if (v) {
if (passwordRegex.test(v)) {
this.invalidate('password', 'Invalid password format');
}
}
if (!v) {
this.validate('password', 'password required');
}
}, null);
Test codes
var user = new User({name: 'dd'});
user.password = new Password({password: 'asdfASF123444'});
user.save(function (err) {
if (err)
console.log(err);
else
console.log("save user successfully");
});
Validation error is
{ [ValidationError: User validation failed]
message: 'User validation failed',
name: 'ValidationError',
errors:
{ password:
{ [ValidatorError: Invalid password format]
properties: [Object],
message: 'Invalid password format',
name: 'ValidatorError',
kind: 'user defined',
path: 'password',
value: undefined } } }
Per invalidate source code
Document.prototype.invalidate = function (path, err, val) {
if (!this.$__.validationError) {
this.$__.validationError = new ValidationError(this);
}
// ...
We know invalidate function belong to Document.
password.$__.validationError.errors['hashedPassword']
You define the validation for PasswordSchema, not in UserSchema. so user.password.$__.validationError.errors['hashedPassword'] is not valid.
Test your code with
var user = new User({name: 'dd'});
user.password = new Password({password: 'asdfwe32113'});
user.save(function (err) {
if (err)
console.log(err);
else
console.log("save user successfully");
});
Validation will be triggered, however, with this code
`user.password = new Password({hashedPassword: 'asdfwe32113'});`
This validation is NOT triggered.
Because for virtual field, only the correct virtual name field is updated then the .set function could be called.
Also please add those codes to virtual('password'), to make sure the hashedPassword could be set correctly.
if (passwordRegex.test(password)) {
this.invalidate('password', 'Invalid password format');
}else {
this.hashedPassword = password;
}
For the second question, require('../models/User'); must be invoked before mongoose.model() to make sure the User.js is parsed firstly, and the User could be added into mongoose.model in User.js. So in user.js could find this User model from mongoose. JavaScript is an interpreted programming language, so we should tell the JS engine the file parsed order in this way.

my serializeUser isn't being called during LocalStrategy done callback

My problem is that I'm getting the error "Error: Failed to serialize user into session". I'm confused, because I've set a serializeUser function, but it doesn't appear to be called (my console.log isn't being printed).
This is while I'm following the feathers passport tutorial: http://feathersjs.com/learn/authorization/
Note: my suspicion is that feathers-passport uses a different "passport" object than my own library. Unfortunately I have no idea how I would rememdy such an issue. It seems to me it's just horrendous design by Passport to not work by passing around instances, and instead attaching things to itsself directly.
I'm setting up passport for serialization and authentication using the following:
var LocalStrategy = require('passport-local').Strategy;
function GetPassport(userService, Passport) {
console.log('passport has been prepared.\n');
Passport.serializeUser(function(user, done) {
console.log('user: ', user);
done(null, user._id);
});
Passport.deserializeUser(function(id, done) {
userService.get(id, {}, done);
});
Passport.use(new LocalStrategy(function(username, password, done) {
userService.authenticate(username, password, done);
}));
return Passport;
}
module.exports = GetPassport;
Then I'm using:
var userService = UserService(config.db);
var passport = GetPassport(userService);
app.post('/login', passport.authenticate('local'));
If you need more details here is UserService:
var MongoDB = require('feathers-mongodb');
var Crypto = require('crypto');
var UserService = function(database) {
return MongoDB({
db: database,
collection: '_users',
}).extend({
authenticate: function(username, password, callback) {
this.find({query: {username: username}}, function(error, users) {
if(error)
callback(error);
var user = users[0];
if(!user)
return callback(new Error('No User Found'));
if(user.password !== hash(password, user.salt))
return callback(new Error('Password Is Incorrect'));
//success, return the authenticated user
return callback(null, user);
});
},
setup: function() {
this.before({
create: function(hook, next) {
//Create the salt
var salt = Crypto.randomBytes(128).toString('base64');
hook.data.salt = salt;
hook.data.password = hash(hook.data.password, hook.data.salt);
next();
},
});
},
});
};
module.exports = UserService;
function hash(string, salt) {
var shasum = Crypto.createHash('sha256');
shasum.update(string + salt);
return shasum.digest('hex');
}
The error trace:
Error: Failed to serialize user into session
at pass (/Users/funk/Development/Projects/generic_rest_server/node_modules/feathers-passport/node_modules/passport/lib/authenticator.js:277:19)
at Authenticator.serializeUser (/Users/funk/Development/Projects/generic_rest_server/node_modules/feathers-passport/node_modules/passport/lib/authenticator.js:295:5)
at IncomingMessage.req.login.req.logIn (/Users/funk/Development/Projects/generic_rest_server/node_modules/passport/lib/http/request.js:48:29)
at Strategy.strategy.success (/Users/funk/Development/Projects/generic_rest_server/node_modules/passport/lib/middleware/authenticate.js:228:13)
at verified (/Users/funk/Development/Projects/generic_rest_server/node_modules/passport-local/lib/strategy.js:83:10)
at /Users/funk/Development/Projects/generic_rest_server/user-service.js:22:24
at /Users/funk/Development/Projects/generic_rest_server/node_modules/feathers-mongodb/node_modules/mongoskin/node_modules/mongodb/lib/mongodb/cursor.js:158:16
at commandHandler (/Users/funk/Development/Projects/generic_rest_server/node_modules/feathers-mongodb/node_modules/mongoskin/node_modules/mongodb/lib/mongodb/cursor.js:651:16)
at /Users/funk/Development/Projects/generic_rest_server/node_modules/feathers-mongodb/node_modules/mongoskin/node_modules/mongodb/lib/mongodb/db.js:1670:9
at Server.Base._callHandler (/Users/funk/Development/Projects/generic_rest_server/node_modules/feathers-mongodb/node_modules/mongoskin/node_modules/mongodb/lib/mongodb/connection/base.js:382:41)
The answer was in my note.
This should be helpful for anyone else who gets stuck following:
"http://feathersjs.com/learn/authorization/"
You must provide the FeathersPassport call with the passport option. If not, feathers-passport well use a different version of passport, than the one you add serializeUser to:
app.configure(FeathersPassport(function(result) {
// MongoStore needs the session function
var MongoStore = ConnectMongo(result.createSession);
result.secret = 'noymysecret';
result.store = new MongoStore({
db: config.db,
});
result.resave = false;
result.saveUninitialized = false;
//*HERE*//
result.passport = passport;
//**//
return result;
}));
I blame Passport inexplicably being a singleton, for not noticing this sooner.

Categories

Resources