I'm building my first SPA project with Vue.
I decided to go with NodeJS for the back-end, however, I'm having a headache building the login function with the JsonWebToken.
I had wrote some codes to see how JWT works and when I tried to see how JWT gets verified, server gave me an error.
JsonWebTokenError: jwt must be provided
at Object.module.exports [as verify] (c:\dir\node_modules\jsonwebtoken\verify.js:39:17)
at c:\projects\practice\demo\back\server.js:34:17
Below is the code for my server.js
This is the code for importing the stuff.
const express = require('express');
const jwt = require('jsonwebtoken');
const bodyParser = require('body-parser');
const api = express();
api.use(bodyParser.json());
api.use(bodyParser.urlencoded({ extended: true }));
This is for the API for issuing JWT.
api.post('/secure', function (req, res) {
const token = jwt.sign({ user: {id:1, name:'ME!', role: 'average'} }, 'dsfklgj');
console.log(token);
res.json({jwt: token});
});
This is the API for checking JWT.
api.post('/check/post', function (req, res) {
const token = req.body.jwt;
const x = jwt.verify(token, 'dsfklgj', function (err, decoded) {
if (err) throw err;
console.log(decoded);
});
if (x != true) {
res.json({ auth: false });
}else {
res.json({ auth: true });
}
});
jwt must be provided
This error happens when the coming token is null or empty.
Make sure that the Authorization header has a value of Bearer <Access Token>. Bearer < white space > < Access Token >
It may be you have not defined jwt in specific file or it is null or empty. Therefore you are getting an error. I just test your code and it works for me. It may be that you are not sending jwt token into post request correctly.
const express = require('express');
const jwt = require('jsonwebtoken');
const bodyParser = require('body-parser');
const http = require('http');
const api = express();
api.use(bodyParser.json());
api.use(bodyParser.urlencoded({ extended: true }));
api.post('/secure', function(req, res) {
const token = jwt.sign({ user: { id: 1, name: 'ME!', role: 'average' } }, 'dsfklgj');
console.log(token);
res.json({ jwt: token });
});
api.post('/check/post', function(req, res) {
const token = req.body.jwt;
console.log('token: ' + token);
const x = jwt.verify(token, 'dsfklgj', function(err, decoded) {
if (err) throw err;
console.log(decoded);
});
console.log(x);
if (x != true) {
res.json({ auth: false });
} else {
res.json({ auth: true });
}
});
api.set('port', 3000);
var server = http.createServer(api);
server.listen(api.get('port'), function() {
console.log("Express server listening on port " + api.get('port'));
});
BTW there is no way to test it like like this const x = jwt.verify(token, 'dsfklgj', function (err, decoded) {. Either write it in Sync way or check condition in async callback function. In your case, x will be undefined and no guarantee when it will run.
Related
I have a express nodejs backend which has three URL functions in which
1) registerUser() added user details to database and provided a JWT for the caller
2) verifyToken()- verifies if the JWT is valid
3) getConfiguration()- if JWT is verified from above function provides user with some configuration data
So the express code I'm using to achieve this is
//Routes.js
app.use(requestIp.mw())
app.route('/register')
.post(userController.registerUser);
app.use(userController.verifyToken)
app.route('/user/configuration')
.post(chayakkadaController.getConfiguration);
Now my issue is whenever I try calling the URL /register instead of calling registerUser function it calls verifyToken and says my token is invalid ( I want registerUser function to work without token, but getConfiguration should work only with token)
This is my verifyToken function
export function verifyToken(req, res, next) {
var token = req.body.token || req.headers["token"];
var appData = {};
if (token) {
jwt.verify(token, process.env.SECRET_KEY, function (err, decoded) {
if (err) {
appData["status"] = 1;
appData["error"] = "Invalid Token";
res.status(500).json(appData);
} else {
req.user = decoded;
next();
}
});
} else {
appData["status"] = 1;
appData["error"] = "Need access token";
res.status(403).json(appData);
}
}
My register User code
export function registerUser(req, res) {
let userData = {
device: req.body.device,
device_version: req.body.device_version,
device_id: req.body.device_id,
app_version: req.body.app_version,
app_id: 2,
ip_address: req.headers['x-real-ip'] || req.connection.remoteAddress
}
database.query(`INSERT INTO users SET ?`, userData)
.then(result => {
let user = {
id: result.insertId
}
let token = jwt.sign(user, process.env.SECRET_KEY);
let appData = {};
appData["token"] = token;
redis.sendMessage({
qname: 'registration_queue',
message: result.insertId + '',
}, (err, resp) => {
res.status(201).json(appData);
});
})
.catch(err => {
console.log(err);
res.status(500).json("Database Error");
})
}
Why you wanna to invent the wheel? there is a NPM module for that:
express-jwt
It has middleware that checks the jwt, if it valid, it decodes the payload and adds it to the request after that it proceed to your controller, if it is not valid, it throws an error, that you should catch, and do what ever you want.
It has the unless feature, so you can configure the entire subpath as restricted unless it is /register
router.use(`admin/`, [
expressJwt({ secret: jwtSecret }).unless({
path: ['/register]
}),
]);
I have a really big problem with security in my web application.
I implemented JWT token when user login to my application (REST API returns token).
In my jwt token, I have only userID. Problem is that, when I would like to login on user with ID = 1,
I can see and execute rest actions from all other users with the same token. for example:
When I looged userId = 1, I doing GET action: /api/users/1 and I have a information about user 1. But I can doing action /api/users/2, 3 etc.
All with one token. how to secure it?
const jwt = require('jsonwebtoken');
const env = require('../config/env.config.js');
module.exports = (req, res, next) => {
try {
const token = req.headers.authorization.split(' ')[1];
const decoded = jwt.verify(token, env.SECRET_KEY);
req.userData = decoded;
next();
} catch (error) {
return res.status(401).json({
message: 'Auth failed',
});
}
};
I think the best solution would be to create middleware that check the id of the sender and attach it to routes, similar to bellow
const middleware = (req, res, next) => {
const id = req.params.id || req.body.id || req.query.id
if (req.userData.id === id) {
next()
} else {
res.status(403).send({message: "forbidden"})
}
}
router.get("/api/users/:id", middleware, (req, res) => {
// do your staff
res.send({message: "ok"})
})
router.put("/api/users/:id", middleware, (req, res) => {
// do your staff
res.send({message: "ok"})
})
I have an express API and a ReactJs front-end. I try to make a POST call from my front-end directly to the local API.
For this I'm using axios.
The request is working fine when I set the parameters directly inside the query string but is always getting on timeout if I try to add the parameters through the data attribute of the axios.post() method.
Working
axios.post(`http://localhost:5001/site/authenticate?username=demo&password=demo`)
Not working
const payload = {
"username":"mh",
"password":"mh"
}
axios.post(`http://localhost:5001/site/authenticate`, payload)
My express server:
const express = require('express');
const bodyParser = require('body-parser');
const morgan = require('morgan');
const jwt = require('jsonwebtoken'); // used to create, sign, and verify tokens
var cors = require('cors');
const app = express();
const port = process.env.API_PORT || 5001;
app.use(cors());
app.set('secret', process.env.API_SECRET);
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(morgan('dev'));
app.use((req, res, next) => {
let data = '';
req.setEncoding('utf8');
req.on('data', (chunk) => {
data += chunk;
});
req.on('end', () => {
req.rawBody = data;
next();
});
});
// Allow CORS
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
// SITE ROUTES -------------------
const siteRoutes = express.Router();
siteRoutes.post('/authenticate', function(req, res) {
console.log('auth');
getDocument(usersBucket, req.query.username)
.then((doc) => {
console.log("Authentification... TODO");
// return the information including token as JSON
res.json({
success: true,
status: 200,
token: token
});
})
.catch(() => {
res.status(401).json({ success: false, message: 'Authentification failed. User not found.' });
});
});
// route middleware to verify a token
siteRoutes.use(function(req, res, next) {
const token = req.body.token || req.query.token || req.headers['x-access-token'];
if (token) {
// verifies secret and checks exp
jwt.verify(token, app.get('secret'), function(err, decoded) {
if (err) {
return res.json({ success: false, message: 'Failed to authenticate token.', status: 401 });
} else {
req.decoded = decoded;
next();
}
});
} else {
return res.status(403).send({
success: false,
message: 'No token provided.'
});
}
});
siteRoutes.get('/', function(req, res) {
res.json({ message: 'Welcome!' });
});
app.use('/site', siteRoutes);
app.listen(port, () => {
logger.log(`Express server listening on port ${port}`);
});
Any idea? Thanks.
Update
I replaced my route just to see if I got in or not (without worrying about parameters):
siteRoutes.post('/authenticate', function(req, res) {
console.log("go in");
res.json({
success: true,
status: 200,
});
});
But my console.log is not showing hen I use the payload (it is when I do not).
You should access the payload data via request.body, not the request.query:
// SITE ROUTES -------------------
const siteRoutes = express.Router();
siteRoutes.post('/authenticate', function(req, res) {
console.log('auth');
getDocument(usersBucket, req.body.username) // <------- HERE
.then((doc) => {
console.log("Authentification... TODO");
// return the information including token as JSON
res.json({
success: true,
status: 200,
token: token
});
})
.catch(() => {
res.status(401).json({ success: false, message: 'Authentification failed. User not found.' });
});
});
request.query are the parameters passed in the URL, like:
protocol://hostname:port/path/to.route?query_param_0=value_0&query_param_1=value_1
on your express endpoint request.query will be:
{
query_param_0: value_0,
query_param_1: value_1
}
while sending the payload, with the second argument in axios.post(url, payload):
axios.post('/user', {
firstName: 'Fred',
lastName: 'Flintstone'
})
on your express endpoint request.body will be:
{
firstName: 'Fred',
lastName: 'Flintstone'
}
when you use app.use(bodyParser.json()); (and you do).
You are using “getDocument(usersBucket, req.query.username)”
This means you express route is expecting username as a request param. That’s why it’s working when you use “?username=xx”
Instead try to get it from json body of request.
“req.body.username”
Also you should consider validating the request body or param as required.
Hi so I have been using this node server for some time and it recently stopped working (presumably due to some logical error I mistakenly adjusted), throwing a 404 when I run the server. When I call on it with an http request it throws a 404 as well and shows the same on load from the actual URL in a browser. What is going on?
ter image description here]3]3
index.js:
//Environment Vars
var uri = process.env.NODE_ENV || "development"
console.log(uri + " environment")
//Express App
var express = require('express');
var app = express();
//Api for reading http post request body in express
var bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json())
//Log Connections
app.use(function timeLog (req, res, next) {
console.log('incoming connection . . . ')
console.log(req)
next()
})
//API middelware
var api = require('./api')
app.use('/api', api)
app.get('/', function (req, res) {
res.status(200).send(JSON.stringify({ message: 'Welcome!', success: true, error: null }));
});
//Create Server
var port = process.env.PORT || 1337;
var httpServer = require('http').createServer(app);
httpServer.listen(port, function () {
console.log('Server running on port ' + port + '.');
});
api.js
var express = require('express')
var router = express.Router()
var stripe_key = process.env.STRIPE_KEY || "sk_test_P9XQtrrfGYU9mF8h6C47bgUp"
var stripe = require('stripe')(stripe_key);
var request = require("request-promise-native")
//API
router.get('/', function (req, res) {
res.status(200).send(JSON.stringify({ message: 'API Gateway', success: true, error: null }));
}) // Just for testing, just for error-handling
//1. Create a customer account
router.post('/new_customer', function (req, res) {
console.log("Creating new customer account...")
var body = req.body
stripe.customers.create({ email: body.email, })
.then((customer) => {
console.log(customer)
// Send customerId -> Save this on Firebase for later use
res.status(200).send(JSON.stringify({ success: true, error: null, customerId: customer.id }));
})
.catch((err) => {
console.log(err)
res.status(400).send(JSON.stringify({ success: false, error: err }))
});
})
//2. Save Credit Card with token
router.post('/new_card', function (req, res) {
var customerId = req.body.customerId
var token = req.body.token
stripe.customers.update(customerId, { source: token })
.then((customer) => {
console.log(customer)
res.status(200).send(JSON.stringify({ success: true, error: null }));
})
.catch((err) => {
console.log(err)
res.status(400).send(JSON.stringify({ success: false, error: err }))
});
})
//3. Use customerId to post a charge
router.post('/new_charge', function (req, res) {
var customerId = req.body.customerId
var amount = req.body.amount
var source = req.body.source
stripe.charges.create({
amount: amount, //in cents
currency: "usd",
customer: customerId, //CUSTOMER_STRIPE_ACCOUNT_ID
source: source, // obtained with Stripe.js
}).then((charge) => {
res.status(200).send(JSON.stringify({ message: 'Sucess.', success: true, error: null }));
}).catch((error) =>{
res.status(400).send(JSON.stringify({ message: 'Error', success: false, error: error }));
})
})
router.post('/ephemeral_keys', (req, res) => {
const stripe_version = req.body.api_version;
var customerId = req.body.customerId;
if (!stripe_version) {
res.status(400).end();
return;
}
console.log(stripe_version)
// This function assumes that some previous middleware has determined the
// correct customerId for the session and saved it on the request object.
stripe.ephemeralKeys.create(
{customer: customerId},
{stripe_version: stripe_version}
).then((key) => {
console.log("Ephemeral key: " + key)
res.status(200).json(key);
res.status(200).send(JSON.stringify({ message: 'AAAAhh', success: true, error: null }));
}).catch((err) => {
console.log("Ephemeral key error: " + err)
res.status(200).send(JSON.stringify({ message: 'ABBBBBB', success: true, error: null }));
res.status(500).end();
});
});
module.exports = router;
Other details:
Two important files: index.js & api.js but functionality is all in api.js which is why URL stem goes: .../api/...
Ok got the issue and its as silly as it can get. Are you sure you started the right server ? Its definitely not the node server. Its the http-server you have started. To start the server through node you need to go into the directory (in the terminal) and write "node index.js". Necessary code to start a http-server is written inside index.js.
Got this from the below screenshot.
the error for me is that you are making a GET request on a route that requires a POST request:
router.post('/new_charge', function (req, res) {
...
})
So you should check that you are making a post request to this route and not a GET request. How are you accessing that route from the client?
There is no router.get route for '/api/new_charge', there is one router.post route though.
Where is your router.get('/new_charge') route? it's not in the files you posted. You might have either deleted it or the /new_charge route needs to be used as post and not get.
I already checked multiple answers here on Stackoverflow, and also went through on the documentation but I still cannot find out what could be the problem. In my application I'm using SequelizeJS to access to my mySQL database and now I'm trying to secure my REST API endpoints with PassportJS using the JWT Strategy.
./app.js
// ...
// passport
app.use(passport.initialize());
require('./config/passport')(passport);
// ...
./config/passport.js
var passport = require('passport');
var passportJwt = require('passport-jwt');
var models = require('../models');
var config = require('./config');
var ExtractJwt = passportJwt.ExtractJwt;
var Strategy = passportJwt.Strategy;
module.exports = function(passport) {
var params = {
secretOrKey: config.jwt.secret,
jwtFromRequest: ExtractJwt.fromAuthHeader()
};
passport.use(new Strategy(params, function(jwt_payload, done) {
models.User.findOne({
where: {
id: jwt_payload.id
}
}).then(
function(user) {
if (user) {
done(null, user);
} else {
done(null, false);
}
},
function(err) {
return done(err, false);
}
);
}));
};
I'm trying to get the user entity from the request of this simple route:
var router = express.Router();
// ...
router.route('/user/me', passport.authenticate('jwt', { session: false }))
.get(function(req, res) {
console.log(req.user);
res.json(req.user);
});
I already created another route which returns a JWT token based on the provided username and password. When I call the /user/me endpoint I attach the JWT token into the header, for example:
Authorization: JWT eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MX0.M9z3iWNdjAu4THyCYp3Oi3GOWfRJNCYNUcXOw1Gd1Mo
So, my problem is that when I call the /user/me endpoint with a token, the req.user will be undefined and I cannot figure it out what is the reason.
Thank you in advance for your help!
Your route definition seems to be wrong: router.route doesn't accept a middleware in its second argument, so authentication does not happen at all.
It should be smth like
var router = express.Router();
// ...
router.route('/user/me')
.all(passport.authenticate('jwt', { session: false }))
.get(function(req, res) {
console.log(req.user);
res.json(req.user);
});