This is my code from routes file(users.js)
User.findOne({linkedin_id: req.body.linkedin_id}, function(err, linkedinUser) {
if(err) {
console.log('err in finding linkedin user '+err);
}
// if user exits
else if(linkedinUser) {
console.log('user exist');
const token = jwt.sign(linkedinUser, config.secret, {expiresIn: 604800});
res.json({success: true, token: 'JWT '+token, user: {
id: linkedinUser._id,
linkedin_id: linkedinUser.linkedin_id,
name: linkedinUser.name,
username: linkedinUser.username,
email: linkedinUser.email,
lkprofilePic: linkedinUser.profilePic
}, msg: 'user exits'
});
}
// if user doesn't exist
else {
User.create({
linkedin_id: req.body.linkedin_id,
name: req.body.name,
username: req.body.username,
email: req.body.email,
lkprofilePic: req.body.lkprofilePic
}, function(err, result) {
if(err) {
res.json({success: false, msg: 'failed to add'})
console.log('error in adding the data '+err);
}
else if(result) {
const token = jwt.sign(linkedinUser,config.secret,{ expiresIn: 604800 });
res.json({success: true, token: 'JWT '+token, user: {
id: result._id,
linkedin_id: result.linkedin_id,
name: result.name,
username: result.username,
email: result.email,
lkprofilePic: result.profilePic
}, msg: 'User added ' });
}
});
}
});
This from the config -> secret
module.exports = {
secret: 'bigfish'
}
This is the error I'm getting in the nodejs console
Receiving linkedin data
D:\product\project-1\node_modules\mongodb\lib\utils.js:132
throw err;
^
Error: Expected "payload" to be a plain object.
at validate (D:\product\project-1\node_modules\jsonwebtoken\sign.js:34:11)
at validatePayload (D:\product\project-1\node_modules\jsonwebtoken\sign.js:56:10)
at Object.module.exports [as sign] (D:\product\project-1\node_modules\jsonwebtoken\sign.js:108:7)
at D:\product\project-1\routes\users.js:415:29
at Function. (D:\product\project-1\node_modules\mongoose\lib\model.js:4177:16)
at parallel (D:\product\project-1\node_modules\mongoose\lib\model.js:2230:12)
at D:\product\project-1\node_modules\mongoose\node_modules\async\internal\parallel.js:35:9
at D:\product\project-1\node_modules\mongoose\node_modules\async\internal\once.js:12:16
at iteratorCallback (D:\product\project-1\node_modules\mongoose\node_modules\async\eachOf.js:52:13)
at D:\product\project-1\node_modules\mongoose\node_modules\async\internal\onlyOnce.js:12:16
at D:\product\project-1\node_modules\mongoose\node_modules\async\internal\parallel.js:32:13
at apply (D:\product\project-1\node_modules\lodash_apply.js:15:25)
at D:\product\project-1\node_modules\lodash_overRest.js:32:12
at callbackWrapper (D:\product\project-1\node_modules\mongoose\lib\model.js:2199:11)
at D:\product\project-1\node_modules\mongoose\lib\model.js:4177:16
at model.$__save.error (D:\product\project-1\node_modules\mongoose\lib\model.js:359:7)
But the data is getting saved in the database & doesn't return the
res.json({success: true, token: 'JWT '+token, user: {
id: result._id,
linkedin_id: result.linkedin_id,
name: result.name,
username: result.username,
email: result.email,
lkprofilePic: result.profilePic
}, msg: 'User added ' });
The issue is with the way you signed your token
The user you are using is a returned user from mongoose so you will need to use YOUR_USER.toJSON. if the user is not coming from mongoose use JSON.stringify(YOUR_USER) instead
change your code to either
const token = jwt.sign({linkedinUser}, config.secret, {expiresIn: 604800});
//if you want to set expiration on the token
OR
const token = jwt.sign(linkedinUser.toJSON(), config.secret);
//if you just want to sign the token without setting the expiration
const token=jsonwebtoken.sign(user.toJSON(),config.secret,{expiresIn:30});
add .toJSON() with your object then it will be ok
Related
I am having issues with fetching from my api, and I keep getting the aforementioned error and have no clue what is going wrong. Essentially what I'm trying to do is create a user, then get the token returned to me to create another profile.
I'm not completely sure whether it's an issue with the front end or back end, and don't know how to determine if it is one or the other. Here's the code in the front end:
let response;
await fetch('https://herokuapiurl.com/api/users', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
'name': name,
'email': email,
'password': password,
'phoneNumber': phoneNumber,
'userName': userName,
'address': address
})
})
.then(msg => {
response = msg.json()
return response
})
.then(msg => console.log(JSON.stringify(msg) + ' This is part of the .then()'))
fetch('https://apiurl.com/api/profiles', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
'x-auth-token': response
},
body: JSON.stringify({
'name': name,
'email': email,
'password': password,
'phoneNumber': phoneNumber,
'userName': userName,
'address': address
})
}
)
.then(msg => msg.json())
.then(msgJSON => console.log(msgJSON + ' this fired'))
.catch((error) => console.log(error))
Any help would be much appreciated. Thanks.
EDIT
This is the our route on our api that is called for registering a user:
router.post(
"/",
[
check("name", "name is required").not().isEmpty(),
check("email", "Please inclue a valid email").isEmail(),
check(
"password",
"Please enter a password with 6 or more characters"
).isLength({ min: 1 }),
check("phoneNumber", "Phone Number is required").isMobilePhone(),
check("address", "address is required").not().isEmpty(),
check("userName", "Username is required").not().isEmpty(),
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const { name, email, password, phoneNumber, userName, address } = req.body;
try {
let user = await User.findOne({ userName });
if (user) {
return res
.status(400)
.json({ errors: [{ msg: "User already exists" }] });
}
console.log(userName);
//get users gravitar
const avatar = gravatar.url(email, {
s: "200",
r: "pg",
d: "mm",
});
user = new User({
name,
email,
password,
phoneNumber,
userName,
address,
gravatar: avatar,
});
const salt = await bcrypt.genSalt(10);
user.password = await bcrypt.hash(password, salt);
await user.save();
const payload = {
user: {
id: user.id,
},
};
jwt.sign(
payload,
config.get("jwtSecret"),
{ expiresIn: 360000 },
(err, token) => {
if (err) throw err;
res.json({ token });
}
);
console.log(userName);
//res.send("User registered");
} catch (err) {
console.error(err.message);
res.status(500).send("Server Error");
}
}
);
module.exports = router;
Turns out the issue was with the backend. It required unique emails, and existing emails from past registration testing was causing the back end to act unpredictably.
User.findOne({ email }, (err, user) => {
if(err){
console.log(err);
return res.status(400).json({
message: "USER doe's not exist"
})
}
if (!user.authenticate(password)){
return res.status(401).json({
message: "password is wrong"
})
}
var token = jwt.sign({ _id: user._id }, process.env.SECRET);
res.cookie("token", token, { expires: new Date(Date.now() + 900000)});
const { _id, name, email, role } = user;
res.json({ token, user: { _id, name, email, role }});
})
//POST request that I am giving from postman application
{
"email": "notavailable#gmail.com", //not available in database
"password": "shubham#17"
}
Why i am not getting the err property while I am giving a WRONG POST request. instead of giving the err property, my app is going to crash.
events.js:287
throw er; // Unhandled 'error' event
^
TypeError: Cannot read property 'authenticate' of null
at C:\Users\Shubham Ghosh\Desktop\MERN\projBack\controller\auth.js:45:19
at C:\Users\Shubham Ghosh\Desktop\MERN\projBack\node_modules\mongoose\lib\model.js:4846:16
at C:\Users\Shubham Ghosh\Desktop\MERN\projBack\node_modules\mongoose\lib\model.js:4846:16
at C:\Users\Shubham Ghosh\Desktop\MERN\projBack\node_modules\mongoose\lib\helpers\promiseOrCallback.js:24:16
at C:\Users\Shubham Ghosh\Desktop\MERN\projBack\node_modules\mongoose\lib\model.js:4869:21
at C:\Users\Shubham Ghosh\Desktop\MERN\projBack\node_modules\mongoose\lib\query.js:4424:11
at C:\Users\Shubham Ghosh\Desktop\MERN\projBack\node_modules\kareem\index.js:135:16
at processTicksAndRejections (internal/process/task_queues.js:79:11)
Emitted 'error' event on Function instance at:
at C:\Users\Shubham Ghosh\Desktop\MERN\projBack\node_modules\mongoose\lib\helpers\promiseOrCallback.js:24:16
at C:\Users\Shubham Ghosh\Desktop\MERN\projBack\node_modules\mongoose\lib\model.js:4869:21
at C:\Users\Shubham Ghosh\Desktop\MERN\projBack\node_modules\mongoose\lib\query.js:4424:11
at C:\Users\Shubham Ghosh\Desktop\MERN\projBack\node_modules\kareem\index.js:135:16
at processTicksAndRejections (internal/process/task_queues.js:79:11)
Emitted 'error' event on Function instance at:
at C:\Users\Shubham Ghosh\Desktop\MERN\projBack\node_modules\mongoose\lib\model.js:4848:13
at C:\Users\Shubham Ghosh\Desktop\MERN\projBack\node_modules\mongoose\lib\helpers\promiseOrCallback.js:24:16
[... lines matching original stack trace ...]
at processTicksAndRejections (internal/process/task_queues.js:79:11)
[nodemon] app crashed - waiting for file changes before starting...
In addition to checking err, you also have to check if (user) to see if you actually got a user object back. With that database, there is a middle ground where you didn't get an error, but no user was found either. You need to check both of those conditions. Or said another way, you get err when there's something wrong with the database. You can user === null when there's nothing wrong with the database, but the query didn't find a matching user. So, you can check for both of those like this:
User.findOne({ email }, (err, user) => {
if (err) {
console.log(err);
return res.status(500).json({message: "database error"});
}
if(!user){
return res.status(400).json({
message: "USER does not exist"
})
}
if (!user.authenticate(password)){
return res.status(401).json({
message: "password is wrong"
})
}
var token = jwt.sign({ _id: user._id }, process.env.SECRET);
res.cookie("token", token, { expires: new Date(Date.now() + 900000)});
const { _id, name, email, role } = user;
res.json({ token, user: { _id, name, email, role }});
})
Try to do something like this, it might help.
User.findOne({ email }, (user) => {
if(!user){
return res.status(400).json({
message: "USER doe's not exist"
})
}
if (!user.authenticate(password)){
return res.status(401).json({
message: "password is wrong"
})
}
var token = jwt.sign({ _id: user._id }, process.env.SECRET);
res.cookie("token", token, { expires: new Date(Date.now() + 900000)});
const { _id, name, email, role } = user;
res.json({ token, user: { _id, name, email, role }});
})
//POST request that I am giving from postman application
{
"email": "notavailable#gmail.com", //not available in database
"password": "shubham#17"
}
**I will suggest you to work with promises so you can handle the errors easily.
so you can do this, using promise to catch the err.: (you can see i did .then(user => {//here the function if no errors}).catch(err=>console.log(err)
User.findOne({ email }).then(user => {
if (!user) {
return res.status(400).json({
message: "USER doe's not exist"
})
}
if (!user.authenticate(password)) {
return res.status(401).json({
message: "password is wrong"
})
}
var token = jwt.sign({ _id: user._id }, process.env.SECRET);
res.cookie("token", token, { expires: new Date(Date.now() + 900000) });
const { _id, name, email, role } = user;
res.json({ token, user: { _id, name, email, role } });
}).catch(err => console.log(err))
//POST request that I am giving from postman application
{
"email": "notavailable#gmail.com", //not available in database
"password": "shubham#17"
}
I have small project to introduce myself to some front-end technologies. I am using Node, Express, Pug, and MongoDB.
I define the user schema in my user.js file:
var userSchema = mongoose.Schema({
username : String,
password : String,
jobs : [{ type: mongoose.Schema.Types.Mixed }]
});
Then, in my passport.js file I start the sign up process.
User.findOne({ 'username' : username }, function(err, user) {
// if there are any errors, return the error
if (err) {
console.log(err);
return done(err);
}
// check to see if theres already a user with that email
if (user) {
console.log('user exists');
return done(null, false, req.flash('signupMessage', 'That username is already taken.'));
} else {
console.log('creating new user...');
// if there is no user with that email
// create the user
var newUser = new User();
newUser.username = username;
newUser.password = newUser.generateHash(password);
newUser.jobs = [{ website: 'google.com' }];
// save the user
newUser.save(function(err) {
if (err) {
console.log(err);
throw err;
}
console.log('user saved: ', newUser);
return done(null, newUser);
});
}
});
The post successfully saves the new user as:
{
"_id": {
"$oid": "5967d2acc64d953330a3ac32"
},
"__v": 0
}
My goal is to have an array in the database where website links can be pushed into the array for that user.
Thanks for any assistance.
Set the jobs field type as array of Mixed:
var userSchema = mongoose.Schema({
local: { username : String, password : String },
jobs: [ { type: mongoose.Schema.Types.Mixed } ]
});
Then create the user passing all parameters to the constructor:
var newUser = new User({
local: {
username: username,
password: User.generateHash(password),
},
jobs: [{ website: 'google.com' }]
});
// save the user
newUser.save(function(err) {
if (err) {
console.log(err);
throw err;
}
console.log('user saved: ', newUser);
return done(null, newUser);
});
You could also create the user without instantiating it first:
// save the user
User.create({
local: {
username: username,
password: User.generateHash(password),
},
jobs: [{ website: 'google.com' }]
}, function(err, newUser) {
if (err) {
console.log(err);
throw err;
}
console.log('user saved: ', newUser);
return done(null, newUser);
})
For both these methods you will probably need to make the generateHash a static method.
I have a sails app. I was trying to implement Facebook Login. When I click on the Login with facebook button i am getting this error:
error: A server error occurred in a request:
error: FacebookTokenError: This authorization code has been used.
Full error log looks like this:
error: A server error occurred in a request:
error: FacebookTokenError: This authorization code has been used.
at Strategy.parseErrorResponse (/home/node_modules/passport-facebook/lib/strategy.js:198:12)
at Strategy.OAuth2Strategy._createOAuthError (/home/node_modules/passport-facebook/node_modules/passport-oauth2/lib/strategy.js:341:16)
at /home/node_modules/passport-facebook/node_modules/passport-oauth2/lib/strategy.js:166:45
at /home/node_modules/passport-facebook/node_modules/passport-oauth2/node_modules/oauth/lib/oauth2.js:177:18
at passBackControl (/home/node_modules/passport-facebook/node_modules/passport-oauth2/node_modules/oauth/lib/oauth2.js:123:9)
at IncomingMessage.<anonymous> (/home/node_modules/passport-facebook/node_modules/passport-oauth2/node_modules/oauth/lib/oauth2.js:143:7)
at IncomingMessage.emit (events.js:117:20)
at _stream_readable.js:944:16
at process._tickDomainCallback (node.js:492:13) { [FacebookTokenError: This authorization code has been used.]
name: 'FacebookTokenError',
message: 'This authorization code has been used.',
type: 'OAuthException',
code: 100,
subcode: undefined,
status: 500 }
Middleware code looks like this:
var passport = require('passport')
, FacebookStrategy = require('passport-facebook').Strategy
, moment= require('moment')
, momentTimeZone=require('moment-timezone')
, inflection = require('inflection')
, markdown = require('markdown').markdown
, URL =require('url')
, LocalStrategy=require('passport-local').Strategy
, config= require('./local')
, device = require('express-device')
var createUser = function (token, tokenSecret, profile, done) {
process.nextTick(function () {
User.findOne({
or: [
{uid: parseInt(profile.id)},
{uid: profile.id}
]
}
).exec(function (err, user) {
if (user) {
return done(null, user);
} else {
var data = {
provider: profile.provider,
uid: profile.id,
name: profile.displayName,
email: profile.email
};
if(profile.emails && profile.emails[0] && profile.emails[0].value) {
data.email = profile.emails[0].value;
}
if(profile.name && profile.name.givenName) {
data.firstname = profile.name.givenName;
}
if(profile.name && profile.name.familyName) {
data.lastname = profile.name.familyName;
}
User.create(data).exec(function (err, user) {
sails.log.info("Error",JSON.stringify(err))
return done(err, user);
});
}
});
});
};
module.exports = {
passport.use(new FacebookStrategy({
clientID: config.facebook.clientID,
clientSecret: config.facebook.clientSecret,
callbackURL: config.facebook.callbackURL,
profileFields: ['name', 'emails' ],
enableProof: true
},
function (accessToken, refreshToken, email, done)
{
//console.log("Auth done");
//done(null, email);
createUser
}
//createUser
//}
))
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(obj, done) {
done(null, obj);
});
/*app.get("/auth/facebook", passport.authenticate("facebook", { scope: ['email', 'public_profile'] }));*/
app.get('/auth/facebook',
passport.authenticate('facebook', { scope: ['email', 'public_profile'] }));
app.get("/auth/facebook/callback",
passport.authenticate("facebook", {
successRedirect: "/",
failureRedirect: "/login"
}),
function(req, res) {
res.redirect('/');
});
app.get('/logout', function(req, res){
req.logout();
res.redirect('/');
});
function ensureAuthenticated(req, res, next) {
if (req.isAuthenticated()) { return next(); }
res.redirect('/login')
}
app.use(passport.initialize());
app.use(passport.session());
app.use(device.capture());
device.enableDeviceHelpers(app)
}
}
};
Can anyone suggest why I am getting this error and any possible solution.
This error occurs when you logged using facebook login, after that delete the user record in your database. You must delete your APP in your facebook account and try again.
Another posibility is that you already are logged in, and your middleware is trying to login again. In your code, you are not checking if the user is already logged in before send the request to "auth/facebook. There is a simple way to prove this: Open a Chrome window in private mode, so no cookie is used, and try again your facebook login. Good Luck!
Probabily not exist some attributes of profile data. Try:
console.log(profile)
For verify all attributes of profile. In my case:
{ id: 'nnnnnnnn',
username: undefined,
displayName: 'My Name',
name:
{ familyName: undefined,
givenName: undefined,
middleName: undefined },
gender: undefined,
profileUrl: undefined,
provider: 'facebook',
_raw: '{"name":"My name","id":"nnnnnnnn"}',
_json: { name: 'My name', id: 'nnnnnnnn' } }
Not exist any attribute "email" or similar, this generate the error and not complete the cicle life of the authenticate:
error: FacebookTokenError: This authorization code has been used.
The attribute email not get because I hidden this in my account Facebook.
In your code:
...
var data = {
provider: profile.provider,
uid: profile.id,
name: profile.displayName,
email: profile.email
};
...
The line:
email: profile.email
You are already assuming that the attribute email is already exist.
Try:
email: (profile.emails && profile.emails[0]) ? profile.emails[0].value : ''
I am using Satellizer in my MEAN Stack webapp to login users. The satellizer module uses JSON Web Tokens.
The token is created in:
var jwt = require('jwt-simple');
function createJWT(user) {
var payload = {
sub: user._id,
user: {
displayName: user.displayName,
email: user.email,
admin: user.admin
},
iat: moment().unix(),
exp: moment().add(2, 'hours').unix()
};
return jwt.encode(payload, config.TOKEN_SECRET);
}
app.post('/auth/login', function(req, res) {
User.findOne({ email: req.body.email }, '+password', function(err, user) {
if (!user) {
return res.status(401).send({ message: 'Wrong email and/or password' });
}
user.comparePassword(req.body.password, function(err, isMatch) {
if (!isMatch) {
return res.status(401).send({ message: 'Wrong email and/or password' });
}
res.send({ token: createJWT(user) });
});
});
});
The thing is that later in a function, I need to update the user key inside the payload object.
Is this possible?
Basically token looks like string. when you change payload then your token is changed (new string). You can't change token / payload without changing string. You can create new one based on previous.
Remember to return new token to client application.