I am making a web application using nodejs and angular cli
I'm using JWT to authenticate my login function . But when I process it threw this error
Error: Expected "payload" to be a plain object.
at validate (D:\Mean_Projects\meanauthapp\node_modules\jsonwebtoken\sign.js:34:11)
at validatePayload (D:\Mean_Projects\meanauthapp\node_modules\jsonwebtoken\sign.js:56:10)
at Object.module.exports [as sign] (D:\Mean_Projects\meanauthapp\node_modules\jsonwebtoken\sign.js:108:7)
at User.comparePassword (D:\Mean_Projects\meanauthapp\routes\users.js:86:27)
at bcrypt.compare (D:\Mean_Projects\meanauthapp\models\user.js:53:9)
at D:\Mean_Projects\meanauthapp\node_modules\bcryptjs\dist\bcrypt.js:297:21
at D:\Mean_Projects\meanauthapp\node_modules\bcryptjs\dist\bcrypt.js:1353:21
at Immediate.next [as _onImmediate] (D:\Mean_Projects\meanauthapp\node_modules\bcryptjs\dist\bcrypt.js:1233:21)
at runCallback (timers.js:785:20)
at tryOnImmediate (timers.js:747:5)
at processImmediate [as _immediateCallback] (timers.js:718:5)
Here my passport code
const JwtStrategy= require('passport-jwt').Strategy;
const ExtractJwt=require('passport-jwt').ExtractJwt;
const User= require('../models/user');
const config=require('../config/database');
module.exports=function(passport){
let opts={};
opts.jwtFromRequest=ExtractJwt.fromAuthHeader();
opts.secretOrKey=config.secret;
opts.issuer = 'accounts.examplesoft.com';
opts.audience = 'yoursite.net';
passport.use(new JwtStrategy(opts,(jwt_payload,done)=>{
console.log(jwt_payload);
User.getUserById(jwt_payload._doc._id,(err,user)=>{
if(err){
return done(err,false);
}
if(user){
return done(null,user);
}
else{
return done(null,false);
}
});
}));
}
My code for authenticate and get profile
// Authenticate
router.post('/authenticate', (req, res, next) => {
const username = req.body.username;
const password = req.body.password;
User.getUserByUsername(username, (err, user) => {
if(err) throw err;
if(!user){
return res.json({success: false, msg: 'User not found'});
}
User.comparePassword(password, user.password, (err, isMatch) => {
if(err) throw err;
if(isMatch){
const token = jwt.sign(user, config.secret, {
expiresIn: 604800 // 1 week
});
res.json({
success: true,
token: 'JWT '+token,
user: {
id: user._id,
name: user.name,
username: user.username,
email: user.email
}
});
} else {
return res.json({success: false, msg: 'Wrong password'});
}
});
});
});
// Profile
router.get('/profile', passport.authenticate('jwt', {session:false}), (req, res, next) => {
res.json({user: req.user});
});
It fails at the line
const token = jwt.sign(user, config.secret, {
With error "Expected "payload" to be a plain object"
Your user object is initialized here:
User.getUserByUsername(username, (err, user)
Which I assume is mongoosejs object, which contains many methods and is not "serializable". You could handle this by passing a plain object, by either using .lean() from mongoose or plain toJSON method:
const token = jwt.sign(user.toJSON(), config.secret, {
expiresIn: 604800 // 1 week
});
I had this problem as well, with a returned user from mongoose, just add toJSON() or toObject() will fix the issue, but what happens if your user is not always coming from mongoose?
You will get a
user.toJson/user.ToObject is not a function
if you try to do this on a plain object.
If your user is coming from different sources and you don't know if it will be a plain object or not, you can solve it like this:
JSON.parse(JSON.stringify(user));
this is clearly mentioned in the migration doc of passport-jwt
that they have removed the ExtractJwt.fromAuthHeader() from version 2 and 3 and also to use the new method ExtractJwt.fromAuthHeaderAsBearerToken() or one of like that in place of old method. for compelte reference visit
From your log there is the issue
User.comparePassword (D:\Mean_Projects\meanauthapp\routes\users.js:86:27) at
so here four thing need to be updated in your code #every Bit
First in package.json file
Change the version to latest by using * or version no like this
by going to project directory and run the command
npm install passport-jwt --save
"dependencies": {
....
"passport-jwt": "^3.0.1"
}
or
write in the file and run the commadn
`npm install`
"dependencies": {
....
"passport-jwt": "*"
}
Second change this line of your code in authenticate method
const token = jwt.sign(user.toJSON(), config.secret, {
expiresIn: 604800 // 1 week
});
Third in the passport code change the old method
ExtractJwt.fromAuthHeader();
to new one, from the doc reference you need to use this method opts.jwtFromRequest=ExtractJwt.fromAuthHeaderWithScheme('jwt');
and fourth change this
User.getUserById(jwt_payload._id,(err,user)=>{
This solution will work on latest version's
if you still want to use this old method then
only change the version of your passport-jwt in package.json to 1.x.x (x is the nuber here )of your choise of lower version then 2, by moving to project folder and runing the command npm install
the only thing you need to check is data in the payload_jwt,it will be inside the second layer so please check the jwt_payload.
ok you are all set to go you had already handled User.getUserById(jwt_payload._doc._id,(err,user)=>{
It's very simple, if the user comes from database (mongo) then simply do user.toJSON(), if the user comes from any other source then simply do JSON.stringify(user).
if it's not comming from moongose
then use spread operator
const token = jwt.sign({ ...payload }, config.secret, {
expiresIn: 100080
});
Change
const token = jwt.sign(user, config.secret, { expiresIn: 10080 });
To
const token = jwt.sign(user.toJSON(), config.secret, { expiresIn: 10080 });
const token = jwt.sign(user, config.secret, {
expiresIn: 604800 // 1 week
});
convert this to
const token = jwt.sign(user.toJSON(), config.secret, {
expiresIn: 604800 // 1 week
});
or you need to console.log(jwt_payload); to find your ID comes under _doc or directly with jwt_payload. because this may change with the versions.
Related
I'm working on a project with some friends and wanting to start a project with npm run development I get this error:
const requireSignin = Object(express_jwt__WEBPACK_IMPORTED_MODULE_2__["expressJwt"])({
^
TypeError: Object(...) is not a function
at Module.eval (webpack:///./server/controllers/auth.controller.js?:58:85)
at eval (webpack:///./server/controllers/auth.controller.js?:95:30)
at Module../server/controllers/auth.controller.js (/Users/mihailomitrovic/Downloads/iteh_projekat/dist/server.generated.js:420:1)
at __webpack_require__ (/Users/mihailomitrovic/Downloads/iteh_projekat/dist/server.generated.js:21:30)
at Module.eval (webpack:///./server/routes/user.routes.js?:5:86)
at eval (webpack:///./server/routes/user.routes.js?:36:30)
at Module../server/routes/user.routes.js (/Users/mihailomitrovic/Downloads/iteh_projekat/dist/server.generated.js:540:1)
at __webpack_require__ (/Users/mihailomitrovic/Downloads/iteh_projekat/dist/server.generated.js:21:30)
at Module.eval (webpack:///./server/express.js?:17:77)
at eval (webpack:///./server/express.js?:165:30)
at Module../server/express.js (/Users/mihailomitrovic/Downloads/iteh_projekat/dist/server.generated.js:468:1)
at __webpack_require__ (/Users/mihailomitrovic/Downloads/iteh_projekat/dist/server.generated.js:21:30)
at eval (webpack:///./server/server.js?:3:66)
at Module../server/server.js (/Users/mihailomitrovic/Downloads/iteh_projekat/dist/server.generated.js:552:1)
at __webpack_require__ (/Users/mihailomitrovic/Downloads/iteh_projekat/dist/server.generated.js:21:30)
at eval (webpack:///multi_./server/server.js?:1:18)
[nodemon] app crashed - waiting for file changes before starting...
I've tried looking for solutions and looking at parts of the code but nothing has helped so far. Does it have something to do with importing a module? Can someone please help or at least point me in the right direction?
Since it's referring me to this file, this is the code from auth.controller.js:
import User from '../models/user.model'
import jwt from 'jsonwebtoken'
import { expressJwt } from 'express-jwt'
import config from './../../config/config'
const signin = async (req, res) => {
try {
let user = await User.findOne({
"email": req.body.email
})
if (!user)
return res.status('401').json({
error: "User not found"
})
if (!user.authenticate(req.body.password)) {
return res.status('401').send({
error: "Email and password don't match."
})
}
const token = jwt.sign({
_id: user._id
}, config.jwtSecret)
res.cookie("t", token, {
expire: new Date() + 9999
})
return res.json({
token,
user: {
_id: user._id,
name: user.name,
email: user.email
}
})
} catch (err) {
return res.status('401').json({
error: "Could not sign in"
})
}
}
const signout = (req, res) => {
res.clearCookie("t")
return res.status('200').json({
message: "signed out"
})
}
const requireSignin = expressJwt({
secret: config.jwtSecret,
userProperty: 'auth'
})
const hasAuthorization = (req, res, next) => {
const authorized = req.profile && req.auth && req.profile._id == req.auth._id
if (!(authorized)) {
return res.status('403').json({
error: "User is not authorized"
})
}
next()
}
export default {
signin,
signout,
requireSignin,
hasAuthorization
}
The problem is caused by importing express-jwt.
You need to replace it with this:
import { expressjwt } from 'express-jwt'
and you need to pass algorithms as an option when invoking expressjwt:
expressjwt({
secret: config.jwtSecret,
userProperty: 'auth', // and what is this? I'm not sure 'express-jwt' accepts this option
algorithms: ["HS256"]
})
You may need to read the doc on how to use express-jwt.
I have a Koa server that uses Passport to authenticate users against an Array,
and a React client. After successful login, the following requests are not authenticated as the cookie is undefined. The authenticate function's error parameter has:
{ message: 'Missing credentials' }
After browsing the site, I fixed the usual errors, calling the returned function of authenticate, adding {credentials: 'include'} to fetch etc, but still I have the same problem.
Middleware list:
router.use(cookie.default());
app.use :
koa-body, koa-session-store (also tried with koa-session), passport.initialize(), passport.session(), router.routes(), koa-static
local strategy
passport.use(new Strategy((username,password,callback)=>{
var u = users.find(u=>u.username == username);
return (u && password == 'password')? callback(null, u ):callback('user not found', false);
}));
/login authenticate
.post('/login', (ctx)=>{
console.log(ctx.request.body);
return passport.authenticate('local',(err,user,info,status)=>{
if(user) {
ctx.login(user);
ctx.body = {success: true}; // works correctly
ctx.redirect('/login-success');
} else {
ctx.redirect('/login-failure');
}
})(ctx);
});
/login-success
router.get('/login-success',async(ctx)=> {
return passport.authenticate('local',(err,user,info,status)=>{
console.log(err); // "Missing credentials"
})(ctx);
await ctx.response;
ctx.body = {success: true};
}).
Client call
let body = JSON.stringify({username: this.state.username, password: this.state.password});
let result = await fetch('http://localhost:4200/login',{method:'POST',credentials: 'include',body, headers:{'Content-Type':'application/json'}});
The fix is actually simple, but the reason is hard to find.
async middleware must either call await next() or return next() at the end.
Otherwise a 404 error is caused.
Adding await next() to the async /login-success callback, fixed the issue.
Documentation: https://github.com/koajs/koa/blob/master/docs/troubleshooting.md#my-middleware-is-not-called
Disclaimer: this is my first project with react, mongo and js. I'm following a tutorial and trying to adapt it to my needs.
I'm trying to signin to a DB, get some values from a user and return them just so I know I managed to achieve a connection. I know that the credentials are being passed and are ok. When wrong credentials are passed instead, it works as intended and return an error.
I got reducers for 3 states: request, success and fail. In both success and fail the loading is set to false upon being changed to, but when I enter the correct credentials the loading remains on the screen. Meaning it probably got stuck during the request validation.
Login Screen with XHR window
When looking through the terminal, this is the error that I get on the log.
(node:13688) UnhandledPromiseRejectionWarning: TypeError: (0 , _util.default) is not a function
at _callee$ (C:\Users\Andor\Documents\GitHub\NaturaVARK\backend\routes/userRoute.js:18:16)
at tryCatch (C:\Users\Andor\Documents\GitHub\NaturaVARK\node_modules\regenerator-runtime\runtime.js:63:40)
at Generator.invoke [as _invoke] (C:\Users\Andor\Documents\GitHub\NaturaVARK\node_modules\regenerator-runtime\runtime.js:293:22)
at Generator.next (C:\Users\Andor\Documents\GitHub\NaturaVARK\node_modules\regenerator-runtime\runtime.js:118:21)
at asyncGeneratorStep (C:\Users\Andor\Documents\GitHub\NaturaVARK\backend\routes\userRoute.js:16:103)
at _next (C:\Users\Andor\Documents\GitHub\NaturaVARK\backend\routes\userRoute.js:18:194)
at processTicksAndRejections (internal/process/task_queues.js:93:5)
at runNextTicks (internal/process/task_queues.js:62:3)
at processImmediate (internal/timers.js:434:9)
I double checked both userRoute.js and util.js. Both are identical to the source code of the tutorial I'm following, went so far as to copy and paste them, just to be sure. The problems persists. The line that brings up the problem seem to be 18 and 16, those would be the token and email field at res.send. I selected the relevant parts of the code and put here, as well as the repository link in case I might've missed something.
userRoute.js
import express from 'express';
import User from '../models/userModel';
import getToken from "../util";
const router = express.Router();
router.post('/signin', async (req, res) => {
const signinUser = await User.findOne({
email: req.body.email,
password: req.body.password,
});
if (signinUser) {
res.send({
_id: signinUser.id,
name: signinUser.name,
email: signinUser.email,
isAdmin: signinUser.isAdmin,
token: getToken(signinUser),
});
} else {
res.status(401).send({ message: 'Invalid Email or Password.' });
}
});
export default router;
util.js
import jwt from 'jsonwebtoken';
import config from './config';
const getToken = (user) => {
return jwt.sign(
{
_id: user._id,
name: user.name,
email: user.email,
isAdmin: user.isAdmin,
},
config.JWT_SECRET,
{
expiresIn: '48h',
}
);
};
export { getToken };
Github Repository
Any insight is more than welcome.
Since the data exported was an object {getToken}, you have to import as an object. So getToken becomes {getToken}, as it was not exported as a default value. For more insight check this out https://developer.mozilla.org/en-US/docs/web/javascript/reference/statements/export
Try this:
import express from 'express';
import User from '../models/userModel';
import {getToken} from "../util";
const router = express.Router();
router.post('/signin', async (req, res) => {
const signinUser = await User.findOne({
email: req.body.email,
password: req.body.password,
});
if (signinUser) {
res.send({
_id: signinUser.id,
name: signinUser.name,
email: signinUser.email,
isAdmin: signinUser.isAdmin,
token: getToken(signinUser),
});
} else {
res.status(401).send({ message: 'Invalid Email or Password.' });
}
});
export default router;
findByIdAndUpdate() successfully updates document, but returns error which i don't understand.
Here is schema:
const userSchema = mongoose.Schema({
phone: String,
password: String,
token: String
});
const User = mongoose.model('User', userSchema);
And here is function to update user in database
export const login = (req, res) => {
User.findOne({ phone: req.body.phone }, (err, result) => {
if (err) res.status(500).send(`User with ${req.body.phone} doesn't exist. \n Error: ${err}`);
if( result.password === req.body.password ){
// here Console.log(result) returns:
//{
// _id: 5aa28eb4f4a8de28c24e6990,
// phone: '+79781231233434',
// password: 'passsss',
// token: '1520613346284',
// __v: 0
//}
User.findByIdAndUpdate( result.id, { "token": Date.now() },
(err, result) => {
// It gives error, of which stacktrace i give below. But if check database -
// everything is fine, token was updated successfully
if (err) return res.status(500).send('Unable to create token. Error: ', err);
return res.status(200).send(result._id, result.token);
})
} else return res.status(500).send('Incorrect password');
})
}
Here is what i get in console when i do post request with data which should successfully pass this check and get token.
express deprecated res.send(status, body): Use res.status(status).send(body) instead controllers/user.js:17:28
express deprecated res.send(status, body): Use res.status(status).send(body) instead controllers/user.js:16:37
/Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/mongoose/lib/utils.js:423
throw err;
^
RangeError: Invalid status code: Unable to create token. Error:
at ServerResponse.writeHead (_http_server.js:190:11)
at ServerResponse._implicitHeader (_http_server.js:181:8)
at write_ (_http_outgoing.js:635:9)
at ServerResponse.end (_http_outgoing.js:754:5)
at ServerResponse.send (/Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/express/lib/response.js:221:10)
at ServerResponse.json (/Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/express/lib/response.js:267:15)
at ServerResponse.send (/Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/express/lib/response.js:158:21)
at /Users/dmitryklymenko/Documents/projects/project_exchange/server/controllers/user.js:10:38
at /Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/mongoose/lib/model.js:3930:16
at _init (/Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/mongoose/lib/query.js:2000:14)
at completeOne (/Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/mongoose/lib/query.js:1995:5)
at cb (/Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/mongoose/lib/query.js:2365:14)
at /Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/mongoose/lib/query.js:2465:14
at /Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/mongoose/lib/utils.js:418:16
at result (/Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/mongodb/lib/utils.js:413:17)
at session.endSession (/Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/mongodb/lib/utils.js:400:11)
at ClientSession.endSession (/Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/mongodb-core/lib/sessions.js:69:41)
at executeCallback (/Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/mongodb/lib/utils.js:396:17)
at handleCallback (/Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/mongodb/lib/utils.js:128:55)
at /Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/mongodb/lib/collection.js:2302:12
at result (/Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/mongodb/lib/utils.js:413:17)
at executeCallback (/Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/mongodb/lib/utils.js:405:9)
[nodemon] app crashed - waiting for file changes before starting...
I don't understand this error. Why it appears? If update done, why there is an error at all? And why i see warning about deprecations if i use res.status(status).send(body), as it suggests, everywhere? Maybe it's Babel makes problems to me? Anybody know what i need to check to figure out whats going on?
The body parameter can be a Buffer object, a String, an object, or an Array.
For it to work properly, use string interpolation:
User.findByIdAndUpdate( result.id, { "token": Date.now() },
(err, result) => {
// It gives error, of which stacktrace i give below. But if check database -
// everything is fine, token was updated successfully
if (err) return res.status(500).send(`Unable to create token. Error: ${err}`);
return res.status(200).send(`${result._id}, ${result.token}`);
})
Source: node-express error : express deprecated res.send(status): Use res.sendStatus(status) instead
I'm using "jsonwebtoken": "^5.4.0", I'am not able to expire my token
I create the token with:
var token = jwt.sign(user, app.get('superSecret'), {
expiresInSeconds: 1
});
My token is like
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJfaWQiOiI1NjFhODE5MjFhZGJmYWI2MzNlZWU4ZjciLCJ1c2VybmFtZSI6ImJhcm5vIiwicGFzc3dvcmQiOiIkMmEkMTAkcnh6SHY0dFFhbkxwVDNQOEVJSzNBTzVhLjcwNUJZdmxIOVhXOHlSVmpUMi9hNEdmTFd4YU8iLCJfX3YiOjAsImNyZWF0ZWRfYXQiOiIyMDE1LTEwLTExVDE1OjM0OjQyLjg3MFoifQ.ooELWBRlxtYwFTmJDFNOLiR6-2uR_-wjjXwPnS0c5Lk
In my middleware I have this check
jwt.verify(token, app.get('superSecret'), function (err, decoded) {
if (err) {
return res.json({success: false, message: 'Failed to authenticate token.'});
} else {
console.dir(decoded);
// if everything is good, save to request for use in other routes
req.decoded = decoded;
return res.json({success: true, token: decoded});
next();
}
});
With Postman I do a Post Request Like
Post http://localhost:3000/users
with
x-access-token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJfaWQiOiI1NjFhODE5MjFhZGJmYWI2MzNlZWU4ZjciLCJ1c2VybmFtZSI6ImJhcm5vIiwicGFzc3dvcmQiOiIkMmEkMTAkcnh6SHY0dFFhbkxwVDNQOEVJSzNBTzVhLjcwNUJZdmxIOVhXOHlSVmpUMi9hNEdmTFd4YU8iLCJfX3YiOjAsImNyZWF0ZWRfYXQiOiIyMDE1LTEwLTExVDE1OjM0OjQyLjg3MFoifQ.ooELWBRlxtYwFTmJDFNOLiR6-2uR_-wjjXwPnS0c5Lk
And My token is always valid!
console.dir(decoded)
{
"success": true,
"token": {
"_id": "561a81921adbfab633eee8f7",
"username": "myuser",
}
}
Why my code is always valid? How can I force to invalidate this code, for example like logout?
library might not invalidate token by time itself. In your case signature might be correct, so you get decoded variable. Probably you need to check it yourself.
Here is very useful tool to check what JWT token you actually generated.
http://jwt.io/#debugger
When using jwt.sign(), the first argument provided must be an object. Try replacing your code as below.
var token = jwt.sign({user: user}, app.get('superSecret'), {
expiresIn: 1
});
Also, use expiresIn instead of expiresInSeconds as it has been deprecated.