I have a strange problem, or maybe I don't understand how JWT works in Express context.
var express = require('express')
var app = express();
var expressJWT = require('express-jwt');
var jwt = require('jsonwebtoken');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var unless = require('express-unless');
app.set('secret', 'some secret');
app.use(cookieParser());
app.use(bodyParser.urlencoded({ extended: false }));
app.use("/", expressJWT({secret:app.get('secret')})
.unless({
path:[
'/',
'/foo',
'/login'
]}
));
// my custom route middleware to verify a token
app.use(function(req, res, next) {
console.log("------------------------------------");
console.log("route middleware to verify a token");
console.log("");
// check header or url parameters or post parameters for token
var token = req.body.access_token || req.query.access_token || req.headers['x-access-token'] || req.cookies.access_token;
console.log("req.cookies.access_token:", req.cookies.access_token);
console.log("token:", token);
// decode token
if (token) {
// verifies secret and checks exp
jwt.verify(token, app.get('secret'), function(err, decoded) {
if (err) {
console.log("jwt.verify ERROR")
return res.json({ success: false, message: 'Failed to authenticate token.', err:err });
} else {
console.log("jwt.verify OK")
// 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.'
});
}
});
app.get('/', function (req, res) {
res.send('Hello World!. /foo is open, /bar is protected. Login at /login')
})
app.get('/foo', function (req, res) {
res.send('Foo')
})
app.get('/bar', function (req, res) {
res.send('Foo')
})
app.get('/login', function (req, res) {
var username = 'mock_username';
var myToken = jwt.sign({username:username}, app.get('secret'));
res.cookie('access_token', myToken).send("logged in, check cookie");
})
app.listen(3000, function () {
console.log('Example app listening on port 3000!')
})
I'm setting up a JWT token and saving it to cookie in the /login route. This works and the token is set if I check cookie in the browser (dev tools in Chrome).
I visit the / or /foo route (unprotected as specified with unless), and browser displays correct result, but the console still throws the UnauthorizedError. Why is the error showing in the console if I explicitly marked this as unprotected route with "unless"?
I visit /bar route (protected), my middleware is not being called, I get the UnauthorizedError both in console and the browser. How do I make sure the middleware does get triggered here, and how do I provide the access to this route if the token is indeed found and verified in my middleware?
I visit the / or /foo route (unprotected as specified with unless), and browser displays correct result, but the console still throws the UnauthorizedError. Why is the error showing in the console if I explicitly marked this as unprotected route with "unless"?
By specifiying unless
, you made the /
and /foo
route unprotected from app.use("/", expressJWT(...)
only, not from subsequent middlewares
. The request will pass to your custom middlware too.
I visit /bar route (protected), my middleware is not being called, I get the UnauthorizedError both in console and the browser. How do I make sure the middleware does get triggered here, and how do I provide the access to this route if the token is indeed found and verified in my middleware?
Because, the application got crashed at app.use("/", expressJWT(...))
, when it could not find authorization token
. Hence, it could not reach to your custom middleware.
Possible solution : 1
Since JWT
token is stored in cookie
in your case, you can set getToken
method to get the token and let express-jwt
to verify it, and remove your custom middleware altogether.
e.g.
app.use("/", expressJWT({
secret : app.get('secret'),
getToken: function fromCookie (req) {
var token = req.cookies.access_token || req.body.access_token || req.query.access_token || req.headers['x-access-token'] ;
if (token) {
return token;
}
return null;
}
}).unless({
path:[
'/',
'/foo',
'/login'
]}
));
and handle the error
app.use(function (err, req, res, next) {
if (err.name === 'UnauthorizedError') {
return res.status(403).send({
success: false,
message: 'No token provided.'
});
}
});
Possible Solution : 2
You can do the custom jwt
verification by implementing your custom middleware (that you have already done). Then there is no need of the following middleware at all.
Remove the following line.
app.use("/", expressJWT({secret:app.get('secret')}).unless(...)
And for protecting and unprotecting the routes, put unprotected
routes before the the custom middleware and protected
route after custom middleware (naive way).
Hope it helps you.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With