Cannot add jwt token to node.js response - javascript

I'm new to javascript ecosystem and want to add jwt token to response out of this registration router:
router.post('/register', (req, res)=> {
User.findOne({email: req.body.email})
.then(user => {
if(user) {
return res.status(400).json({error: 'Email already exists'});
} else {
const newUser = new User({
username: req.body.username,
email: req.body.email,
password: req.body.password
});
bcrypt.genSalt(10, (err, salt)=> {
bcrypt.hash(newUser.password, salt, (err, hash)=> {
if (err) throw err;
newUser.password = hash;
newUser.save()
.then(user => res.status(200).json(user)) //<=Problem is here
.catch(err => console.log(err));
} )
})
}
})
});
The jwt snippet (which works fine on longin router) is this:
const payload = {
username: user.username
}
//sign token
jwt.sign(
payload,
keys.secretOrKey,
{ expiresIn: 3600},
(err, token)=> {
res.json({
success: true,
token: 'Bearer '+ token,
username: username
});
});
The problem is that I don't know how can I add the snippet to the response header.
When I add it after .then(user => I get a SyntaxError: Unexpected token const error.
How can I make it?

Sounds like you didn't wrap the jwt snippet within curly braces. Without them the arrow function where the problem appears only takes one expression. Paste the jwt snippet into the following snippet instead.
bcrypt.genSalt(10, (err, salt)=> {
bcrypt.hash(newUser.password, salt, (err, hash)=> {
if (err) throw err;
newUser.password = hash;
newUser.save()
.then(user => {
res.status(200).json(user);
<JWT_Snippet_here>
}
.catch(err => console.log(err));
})
})
Here you can see how the syntax of arrow functions is defined. The following quote shows the most important part.
(param1, param2, …, paramN) => { statements }
(param1, param2, …, paramN) => expression
Curly braces are needed in order to be able to use a list of statements. The error you experienced occurred because your JavaScript engine expected a single expression but instead found a list of statements.

Related

TypeError: cb is not a function

So when I try to register using this code
router.post(
"/register",
catchAsync(async (req, res, next) => {
const { username, password, email, number, address } = req.body.user;
const user = new User({
email,
username,
number,
address,
isVerified: false,
});
const registredUser = await User.register(user, username, password);
req.login(registredUser, (err) => {
if (err) {
return next(err);
}
console.log(registredUser);
req.flash("success", "Dobro došli na About Women!");
res.redirect("users/confirm");
});
})
);
it flashes error in the title. I tried doing everything even referencing to my old code but none of it worked. This is log I get from console:
node_modules\passport-local-mongoose\index.js:247
promise.then((result) => cb(null, result)).catch((err) => cb(err));
^
TypeError: cb is not a function
How can I fix it? Also I am using passport for user registration.
That error message points to line 247 of index.js in the passport-local-mongoose package. See https://github.com/saintedlama/passport-local-mongoose/blob/main/index.js#L247
That is in the register function:
schema.statics.register = function (user, password, cb) {
The arguments are expected to be user, password, and callback. You are passing user, username, and password, so at line 247 it is trying to use the password as a callback function, which fails with the error message you noted.

Can I create a user object inside password hash function?

I was following a Backend REST Api tutorial, and in the video, this is what he did, creating a user object, then changing newUser.password to the hash generated.
// Data is valid, register user
let newUser = new User({
name,
username,
password,
email,
});
// Hash password
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(newUser.password, salt, (err, hash) => {
if (err) throw err;
newUser.password = hash;
newUser.save().then(user => {
return res.status(201).json({
success: true,
msg: "User is now registered"
})
})
})
})
Why not just do it all at once?
// Why not do it in one go instaed of creating and then changing User?
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(password, salt, (err, hash) => {
if (err) throw err;
let newUser = new User({
name,
username,
hash,
email,
});
newUser.save().then(user => {
return res.status(201).json({
success: true,
msg: "User is now registered"
})
})
})
})
Is there something wrong with doing it together?
since bcrypt takes a callback function your hash is only gonna be available between the brackets for the callback function, which is why you do the assignment between those brackets. since you declare newuser between those brackets then newuser isn't available in the greater scope

Mongodb error: Cannot set headers after they are sent to the client

I am basically creating a user registration form where I check if the submitted passwords match and then check if the user already exists by querying the collection to see if the submitted username or email already exist. If all of the data passed the checks then I create I new user. My issue is that if the username or email already exist then the user is still created. Shouldn't returning a status if a user is found stop the function?
Front end submission:
submitNewUser() {
axios.post('http://localhost:5000/api/settings/users', {
name: this.newUser.name,
username: this.newUser.username,
email: this.newUser.email,
password: this.newUser.password,
confirm_password: this.newUser.confirm_password,
role: this.newUser.role
})
.then(() => {
this.getUsers();
})
.catch(error => {
console.log(error);
})
}
The user has to be signed in to create a new user so I user passport check if token contains a valid user
passport authentication check:
const JwtStrategy = require('passport-jwt').Strategy;
const ExtractJwt = require('passport-jwt').ExtractJwt;
const User = require('../models/User');
const key = require('./keys').secret;
const opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
opts.secretOrKey = key;
module.exports = passport => {
passport.use(
new JwtStrategy(opts, (jwt_payload, done) =>{
User.findById(jwt_payload._id).then(user => {
if (user) return done(null, user);
return done(null, false);
}).catch(err => {
console.log(err);
});
})
);
};
End route:
router.post('/users', passport.authenticate('jwt', {
session: false
}), (req, res) => {
let companyId = req.user.company_id;
let {
name,
username,
email,
password,
confirm_password,
role,
} = req.body;
//Check that passwords match
if( password !== confirm_password ) {
return res.status(400).json({
msg: "Passwords do not match"
})
}
//Check for unique username
User.findOne({ username: username })
.then(user => {
console.log('username')
if(user) {
return res.status(400).json({
msg: "Username is already taken."
});
}
})
.catch(error => console.log(error));
//check for unique email
User.findOne({ email: email })
.then(user => {
console.log('email')
if(user) {
return res.status(400).json({
msg: "Email is already registered. Did you forget your password?"
});
}
})
.catch(error => console.log(error));
let newUser = new User({
name,
username,
password,
email,
user_role: role,
company_id: companyId,
});
// Hash password
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(newUser.password, salt, (err, hash) => {
if(err) throw err;
newUser.password = hash;
newUser.save()
.then(user => {
return res.status(201).json({
success: true,
msg: "User is now registered."
});
})
.catch(error => console.log(error));
});
});
});
This is an error that I get if the user already exists:
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at ServerResponse.setHeader (_http_outgoing.js:482:11)
at ServerResponse.header (C:\Users\Reece\OneDrive\Desktop\Fyber Docs\Valentis-Pipeline-MEVN-App\node_modules\express\lib\response.js:771:10)
at ServerResponse.send (C:\Users\Reece\OneDrive\Desktop\Fyber Docs\Valentis-Pipeline-MEVN-App\node_modules\express\lib\response.js:170:12)
at ServerResponse.json (C:\Users\Reece\OneDrive\Desktop\Fyber Docs\Valentis-Pipeline-MEVN-App\node_modules\express\lib\response.js:267:15)
at User.findOne.then.user (C:\Users\Reece\OneDrive\Desktop\Fyber Docs\Valentis-Pipeline-MEVN-App\server\routes\api\settings.js:106:40)
at processTicksAndRejections (internal/process/next_tick.js:81:5)
Due to the async nature, (req, res) => { is returning before any of your calls to mongodb are either succeeding or failing, yet you still use the res object to send a response back to the client.
I don't know exactly how to deal with this in the framework you're using, but the framework appears to populate res's header with data and send it back to the client before you start to modify the res object.

js ':' Expected node js

While trying to register user using nodejs and mongoose i get an error saying [js] ':' Expected on the dot notation at User.findOne(email: res.body.email).
I tried this
User: User.findOne(...)
but it raises the following error at runtime when sending a post request from postman
(node:13952) UnhandledPromiseRejectionWarning: ReferenceError: body is not defined
at User.User.findOne.then.user (C:\Users\Aman\Desktop\qwerty\routes\api\users.js:14:29)
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:118:7)
(node:13952) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing ins
ide of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejectio
n id: 1)
this is my code
const User = require("../../Models/User");
router.post("/register", (req, res) => ({
User.findOne({ email: req.body.email }).then(user => {
if (user) {
show email registered before message
} else {
do something
});
const newUser = new User({
name: req.body.name,
email: req.body.email,
avatar: req.body.avatar,
password: req.body.password
});
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(newUser.password, salt, (err, hash) => {
newUser.password = hash;
newUser
.save()
});
});
}
})
}));
Remove the parentheses outside the body of the function (req, res) =>. It should look like this:
router.post("/register", (req, res) => {
User.findOne({ email: req.body.email })
// other code inside
});
() => ({}) will expect to return an object literal expression, e.g. JSON object. () => {} will execute the statements inside function body.
Read more at MDCN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions#Syntax
In an arrow function, the syntax you are using here
(req, res) => ({})
returns an object.
const foo = () => ({foo: 'bar'});
console.log(foo());
It's a shorthand for
const foo = () => {
return {
foo: 'bar'
}
};
console.log(foo());
So you either need to fix your code to really return a valid object, or remove ({ at the beginning, and the }) at the end of your function
router.post("/register", (req, res) => {
User.findOne({ email: req.body.email })
// ...
});

Passport facebook strategy throw Data must be string using Node.js

I'm using Node.js and passport facebook strategy to log client in app.
I followed the passport docs but still have an error: Data must be a string or a buffer.
Strategy redirects to facebook page very well, but after test accepts app's conditions and it redirects to homepage, the app throw this error:
StatusCodeError: 500 - {"error":"Data must be a string or a buffer"}
This is my code from auth.js where strategy is written. I'm using jsonwebtoken module to sign user id.
exports.facebookStrategy = new FacebookStrategy({
clientID: config.auth.facebook.clientID,
clientSecret: config.auth.facebook.clientSecret,
callbackURL: config.auth.facebook.callbackURL,
profileFields: ['id', 'displayName', 'email']
}, function (accessToken, refreshToken, profile, done) {
var userProfile = {
username: profile._json.id,
name: profile._json.name,
email: profile._json.email,
facebook: true
}
findOrCreate(userProfile, (err, user) => {
if (err) {
return done(err);
}
// use token lib to sign id
jwt.sign({ userId: user.username }, config.secret, {}, (e, token) => {
if (e) {
return done(e);
}
user.token = token;
return done(null, user);
})
});
function findOrCreate (user, callback) {
client.getUser(user.username, (err, usr) => {
if (err) {
return client.saveUser(user, callback);
}
callback(null, usr);
})
}
});
Using a console.log I figured out that error comes from this code of block:
...
findOrCreate(userProfile, (err, user) => {
if (err) {
console.log(err.message); // it returns 500 - {"error":"Data must be a string or a buffer"}
return done(err);
}
I tried to change profile._json to profile._raw. However all values are undefined.
I'm using Node 6.10.0 version. and passport: "^0.3.2", "passport-facebook": "^2.1.1".
How can I solve this error?
This error ocurr when function to create password encrypted the parameter is null. By example:
const crypto = require('crypto')
let shasum = crypto.createHash('sha256')
shasum.update(password) // password is null
This authentication method is not require password you must code a conditional to prevent the encryption.

Categories