What is the best way to verify authentication token of different type of users?

Viewed 190

Let's say I have 10 routes and each route is accessible to only a specific type of user. When a user logins, a token is generated.These user tokens are generated with their _id and a token secret which is stored in .env file.

Normally token verification for each user type is done with separate functions because different type of user has different token secret. For example, user1's token secret maybe TOKEN_SECRET_USER2 = 6ygfewf6hj, and user2's token maybe TOKEN_SECRET_USER1 = 87uhjkaf89.

And when any request is made to a route, the user token is verified to see if the user can access that route or not.

Here's two example route accessible to different user types,

// Route accessible to user type 1
router.get("/foo", verifyTokenUSER1, async (req, res) => {
    // All the good stuff
});

// Route accessible to user type 2
router.post("/bar", verifyTokenUSER2, async (req, res) => {
    // All the good stuff
});

Here's some method of verification module looks like,

// Verification for user 1
const verifyTokenUSER1 = (req, res, next) => {

    const token = req.header("auth-token");
    if (!token) return res.status(401).send();

    try {
        jwt.verify(token, process.env.TOKEN_SECRET_USER1);

        next();
    } catch (err) {
        res.status(401).send();
    }
};

// Verification for user 2
const verifyTokenUSER2 = (req, res, next) => {

    const token = req.header("auth-token");
    if (!token) return res.status(401).send();

    try {
        jwt.verify(token, process.env.TOKEN_SECRET_USER2);

        next();
    } catch (err) {
        res.status(401).send();
    }
};

As you can see, there's only one change in the above methods, which is the access token secret of the user types.

I would like to verify them using 1(one) single function if possible. But I can't pass any value as parameter to the verify methods. So, how can I remove duplication here?

1 Answers

You actually can pass a parameter to the verify method if you use bind (mdn):

// Route accessible to user type 1
router.get("/foo", verifyToken.bind(null, process.env.TOKEN_SECRET_USER1), async (req, res) => {
    // All the good stuff
});

// Route accessible to user type 2
router.post("/bar", verifyToken.bind(null, process.env.TOKEN_SECRET_USER2), async (req, res) => {
    // All the good stuff
});

the bind method of a function here is receiving 2 parameters: first, the this context for the function, and second, the first argument. It returns a new function that will receive the provided token as the first argument, and will receive req, res, next as the next arguments.

const verifyToken = (tokenSecret, req, res, next) => {

    const token = req.header("auth-token");
    if (!token) return res.status(401).send();

    try {
        jwt.verify(token, tokenSecret);

        next();
    } catch (err) {
        res.status(401).send();
    }
};

Another way to do this that is equivalent is building a verification method "factory" which returns a verification function that has a closure (mdn) on the token:


// Route accessible to user type 1
router.get("/foo", getTokenVerifier(process.env.TOKEN_SECRET_USER1), async (req, res) => {
    // All the good stuff
});

// Route accessible to user type 2
router.post("/bar", getTokenVerifier(process.env.TOKEN_SECRET_USER2), async (req, res) => {
    // All the good stuff
});

// this function returns a new function, with a closure on the provided tokenSecret
const getTokenVerifier = (tokenSecret) => {
  return (req, res, next) => {
    const token = req.header("auth-token");
    if (!token) return res.status(401).send();

    try {
        jwt.verify(token, tokenSecret);

        next();
    } catch (err) {
        res.status(401).send();
    }
  }

}
Related