Can someone please explain why this code works when in the function verifyUser, the function verifyToken is passed a callback function as the fourth argument. But if we look at the verifyToken function, it only has 3 parameters/arguments. How does this work????
import jwt from "jsonwebtoken";
import { createError } from "../utils/error.js";
export const verifyToken = (req, res, next) => {
const token = req.cookies.access_token;
if (!token) {
return next(createError(401, "You are not authenticated!"));
}
//Verifying if token is correct
//Error (err) or token data (user) are returned by verify function
jwt.verify(token, process.env.JWT, (err, user) => {
if (err) return next(createError(403, "Token is not valid!"));
req.user = user;
next();
});
};
export const verifyUser = (req, res, next) => {
verifyToken(req, res, next, () => {
if (req.user.id === req.params.id || req.user.isAdmin) {
next();
} else {
return next(createError(403, "You are not authorized!"));
}
});
};
CodePudding user response:
I went digging in Express's source code and found this documentation
Once the
next()
function is invoked, just like middleware it will continue on to execute the route, or subsequent parameter functions.
So it looks like next()
first tries to call a callback argument if it it's passed, then tries to call the next middleware.
I think the reason they did this, is to hardcode or chain a call to verifyToken
before verifyUser
without changing their implementation of next()
which would jump to the next middleware when called in verifyToken
instead of continuing verifyUser
.
I wrote an example script that uses the same idea here:
function printResult(object, next) {
console.log("The final results is: " object.result);
next();
}
function printLog(object, next){
console.log('Running printLog')
console.log('The current result is: ' object.result)
next();
}
function add5(object, next) {
// Chain printLog before add5 so that it always runs before it
printLog(object, next, () => {
console.log('Now running add5')
object.result = object.result 5;
next();
})
}
function addOne(object, next) {
console.log('Running addOne')
object.result = object.result 1;
next();
}
function callStack(stack, object) {
function next(index, object) {
return function runNext() {
let caller = runNext.caller
if(caller.name !== '' && caller.arguments[2]) {
caller.arguments[2]()
} else if (stack[index 1]){
stack[index 1](object, next(index 1, object));
}
}
}
stack[0](object, next(0, object));
}
const stack = [addOne, add5, printResult]
const object = {result: 0}
callStack(stack, object)
Note that the anonymous function check caller.name !== ''
is there because checking the arguments property of an anonymous function throws an error.
More thorough explanation of why this is important.
So if you're app is like this there's no problem:
- Middleware 1
next()
goes to next middleware
- verifyToken (Middleware 2)
next()
goes to next middleware
- Middleware 3
But if it was like this and next()
didn't call the parameter function:
- Middleware 1
next()
goes to next middleware
verifyUser
(Middleware 2)- calls
verifyToken
next()
inverifyToken
goes to next middleware <- This is an issue
- Continue
verifyUser
<- Because this never happens next()
goes to next middleware
- calls
- Middleware 3
So they let next()
call a parameter function:
- Middleware 1
next()
goes to next middleware
verifyUser
(Middleware 2)- calls
verifyToken
withverifyUser
's implementation as a callbackveryifyCB
(jus an alias for th example)next()
inverifyToken
checks for a callback, findsverifyCB
and calls it
next()
inverifyCB
goes to next middleware
- calls
- Middleware 3
CodePudding user response:
Fourth argument is in arguments
object. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments