how to get the json of a route without fetch - node.js

l have a route which return me a json with user grant.
router.get('/test', function(req, res,next ) {
var token = req.cookies.auth;
if (!token) return res.status(401).send({ message: ""});
jwt.verify(token, config.secret, function(err, decoded) {
if (err) return res.status(500).send({ message: "Failed to authenticate token."});
User.findById(decoded.id,function(err,user){
if (err) return res.status(500).send({ message: "problmes found user, sorry "});
if(!user) return res.status(404).send({message: "user not found "});
res.status(200).send({message:user.role});
});
});
});
i want to catch the json response in a variable for another route to authorize an action.
router.get('/', function(req, res, next) {
if (jsonresponse == grant ){
var allMusique;
var idMaxMusique;
MongoClient.connect(url, function(err, db) {
if (err) throw err;
var dbo = db.db("projet_node");
dbo.collection("musiques").find({}).toArray(function(err, result) {
if (err) throw err;
allMusique = result;
var size = allMusique.length-1;
idMaxMusique = parseInt(result[size].id)+1;
res.render('musiques', { resultat: allMusique, idMax: idMaxMusique });
});
});}
else{
res.render('unauthorized');
}
});

create Controllers/middlewares/authenticate.js file and write the below code
const jwt = require('jsonwebtoken')
module.exports = (req, res, next) => {
const token = req.headers['token'] || req.body.token || req.cookies.token;
if (token) {
try {
req.decoded = jwt.verify(token, JWT_SECRET_KEY)
// JWT_SECRET_KEY -> config.secret
next()
} catch (err) {
res.status(403)
.send({ success: false, message: 'Failed to authenticate token.' })
}
}
return res.status(403).send({
success: false,
message: 'No token provided.'
})
}
create Controlller/UserController.js file
exports.getUser = function (req, res) {
// Perform Your requirement of code
// return Something
}
exports.getUserTest = function (req, res) {
// Perform Your requirement of code
// return Something
}
In Your routes/routes.js file
const authenticate = require('./Controllers/middlewares/authenticate');
const UserController = require('./Controllers/UserController');
// Routes with authentication. User must be login for get this routes
router.get('/getUser', authenticate, UserController.getUser);
// Routes without authentication. No need to login
router.post('/getUserTest', UserController.getUserTest);
Most Important require('PathOfFile') properly or Simply always double check path of file and folder in require.

Related

Login Authentification: No response from Rest API after Post Request

I recently switched from php development to Javascript (I'm really amazed by the performance and possibilities).
Currently I try to create a simple authentification function (Username,hashed Password checked to mariadb Database)
After following some tutorials I managed to create the following structure:
But when I try to test the API via Postman and Insomnia I just get no response. Not even an Error Code. Just going on forever, just like an infinite Loop?
I'm thankful for any tip as I'm new to this. Thanks in advance.
My Stack: React, Nodejs, Mariadb, Express & Jwt / bcryptjs
My Express Router router.js:
router.post('/login', (req, res, next) => {
pool.query(
`SELECT * FROM TABLE WHERE username = ${pool.escape(req.body.username)};`,
(err, result) => {
// user does not exists
if (err) {
throw err;
return res.status(400).send({
msg: err
});
}
if (!result.length) {
return res.status(401).send({
msg: 'Username or password is incorrect!'
});
}
// check password
bcrypt.compare(
req.body.password,
result[0]['password'],
(bErr, bResult) => {
// wrong password
if (bErr) {
throw bErr;
}
if (bResult) {
const token = jwt.sign({
username: result[0].username,
userId: result[0].id
},
process.env.API_SecretKey, {
expiresIn: '2h'
}
);
return res.status(200).send({
msg: 'Logged in!',
token,
user: result[0]
});
}
return res.status(401).send({
msg: 'Username or password is incorrect!'
});
}
);
}
);
});
router.post('/sign-up', userMiddleware.validateRegister, (req, res, next) => {
pool.query(
`SELECT * FROM TABLE WHERE LOWER(username) = LOWER(${pool.escape(
req.body.username
)});`,
(err, result) => {
if (result.length) {
return res.status(409).send({
msg: 'This username is already in use!'
});
} else {
// username is available
bcrypt.hash(req.body.password, 10, (err, hash) => {
if (err) {
return res.status(500).send({
msg: err
});
} else {
// has hashed pw => add to database
pool.query(
`INSERT INTO TABLE (SecurityID, userPassword, username, userOTP) VALUES ('${pool.escape}', ${pool.escape(
req.body.SecurityID,
req.body.username,
req.body.password,
req.body.userOTP
)}, ${pool.escape(hash)}, now())`,
(err, result) => {
if (err) {
throw err;
return res.status(400).send({
msg: err
});
}
return res.status(201).send({
msg: 'Registered!'
});
}
);
}
});
}
}
);
pool.end;
});
router.get('/secret-route', userMiddleware.isLoggedIn, (req, res, next) => {
console.log(req.userData);
res.send('This is the secret content. Only logged in users can see that!');
});
module.exports = router;
My Middleware users.js
module.exports = {
validateRegister: (req, res, next) => {
// username min length 3
if (!req.body.username || req.body.username.length < 3) {
return res.status(400).send({
msg: 'Passwort:' + req.body.username + 'Please enter a username with at least 3 chars',
});
}
// password min 6 chars
if (!req.body.password || req.body.password.length < 6) {
return res.status(400).send({
msg: 'Passwort:' + req.body.password + 'Please enter a password with at least 6 chars'
});
}
// password (repeat) does not match
if (
!req.body.password_repeat ||
req.body.password != req.body.password_repeat
) {
return res.status(400).send({
msg: 'Both passwords must match'
});
}
next();
},
isLoggedIn: (req, res, next) => {
try {
const token = req.headers.authorization.split(' ')[1];
const decoded = jwt.verify(
token,
process.env.API_SecretKey
);
req.userData = decoded;
next();
} catch (err) {
return res.status(401).send({
msg: 'Your session is not valid!'
});
}
}
};
My index.js:
const express = require("express");
const DigitalMangement = express();
const cors = require('cors');
require("dotenv").config();
DigitalMangement.use(cors());
DigitalMangement.use(express.json());
// add routes
const router = require('./Routes/router.js');
DigitalMangement.use("/api", router);
DigitalMangement.listen(process.env.Application_Port, () => {
console.log("Server is running on Port " + process.env.Application_Port)
});
I haven't reviewed the whole code but, if you throw the error the code block will not continue. In this case, it won't be logged or sent as a response. Try removing the throw err line and rerun the code.
if (err) {
throw err; //! here
return res.status(400).send({
msg: err
});
}
Thanks for all the help fellow Coders:
It seems to be that the import MariaDB isn't 100% correct in this situation.
I changed it to mariadb/callback and it started to work.
The MariaDB library returns Promises and mariadb/callback allows callbacks.

Refreshing JWT access token with refresh token within single middleware function on a post route

I'm trying to learn JWT authentication in express and one thing that I'm came across this code from Github
that this guy has initialised an middleware function to authenticate and check expiry of access token as per below:
app.post("/protected", auth, (req, res) => {
return res.json({ message: "Protected content!" });
})
async function auth(req, res, next) {
let token = req.headers["authorization"];
token = token.split(" ")[1]; //Access token
jwt.verify(token, "access", async (err, user) => {
if (user) {
req.user = user;
next();
} else if (err.message === "jwt expired") {
return res.json({
success: false,
message: "Access token expired"
});
} else {
console.log(err);
return res
.status(403)
.json({ err, message: "User not authenticated" });
}
});
}
and a separate route for refreshing the access token with the help of refresh token
app.post("/refresh", (req, res, next) => {
const refreshToken = req.body.token;
if (!refreshToken || !refreshTokens.includes(refreshToken)) {
return res.json({ message: "Refresh token not found, login again" });
}
// If the refresh token is valid, create a new accessToken and return it.
jwt.verify(refreshToken, "refresh", (err, user) => {
if (!err) {
const accessToken = jwt.sign({ username: user.name }, "access", {
expiresIn: "20s"
});
return res.json({ success: true, accessToken });
} else {
return res.json({
success: false,
message: "Invalid refresh token"
});
}
});
});
So, my question is how secure it is and how can I create single middleware function that could do both authentication and refreshing access token without hitting the app.post('/refresh') as in my view it wouldn't be a smooth experience to deal with it in frontend API management within react
Edit
My middleware seems to work well but it doesn't identify the wrong refresh token and then actually getting worked on protected route
app.post('/home', authenticateUser, (req, res) => {
res.send('welcome');
});
async function authenticateUser(req, res, next) {
let token = req.headers['authorization'];
token = token.split(' ')[1];
jwt.verify(token, JWT_AUTH_TOKEN, async (err, phone) => {
if (phone) {
req.phone = phone;
next();
} else if (err) {
const refreshToken = req.body.refreshToken;
if (!refreshToken || !refreshTokens.includes(refreshToken)) {
return res.json({ message: 'Refresh token not found, login again' });
} else {
jwt.verify(refreshToken, JWT_REFRESH_TOKEN, (err, phone) => {
if (!err) {
const accessToken = jwt.sign({ phone }, JWT_AUTH_TOKEN, { expiresIn: '30s' });
return res.json({ success: true, accessToken });
} else {
return res.json({
success: false,
message: 'Invalid refresh token'
});
}
next();
});
}
} else {
console.log(err);
return res.status(403).json({ err, message: 'User not authenticated' });
}
});
}

router.patch is returning 404 "not found"

I am working on small node api and I have an issue with patch method.
My router.patch is returning me 404.
This is how my route looks:
router.param('userId', findById);
router.patch(
'/api/projects/update/:projectId/:userId',
authCheck,
isAdmin,
findProjectById,
update
);
The findById is based on my :userId param. Whole method looks like this:
exports.findById = async (req, res, next) => {
try {
let user = await User.findById(req.params.userId);
if (!user) return res.status(400).json({ msg: 'User not found' });
next();
} catch (err) {
console.error(err.message);
if (err.kind === 'ObjectId') {
return res.status(400).json({ msg: 'User not found' });
}
res.status(500).send('Server Error');
}
};
Based on that I should get proper user for proper project.
My two ayhorization methods:
exports.authCheck = async (req, res, next) => {
try {
/* get token from header
replace('Bearer', '') - this will remove bearer from token header
*/
const token = req.header('Authorization').replace('Bearer', '');
//check if no token
if (!token) {
return res.status(401).json({ msg: 'No token, authorization denied' });
}
/*
decoded contains _id as a payload in token. Id is from getAuthToken */
const decoded = jwt.verify(token, config.get('jwtSecret'));
const user = await User.findOne({
_id: decoded._id,
'tokens.token': token,
});
if (!user) {
throw new Error();
}
req.token = token;
req.user = user;
next();
} catch (err) {
res.status(401).json({ msg: 'Please authenticate' });
}
};
exports.isAdmin = async (req, res, next) => {
try {
if (req.user.role !== config.get('roleSecret')) {
return res.status(403).json({
errors: [
{
msg: 'No Admin rights. Access Denied!!',
},
],
});
}
next();
} catch (err) {
res.status(403).json({ msg: 'Forbidden access' });
}
};
Finaly, my project controller where i have findProjectById, update
In findProjectById I am looking for project based on route param and i assing it to project
exports.findProjectById = async (req, res, next) => {
const _id = req.params.projectId;
try {
let project = await Project.findById(_id);
if (!project) return res.status(400).json({ msg: 'Porject not found' });
req.project = project;
next();
} catch (err) {
console.error(err.message);
if (err.kind === 'ObjectId') {
return res.status(400).json({ msg: 'Porject not found' });
}
res.status(500).send('Server Error');
}
};
My update method i s not done, because i was testing if anything heppens
exports.update = async (req, res) => {
try {
const proj = await req.project;
const _id = proj._id;
await Project.findByIdAndUpdate(_id, req.body, {
new: true,
runValidators: true,
});
if (!proj) {
return res.status(404).json({ msg: 'Project not found' });
}
return res.json(proj);
} catch (err) {
res.status(500).send('Server Error');
}
};
Not sure what am I missing here, but after few hours and lot of searching still can't get this working
Get this working. Issue was in my router path.
/api/projects/update/:projectId/:userId
Should be
/projects/update/:projectId/:userId
this can be closed

why is my jwt token returning null on the backside but not the front

I am trying to make a GET request to my mLab database. I pass a JWT token with the request and logged it on both the client and server. It reads correctly on the client but shows null on the server. Any help would be much appreciated. I am using Node.js and Angular.
I am pretty new to this, so I apologize in advance if the mistake is obvious.
Here is the server's GET route:
router.get('/', (req, res, next) => {
var decoded = jwt.decode(req.query.token);
console.log(decoded);
console.log('employees');
if(decoded) {
return Company.find({_id: decoded._id})
.populate('user', 'firstName')
.exec(function(err, company) {
if (err) {
return res.status(500).json({
title: 'An error occurred',
error: err
});
}
res.status(200).json({
message: 'Success',
obj: company
});
});
} else {
return res.status(401).json({
title: 'Not authenticated',
error: {
message: 'Please create an account or sign in'
}
});
}
console.log(company);
});
Here is the client:
getEmployees() {
const token = localStorage.getItem('token')
? '?token=' + localStorage.getItem('token')
: '';
console.log(token);
return this.http.get('http://localhost:3000/company' + token)
.map((response: Response) => {
const employees = response.json().obj;
console.log(employees);
let transformedEmployees: Employee[] = [];
for (let employee of employees) {
transformedEmployees.push(new Employee(
employee.firstName,
employee.lastName,
employee.email,
employee.password,
employee.jobTitle,
employee.isAdmin,
employee.tasks
));
}
console.log(transformedEmployees)
this.employees = transformedEmployees;
return transformedEmployees;
})
.catch((error: Response) => {
this.errorService.handleError(error.json());
return Observable.throw(error.json())
});
}
You should NOT be placing your token in the Authorization header of your request.
You use an express middleware to decode the token:
const myDecoder = (req, res, next) => {
req.user = jwt.decode(req.headers.authorization);
next();
}
Then, you place in your route:
router.get('/', myDecoder, (req, res) => {
let user = req.user;
console.log(user);
console.log('employees');
if (user) { blah }
...
Should not be passing the entire token over the URL itself.

How to ignore some request type in Jsonwebtoken

I want to ignore some API URL of being checked against token authentication
I want to protect post and put methods but not get of this url
localhost:3000/api/events/
router.use(function(request, response) {
var token = request.body.token || request.query.token || request.headers['x-access-token'];
if (token) {
jwt.verify(token, app.get(superSecret), function(err, decoded) {
if (err)
return response.json({
sucess: false,
message: "Failed token Authentication"
});
else {
request.decoded = decoded;
next();
}
});
} else {
return response.status(403).send({
success: false,
message: 'No token provided.'
});
}
});
How can I do this using jsonwebtoken in node,express
I want this to apply to only post,put,delete requests but not on get requests.
You can move your anonymous middleware to normal declared function and then pass it to all protected routes (you decide which route you want to protect!)
Your code could look like:
function tokenProtection(request, response, next) {
var token = request.body.token || request.query.token || request.headers['x-access-token'];
if (token) {
jwt.verify(token, app.get(superSecret), function(err, decoded) {
if (err)
return response.json({
sucess: false,
message: "Failed token Authentication"
});
else {
request.decoded = decoded;
next();
}
});
} else {
return response.status(403).send({
success: false,
message: 'No token provided.'
});
}
}
and now your routes could look like (your decision what you want to protect):
router.get('/item', function(req, res) { ... }); // not protected
router.get('/item/:id', function(req, res) { ... }); // not protected
router.post(tokenProtection,'/item', function(req, res) { ... });//protected
router.put(tokenProtection,'/item', function(req, res) { ... });//protected
router.get('/book', function(req, res) { ... });// not protected
router.get('/book/:id', function(req, res) { ... });// not protected
router.post(tokenProtection,'/book', function(req, res) { ... });//protected
router.put(tokenProtection,'/book', function(req, res) { ... });//protected
Put the routes you want to protect below your authentication route and the ones you do not want to protect can above the authentication route. Something like this,
// Require what will be needed
var express = require('express'),
User = require('../models/user'),
usersRouter = express.Router();
var jwt = require('jsonwebtoken'); // used to create, sign, and verify tokens
var config = require('./config'); // get our config file
var secret = {superSecret: config.secret}; // secret variable,
// Create a new user and return as json for POST to '/api/users'
usersRouter.post('/', function (req, res) {
var user = new User(req.body);
user.save(function(){ //pre-save hook will be run before user gets saved. See user model.
res.json({user : user, message: "Thank You for Signing Up"});
});
});
usersRouter.post('/authentication_token', function(req, res){
var password = req.body.password;
// find the user
User.findOne({
email: req.body.email
}, function(err, user) {
//If error in finding the user throw the error
if (err) throw err;
//If there is no error and the user is not found.
if (!user) {
res.json({ success: false, message: 'Authentication failed. User not found.' });
//if the user is found
} else if (user) {
// check if password matches
user.authenticate(password, function(isMatch){
if(isMatch){
// if user is found and password is right
// create a token with full user object. This is fine because password is hashed. JWT are not encrypted only encoded.
var token = jwt.sign({email: user.email}, secret.superSecret, {
expiresIn: 144000
});
// set the user token in the database
user.token = token;
user.save(function(){
// return the information including token as JSON
res.json({
success: true,
id: user._id,
message: 'Enjoy your token!',
token: token
});
});
} else {
res.json({ success: false, message: 'Authentication failed. Wrong password.' });
}
});
}
});
});
//***********************AUTHENTICATED ROUTES FOR USERS******************************
// Return ALL the users as json to GET to '/api/users'
usersRouter.get('/', function (req, res) {
User.find({}, function (err, users) {
res.json(users);
});
});
// Export the controller
module.exports = usersRouter;
I actually explained this yesterday itself on my blog because I was struggling to figure it out. If you are still not clear, you can check it out here, Node API Authentication with JSON Web Tokens - the right way.
If there are other resources like in my case it was plans. Below is the code I put above all the routes for plans I wanted to authenticate.
// route middleware to verify a token. This code will be put in routes before the route code is executed.
PlansController.use(function(req, res, next) {
// check header or url parameters or post parameters for token
var token = req.body.token || req.query.token || req.headers['x-access-token'];
// If token is there, then decode token
if (token) {
// verifies secret and checks exp
jwt.verify(token, secret.superSecret, function(err, decoded) {
if (err) {
return res.json({ success: false, message: 'Failed to authenticate token.' });
} else {
// if everything is good, save to incoming request for use in other routes
req.decoded = decoded;
next();
}
});
} else {
// if there is no token
// return an error
return res.status(403).send({
success: false,
message: 'No token provided.'
});
}
});
//***********************AUTHENTICATED ROUTES FOR PLAN BELOW******************************
PlansController.get('/', function(req, res){
Plan.find({}, function(err, plans){
res.json(plans);
});
});

Resources