Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JWT UnauthorizedError: No authorization token was found (GET request with cookie)

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?

like image 244
Ska Avatar asked Jan 27 '17 06:01

Ska


1 Answers

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.

like image 150
Mukesh Sharma Avatar answered Sep 25 '22 21:09

Mukesh Sharma