I'm trying to parse a URL in an Express backend but when I go to a url like localhost:3000/token=myJwtToken I get undefined for req.query.token in my authJwt.verifyToken middleware in the backend.
In my React App.js, I'm hitting the below endpoint where I need to verify a token in the URL as a param / query string with a secret shared by sending and receiving parties:
const req = {
url: 'http://localhost:8080/api/verify',
method: 'GET',
};
In my backend I have this route:
module.exports = function (app) {
app.get(
'/api/verify',
[authJwt.verifyToken], // verify token in URL middleware
galleryController.loadGallery, // proceed to gallery
);
};
My verifyToken auth middleware is thus:
verifyToken = (req, res, next) => {
let token = req.query.token;
console.log(token); // undefined
console.log(req.params); // {}
if (!token) {
console.log('NO TOKEN!!!');
return res.status(401).send({
message: 'Unauthorized!',
});
}
jwt.verify(token, process.env.JWT_SECRET, (err, decoded) => {
if (err) {
console.log(`Error: ${err}`);
return res.status(401).send({
message: `Error: ${err}`,
});
}
console.log('success!', decoded.token);
next();
});
};
Where have I gone wrong?
localhost:3000/token=myJwtToken is an (invalid) path, not a query parameter
localhost:3000/api/verify?token=myJwtToken will return a query parameter with key token and value myJwtToken
Related
I am a beginner with node js. I want to make an authentication server using jwt (jsonwebtoken).
The problem is when I test my end point "/api/posts?authorisation=Bearer token..." in postman with method POST with the right token, it gives me forbidden.
Here is my code:
const express = require('express')
const jwt = require('jsonwebtoken')
const app = express()
app.get("/api", (req, res) => {
res.json({
message: "Hey there!!!"
})
})
app.post('/api/posts', verifyToken, (req, res) => {
jwt.verify(req.token, "secretkey", (err, authData) => {
if (err) {
res.sendStatus(403) //forbidden
res.send(`<h2>${err}</h2>`)
} else {
res.json({
message: "Post Created...",
authData
})
}
})
})
app.post('/api/login', (req, res) => {
const user = {
id: 1,
username: "John",
email: "john#gmail.com"
}
jwt.sign({ user: user }, "secretkey", (err, token) => {
res.json({
token
})
})
})
function verifyToken(req, res, next) {
const bearerHeader = req.headers["authorization"]
if (typeof bearerHeader !== "undefined") {
const bearerToken = bearerHeader.split(" ")[1]
req.token = bearerToken
next()
} else {
res.sendStatus(403) //forbidden
}
}
app.listen(5000, () => {
console.log("Server is running :)")
})
I expected it to work because I brought it from a tutorial.
Your code works
The problem is in your request invocation:
According to the oauth2 spec, the Authorization token should be a header and your code expect that
So the token should be sent as http header, not as a query param like foo/bar?authorization=Bearer token...".
Here some samples
Postman
Axios (javascript)
let webApiUrl = 'example.com/getStuff';
let tokenStr = 'xxyyzz';
axios.get(webApiUrl,
{ headers: { "Authorization": `Bearer ${tokenStr}` } });
Advice
Read about oauth2 and jwt
Perform the token validation in the middleware to avoid the validation on each route
I set up jsonwebtoken in node.js and testing it with postman. First upon login I generate the token, then I copy it and use for the post request to a protected route. And jwt.verify runs just fine, the correct userId is retrieved, but when I call next() (see below) it goes to 404 error handler instead of the index where the protected routes are located:
var index = require('./routes/index')
function verifyToken (req, res, next) {
// Get auth header value
const bearerHeader = req.headers['authorization']
// check if bearer is undefined
const message = 'Unauthorized user, access denied!'
if (typeof bearerHeader !== 'undefined') {
const bearerToken = bearerHeader.split(' ')[1]
jwt.verify(bearerToken, 'privateKey', (err, userData) => {
if (err) {
// Forbidden
console.log(err)
res.status(403).send({ err })
} else {
console.log(userData.userId)
req.userId = userData.userId
------> next()
}
})
} else {
// Forbidden
res.status(403).send({ message })
// res.sendStatus(403)
}
}
// app.use('/auth', index);
app.use('/auth', verifyToken, index)
Why is it happening? What am I doing wrong here? Basically my goal is to set userId on the req object, then get it back in the root route auth/ in index
I am new in backend Node.js and till now I am able to complete registration and login with authentication.
When login I am getting token in response by using jwt token
Now I want to have the registration details to be shown to users after login. After login the details must of be of particular user's only whos is logging in.
And if admin is logging in, then he will get the entire database user's fields.
This is my index.route:-
const express = require ('express');
const router = express.Router();
const mongoose = require ('mongoose');
const User = mongoose.model('User');
const ctrlUser = require ('../controllers/user.controller.js');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const passport = require('passport');
// routing functions
//registartion user signup
router.post('/register' , ctrlUser.register);
//login user
router.post('/login' , (req, res, next) => {
User.find({email: req.body.email})
.exec()
.then(user => {
if(user.length < 1) {
return res.status(401).json({
message: "Auth failed. User not found."
})
}
bcrypt.compare(req.body.password, user[0].password, (err, result) =>{
if (err) {
return res.status(401).json({
message: "Auth failed. Check email and password"
});
}
if (result){
const adminEmail = "rohit#metapercept.com";
const role = user[0].email===adminEmail? "admin" : "user"; //check user id as admin or user
const token = jwt.sign(
{
email: user[0].email,
userId: user[0]._id,
role
},
process.env.JWT_KEY,
{
expiresIn : "1h"
});
return res.status(200).json({
message: "Auth Successful",
token : token
});
res.redirect('/profile');
}
});
})
.catch(err =>{
if (err.code == 500)
res.status(500).send(["Something went wrong in login"]);
else
return next(err);
});
});
router.get('/profile', function(req, res, next){
//something todo here ...
});
//delete user
router.delete('/:userId' , (req, res, next) =>{
User.deleteMany({_id: req.params.userId})
.exec()
.then(result => {
res.status(200).send(["Deleted"]);
})
.catch(err =>{
if (err.code == 500)
res.status(500).send(["Didn't get deleted"]);
else
return next(err);
});
});
module.exports = router;
How can I access user's details in profile url API?
Get JWT from request header then decode
jwt.verify(token, getKey, options, function(err, decoded) {
console.log(decoded.email)
});
jwt.verify - jwt doc
Create new middleware ( above other routes)
// route middleware to verify a token
router.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'];
// decode token
if (token) {
// verifies secret and checks exp
jwt.verify(token, app.get('superSecret'), 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; next();
}
});
} else {
// if there is no token
// return an error
return res.status(403).send({
success: false,
message: 'No token provided.'
});
}
});
Help : jwt - decode and save in req
In the code, After return your redirect never work. so There're 2 options:
You don't need to return a token to client, just use res.redirect('/profile') after your verification. (in this way, your server and client are in one)
You just return the token to client (Don't use res.redirect('/profile') anymore) then client will use that token to redirect to the profile. (in this way, your server and client are separate each other).
I am using node.js express app. jsonwebtoken for authentication. I want to exlude some api url from the jsonwebtoken verification. below is what I have tried and my code
router.use('/authentication', mountAllRoutes(authenticationModule));
// route middleware to verify a token
router.use((req, res, next) => {
const r = req;
const token = req.body.token || req.query.token || req.headers.authorization;
// decode token
if (token) {
// verifies secret and checks exp
jwt.verify(token, (req.app.get('superSecret')), (err, decoded) => {
if (err) {
// res.json({ success: false, message: 'Failed to authenticate token.' });
res.status(401).send({
success: false,
message: 'Failed to authenticate token.'
});
} else {
// if everything is good, save to request for use in other routes
r.decoded = decoded;
next();
// console.log(decoded);
}
return {};
});
} else {
// if there is no token
// return an error
return res.status(403).send({
success: false,
message: 'No token provided.'
});
}
return {};
});
router.use('/test', mountAllRoutes(testModule));
router.use('/other', mountAllRoutes(otherModule));
router.use('/data', mountAllRoutes(dataModule));
Here I have placed routes above middleware which I dont want to protect. and I have placed after middleware which I want to protect. But it is protected which I placed above middleware. In authenticationModule, login and user registration api comes. so for user registration it gives response no token provided
Note: I have refrerred this link How-to-ignore-some-request-type-in-Jsonwebtoken
create separate route file for the API you want to exclued.
//Routes
var users = require('./routes/users');
var api = require('./routes/publicApi');
App.js:
// route middleware to verify a token
router.use((req, res, next) => {
const r = req;
const token = req.body.token || req.query.token || req.headers.authorization;
// decode token
if (token) {
// verifies secret and checks exp
jwt.verify(token, (req.app.get('superSecret')), (err, decoded) => {
if (err) {
// res.json({ success: false, message: 'Failed to authenticate token.' });
res.status(401).send({
success: false,
message: 'Failed to authenticate token.'
});
} else {
// if everything is good, save to request for use in other routes
r.decoded = decoded;
next();
// console.log(decoded);
}
return {};
});
} else {
// if there is no token
// return an error
return res.status(403).send({
success: false,
message: 'No token provided.'
});
}
return {};
});
app.use('/users', router);//will use Token Authentican
app.use('/publicApi', router);//Dont do this.
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);
});
});