2

I'm currently getting started with ExpressJS and came across a problem with implementing token auth. First of all, here's the code:

const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const mongoose = require('mongoose');
const jsonwebtoken = require('jsonwebtoken');

const config = require('./config');
const User = require('./models/user');

// Connect to mongoose
mongoose.connect(config.db, { useMongoClient: true });
const db = mongoose.connection;

app.use(bodyParser.json());

app.get('/', function(req, res) {
  res.send('Please use /api/ with an existing endpoint.');
});

const router = express.Router();

router.post('/auth', function(req, res) {
  User.findOne({
    name: 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.password != req.body.password) {
        res.json({
          success: false,
          message: 'Authentication failed. Wrong password.'
        });
      } else {
        const token = jsonwebtoken.sign(user, config.secret, { expiresIn: "20 seconds" });

        res.json({
          success: true,
          message: 'Authentication succeeded. Enjoy your token.',
          token: token
        });
      }
    }
  });
});

router.use(function(req, res, next) {
  const token = req.body.token || req.query.token || req.headers['x-access-token'];

  if (token) {
    jsonwebtoken.verify(token, config.secret, function(err, decoded) {
      if (err) {
        res.status(403).json({
          success: false,
          message: 'Failed to authenticate token.'
        });
      } else {
        req.decoded = decoded;
        next();
      }
    });
  } else {
    res.status(403).json({
      success: false,
      message: 'No token provided.'
    });
  }
});

router.get('/', function(req, res) {
  res.send('Please use /api/ with an existing endpoint.');
});

router.get('/users', function(req, res) {
  User.getUsers(function(err, users) {
    if (err)
      throw err;

    res.json(users);
  });
});

router.get('/users/:_id', function(req, res) {
  User.getUserById(req.params._id, function(err, user) {
    if (err)
      throw err;

    res.json(user);
  });
});

router.post('/users', function(req, res) {
  var user = req.body;

  User.addUser(user, function(err, user) {
    if (err)
      throw err;

    res.json(user);
  });
});

router.delete('/users/:_id', function(req, res) {
  var id = req.params._id;

  User.removeUser(id, function(err, user) {
    if (err)
      throw err;

    res.json(user);
  });
});

app.use('/api', router);
app.listen(3000);
console.log('Listening at 3000');

So I try to require a token before using any kind of /api/users/ path. I also have a path /auth/ where you can get your token through authentication. But when using that path (/api/auth/) I also get "No token provided". Of course I want to GET that token there. Of course I didn't provide a token, I have none yet :)

What am I doing wrong? Is it the wrong use of middlewares? Or anything else?

A Second question is if I actually even need to use the express router. I used it because I was following this guide: https://scotch.io/tutorials/authenticate-a-node-js-api-with-json-web-tokens

SVARTBERG
  • 435
  • 1
  • 7
  • 16

3 Answers3

1

Problem is that you are not targeting selected route. It is basically applied to all. It's fine, i will refactor your code and tell you right the way to user middlewares in ExpressJS. Look below for reference to know how i am linking authMiddleware with particular routes.

Structure your app should like this.

app.js
app
    controller
        AppController.js
    middleware
        AuthMiddleware.js
routes
    index.js
    routes.js //configure router here.
views
public
.
.
.

I assume app.js is your main file.

app.js
-----
const express = require('express');
const bodyParser = require('body-parser');
const mongoose = require('mongoose');
const config = require('./config');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
const app = express();

//connect to mongodb
var connect = function(){
    var options = {
        server: {
            socketOptions:{
                keepAlive : 1
            }
        }
    };
    console.log('info', 'connected to mongodb with config url : '+config.db);
    mongoose.connect(config.db,options);
};
connect();
mongoose.connection.on('error',console.log);
mongoose.connection.on('disconnected',connect);

require('./routes/routes')(app);
...
// your custom codes like locale, loggers etc. can go here.
...


routes/routes.js
---------
var index = require('./index');
var customRoute = require('./customRoute');// if you want to further refactor and move your /customRoute routes to a dedicated file.

module.exports = function (app){
    app.use('/',  index);
    //app.use('/customRoute', customRoute);
}

routes/index.js
----------------
var express = require('express');
var router = express.Router();
var ctrl = require('../app/controller/AppController');

var authCheck = require('../app/middlewares/AuthMiddleware');

router.get('/signup',ctrl.signup); //unprotected route
router.get('/login', ctrl.login);  //unprotected route
router.get('/users',authCheck, ctrl.deals); //protected route

app/middleware/AuthMiddleware.js
--------------------------------
module.exports = function(req, res, next){
    //write your logic here to check for token.
    if(token is present ){
        next();
    }else{
        // write logic to redirect to some view or return unauthorized response.
    }
};

So as you can see your application is much better and easy now to manage. If successful login then you can store token in Browser cookie. Write logic for auth check in AuthMiddleware, read cookie value. If cookie data is valid then allow users to proceed with request or else return unauthorized response.

If you want to get started then use express generator. It will give you a basic app to get started and from there experiment and keep learning.

NarendraSoni
  • 2,210
  • 18
  • 26
0

The problem is, when you use the middleware in router, it will be used in all the routes containing the router. In router only the router.use depends on precedence not in your case. Please read this for detailed explanation. You can simply change the use of middleware in another routes, where the middleware must be employed. Please refer to the below code.

const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const mongoose = require('mongoose');
const jsonwebtoken = require('jsonwebtoken');

const config = require('./config');
const User = require('./models/user');

// Connect to mongoose
mongoose.connect(config.db, { useMongoClient: true });
const db = mongoose.connection;

app.use(bodyParser.json());

app.get('/', function(req, res) {
  res.send('Please use /api/ with an existing endpoint.');
});

const router = express.Router();
const userRouter = express.Router();

router.post('/auth', function(req, res) {
  User.findOne({
    name: 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.password != req.body.password) {
        res.json({
          success: false,
          message: 'Authentication failed. Wrong password.'
        });
      } else {
        const token = jsonwebtoken.sign(user, config.secret, { expiresIn: "20 seconds" });

        res.json({
          success: true,
          message: 'Authentication succeeded. Enjoy your token.',
          token: token
        });
      }
    }
  });
});

userRouter.use(function(req, res, next) {
  const token = req.body.token || req.query.token || req.headers['x-access-token'];

  if (token) {
    jsonwebtoken.verify(token, config.secret, function(err, decoded) {
      if (err) {
        res.status(403).json({
          success: false,
          message: 'Failed to authenticate token.'
        });
      } else {
        req.decoded = decoded;
        next();
      }
    });
  } else {
    res.status(403).json({
      success: false,
      message: 'No token provided.'
    });
  }
});

userRouter.get('/', function(req, res) {
  User.getUsers(function(err, users) {
    if (err)
      throw err;

    res.json(users);
  });
});

userRouter.get('/:_id', function(req, res) {
  User.getUserById(req.params._id, function(err, user) {
    if (err)
      throw err;

    res.json(user);
  });
});

useRouter.post('/', function(req, res) {
  var user = req.body;

  User.addUser(user, function(err, user) {
    if (err)
      throw err;

    res.json(user);
  });
});

userRouter.delete('/:_id', function(req, res) {
  var id = req.params._id;

  User.removeUser(id, function(err, user) {
    if (err)
      throw err;

    res.json(user);
  });
});

router.use('/users', userRouter);
app.use('/api', router);
app.listen(3000);
console.log('Listening at 3000');
Tolsee
  • 1,695
  • 14
  • 23
0

I'm very sorry but my code worked as intended. I just tested wrong. I tested with get request but needed to test with post of course. According to the guide I read, the order of the statements is important. So thats why its working.

SVARTBERG
  • 435
  • 1
  • 7
  • 16