Using both JWT and Passportjs - node.js

I am working on mern stack application where I use both jwt for registration and passportjs for social login. I am currently facing issue of authenticating routes.
Here is the Auth middleware of jwt and passportjs:
const jwt = require("jsonwebtoken")
const Auth = async (req,res,next)=>{
//if(req,headers)
try {
const token = req.headers.authorization.split(" ")[1]
if(!token){
return res.status(404).json({msg:"no token authorization"})
}
let decodeData
decodeData = jwt.verify(token, process.env.JWT_SECRET)
req.userId = decodeData.user.id
next()
} catch (error) {
console.error(error.message);
res.status(500).send("Server Error");
}
}
const passportAuth = (req,res,next)=>{
if(!req.user){
return res.status(404).json({msg:"user not loggged in"})
}else{
next()
}
}
module.exports = Auth
module.exports = passportAuth
logged in user can create post but here I have jwt auth and passportauth.
if use both [Auth, passportAuth] in the route it wont be working becuase either user have to logged in with google or simple signup registration. Here is the authenticated route code
route.post("/",[auth,passportAuth], async(req,res)=>{
const post = req.body
try {
const createpost = new Post({
tags:post.tags,
creator:post.creator,
title: post.title,
message: post.message,
selectedFile: post.selectedFile,
user:req.userId
})
await createpost.save()
res.json(createpost)
} catch (error) {
console.error(error.message);
res.status(500).send("Server Error");
}
})
So what should I do here?

Maybe instead of returning error responses set some property on the response (something like res.authenticated = true / res.authenticated = false). Then have another middleware which would check if this property is set and only return error response if it's false or not present. You would just have to make sure that this middleware is called always as the last one (I don't remember now how it works in express).
As a side note - 404 response is not found. For the case of missing/bad authentication you should rather use 401.

Related

Delete User and Logout that user from all Devices

I wanted to implement a feature in my app. Where an Admin can delete the user. So basically the delete is working fine but somehow i cannot logout the logged in user. Let me explain it more briefly, Suppose there is a User A which is currently using my app and the admin decided to remove that user from the app so they can't no longer access the features of the app. To remove the user i can call an API and delete that user but if i completely delete the user it loses all the access to the API's call coz user with the certain ID isn't available anymore and the app breaks coz the API call will fail for that deleted User. So I was wondering is there anyway to logout the user after admin deletes it.
The Frontend is on ReactJs and Backend is on NodeJs. And i am using JWT for authentication. Any help will be appreciated and if this question isn't clear enough please let me know so i can explain it more.
In backend in every protected route you should verify the token and token should contain user id or email using that you will verify the token. After deleting the user throw error with no user found and in frontend make sure if there are the error no user found then it will delete the JWT token.
What comes into my mind is to put a middleware between your requests and server. By doing so, instead of trying to log out from all devices, we will not allow any action if user does not exist; in this very example, we will prevent the user to delete a place and toast a message on the front end. I will share an example of that, but you need to tweak the code according to your needs.
Http Error Model
class HttpError extends Error {
constructor(message, errorCode) {
super(message);
this.code = errorCode;
}
}
module.exports = HttpError;
Middleware
const HttpError = require('../models/http-error');
module.exports = (req, res, next) => {
try {
// Check user if exists
User.findById(req.userData.userId).exec(function (error, user) {
if (error) {
throw new Error('Authentication failed!');
}
else {
return next();
}
});
}
catch (error) {
return next(new HttpError('Authentication failed!', 403));
}
};
Route
const express = require('express');
const router = express.Router();
const checkAuth = require('../middleware/check-auth');
router.use(checkAuth);
// Put after any routes that you want the user to be logged in
router.delete('/:placeId', placesControllers.deletePlace); //e.x.
...
module.exports = router;
E.x. controller (with MongoDB)
const deletePlace = async (req, res, next) => {
const placeId = req.params.placeId;
let foundPlace;
try {
foundPlace = await Place.findById(placeId).populate('userId').exec();
}
catch (error) {
return next(new HttpError('Could not find the place, please try again', 500));
}
// Delete place
res.status(200).json({message: 'Deleted place'});
};
FRONT END PART
import toastr from 'toastr';
....
try {
const response = await fetch(url, {method, body, headers});
const data = await response.json();
if (!response.ok) {
throw new Error(data.message);
}
}
catch(error) {
// handle the error, user not found
console.log(error.message);
toastr.error(error.message, 'Error', {
closeButton: true,
positionClass: 'toast-top-right',
timeOut: 2000,
extendedTimeOut: 1,
});
}

Having trouble with Multer and JWT

I'm currently trying to wrap my head around this:
Basically I am trying to verify the users token before uploading the file with multer.
The problem is the JWT from the cookie is "undefined".
This is the route snippet:
router.post("/files/user/:userId", verifyJWT, upload.single("file"), controllerFunction);
In my verifyJWT method I'm doing:
exports.verifyJWT = async (req, res, next) => {
let accesToken = req.cookies.jwt;
if (!accesToken) {
return res.status(401).json({
message: "Acces is unauthorized.",
});
}
let payload;
try {
payload = jwt.verify(accesToken, process.env.JWT_KEY);
req.name = payload.name;
req.type = payload.type;
req._id = payload._id;
next();
} catch (err) {
res.status(403).json({ message: "Token could not be verified!" });
}
};
If my route has the verifyJWT middleware for whatever reason when I'm calling console.log(accesToken) the acces token is sent as undefined.
If i remove the middleware verifyJWT from the "/files/user/:userId" route, and console.log(accesToken) I'm receiving the token.
This only happens on the route which has the multer upload callback in it.
I have already tried the suggestions from this thread but for whatever reason it does not seem to work

Express Middleware not rendering the view

I have written an auth middleware , which when fails , I want to render a page instead of sending response to my xhr request . How to do it ?
const jwt = require('jsonwebtoken')
const User = require('../models/users')
const express = require('express')
const auth = async (req, res ,next) => {
try {
//res.render("404")
const token = req.header('Authorization').replace('Bearer ' , '') //token comes from client in the header section named Auth(here)
const data = jwt.verify(token , 'itistechvaulttoken') //this gives back the id of the user and also iat :- issued at , a callback can be attached to
// .verify :-- see jsonwebtoken documentation for details .
const user = await User.findOne({ _id : data._id})
if(!user) {
throw new Error()
}
req.token = token //this is being use mainly for logout route handler
req.user = user
next()
} catch(e) {
res.status(401).render("404")
}
}
module.exports = auth
I have create a form and sending that data to my backend using xhr request to route and from there i am redirecting it to another route where I want to render the view .
My routes :-
router.post('/users' ,async (req, res) => {
console.log("Request Recieved")
const user = new User(req.body)
try {
await user.save()
const token = await user.generateAuthToken()
//console.log(token)
//res.status(201).send({ user , token})
req.accesstoken = token
res.redirect(302 , "/users/me" )
}
catch(e) {
console.log(e)
res.status(400).send("User not created" + e)
}
})
/user/me :-
router.get('/users/me', auth, async (req, res) => {
console.log("in users/me")
res.send(req.user)
})
I know here authentication has to fail, and code under the catch of middleware should run ,where I am trying to render a view but failing .
I guess you want your xhr endpoint code on your server to make your user's browser display an error page of some kind upon auth failure.
To do that you need the Javascript code in your browser to detect an error message in the response to the xhr request and navigate to the error page. Your nodejs app can't force any kind of redirect when responding to an xhr request without cooperation from the browser.

Authorization middleware JWT confusion

I have a suspicion about the relative security of some authentication middleware code I came across in a course im enrolled in.
So I used postman to send a request to a protected route(see route code below) and found that I was able retrieve an order for one user with a token generated for another user.
const protected = asyncHandler(async (req, res, next) => {
let token;
if (
req.headers.authorization &&
req.headers.authorization.startsWith("Bearer")
) {
try {
token = req.headers.authorization.split(" ")[1];
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = await User.findById(decoded.id).select("-password");
next();
} catch (error) {
console.error(error);
res.status(401);
throw new Error("Not authorized, token failed");
}
}
if (!token) {
res.status(401);
throw new Error("Not authorized, No token found");
}
});
export protected
It seems evident to me that this middleware code will only verify if a user from decoded token exists in the DB and but will not limit access to resources based on the user/token.
import {addOrderItems, getOrderbyId} from "../controllers/orderController.js";
import { protected } from "../middleware/authMiddleware.js";
const router = express.Router();
router.route("/").post(protected, addOrderItems);
router.route("/:id").get(protected, getOrderbyId);
//:id is the order id
However, when testing another protected route for updating a user's profile info, I receive an error when using wrong token.
Was hoping for some clarification
jwt.verify will only verify that the given token is generated by the server or not. It doesn't care which user send this token.
For your protected middleware, it just check if the request is authorized. If so, the request will pass to the controller.
As for the updating route. It probably be something like this:
// route
router.route("/:userId", protected, updateController)
const updateController = (req, res) => {
const user = req.user; // this is the one generated by protected middleware
const reqUserId = req.params.userId; // this is the one send by request
if (user.id !== reqUserId) {
// if two ids are not the same, it means someone is trying
// to update the profile with the wrong token
res.status(401);
}
// update profile in database
}

Node-Restful with Json web tokens

I am trying to build a simple web token protected api in nodejs. I have been following this tutorial authenticate a node js api with json web tokens and have been implementing the steps in my app. I now have an api running that allows me to get/post/put/delete and a route that generates a webtoken for the user and shows it in plain text (for dev purposes). I am using node-restful for the api's but I am having some trouble understanding how I would actually verify if the client is sending the webtoken in their request, before allowing these get/post/put/delete requests.
Here is my router. Where I define the allowed requests:
const express = require('express')
const router = express.Router()
// Models - Load models here
var userModel = require('./models/User')
// Controllers - Load controllers here
const userController = require('./controllers/userController')
// Routes - Define routes here
router.post('api/authenticate', userController.authenticate) //Route that generates the webkey and shows it in the response
// Configure the endpoint that node-restful will expose. Here I want to first check if the user is sending his or her api key. Before allowing these methods.
userModel.methods(['get', 'put', 'post', 'delete'])
userModel.register(router, '/api/users')
// Export the router object
module.exports = router
Here is my userController where the token is generated.
// Dependencies
const User = require('../models/User')
const jwt = require('jsonwebtoken')
const config = require('../config.js')
module.exports = {
authenticate: function(req, res, next) {
// find the user
User.findOne({username: req.body.name}, function(err, user) {
if (err) throw err;
if (!user) {
res.json({
success: false,
message: 'Authentication failed. User not found.' });
} else if (user) {
// check if password matches
if (user.password != req.body.password) {
res.json({
success: false,
message: 'Authentication failed. Wrong password.' });
} else {
// if user is found and password is right
// create a token
var token = jwt.sign(user, config.secret, {
expiresIn: 60*60*24 // expires in 24 hours
});
// return the information including token as JSON
res.json({
success: true,
message: 'Enjoy your token!',
token: token
});
}
}
})
}
}
And here is my user model.
// Dependencies
const restful = require('node-restful')
const mongoose = restful.mongoose
// Schema
const userSchema = new mongoose.Schema({
username: String,
password: String,
email: String
})
// Return the model as a restful model to allow it being used as a route.
module.exports = restful.model('User', userSchema)
Is there some way I can protect these endpoints, using the same manner of syntax as I am currently using to expose them? I believe I would have to check for the web token before defining the methods:
userModel.methods(['get', 'put', 'post', 'delete'])
userModel.register(router, '/api/users')
If I simply remove the methods themselves, the user will not be able to get the page and is shown a: "Cannot GET /api/users" error. What if I wanted to show a custom error? For example: "No web token provided. Register to authenticate" etc etc? Any help is much appreciated. Thank you in advance.
I now have a function that checks for the token before serving a page. It seems to work for now. Currently I am passing the token manually in postman as a header: x-access-token. How would I catch the token upon generation and automaticly make the client send it on future requests? Here is the function that checks for the token and the protected route.
Great. I kept working while waiting for any answers and completed this step. I can now generate the token and using postman pass that to a secured route I created. It works perfectly, but I am struggeling to understand how I am going to save the token on the client side and pass that on every request. I still generate the token, the same way as above. I can verify the token by manually passing it in my header as x-access-token, but how would I do this automaticly?
Update
Here is the function that checks the token and a protected route that utilizes that function:
// Routes - Define routes here
function getToken(req, res, next) {
var token = req.body.token || req.query.token || req.headers['x-access-token'];
// decode token
if (token) {
// verifies secret and checks exp
jwt.verify(token, config.secret, function(err, decoded) {
if (err) {
return res.json({ success: false, message: 'Failed to authenticate token.' });
} else {
// if everything is good, save to request for use in other routes
req.decoded = decoded;
console.log(decoded);
next();
}
});
} else {
// if there is no token
// return an error
return res.status(403).send({
success: false,
message: 'No token provided.'
});
}
}
router.get('/entries', getToken, entryController.get)
I found this question save-token-in-local-storage-using-node Which solved the last piece of the puzzle.
You can simply write a middleware for this kind of purpose. Clients will generally send tokens in header, so that you can get the header information and verify it. Your middleware will be something like this.
module.exports = (req, res, next) => {
if (!req.headers.authorization) {
return res.status(401).json({
success: false,
message: "You are not authorized for this operation."
})
}
// get the authorization header string
const token = req.headers.authorization
// decode the token using a secret key-phrase
return jwt.verify(token, config.secret, (err, decoded) => {
// the 401 code is for unauthorized status
if (err) {
return res.status(401).json({
success: false,
message: "You are not authorized for this operation."
})
}
const username = decoded.username
// check if a user exists
return User.findOne({username: username}, (userErr, user) => {
if (userErr) {
return res.status(500).json({
success: false,
message: "Error occured while processing. Please try again.",
err: userErr
})
}
if ( !user ) {
return res.status(401).json({
success: false,
message: "You are not authorized for this operation."
})
}
return next()
})
})
}
For the security reasons it is better to store JWTs in your application associated with the user. Complete explanation can be found here.
Update:
You can save the token in cookie and parse the cookie to find out the token and then verify that.

Resources