Description
The callback to jwt.sign() is executed twice when there is an error Bad "options.issuer" option. The payload already has an "iss" property. I would expect the callback to only be executed once.
Reproduction
// a.mjs
import jwt from "jsonwebtoken";
jwt.sign(
{
iss: "bar",
iat: 1757476476,
},
"secret",
{
algorithm: "HS256",
issuer: "foo",
},
(err, asyncSigned) => {
console.log("callback called:", asyncSigned ?? err?.message);
},
);
$> npm init -y && npm add jsonwebtoken
$> node a.mjs
callback called: Bad "options.issuer" option. The payload already has an "iss" property.
callback called: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJiYXIiLCJpYXQiOjE3NTc0NzY0NzZ9.TkSecbZDocHnjwnuDhQRXNvRkxsERNGBkRc6P0wHlNY
Notice that "callback called" is printed twice, while I would expect it to be printed only once.
Environment
jsonwebtoken 9.0.2 on Darwin 24.6.0 arm64 arm
Other information
It looks like the error is here:
|
Object.keys(options_to_payload).forEach(function (key) { |
|
const claim = options_to_payload[key]; |
|
if (typeof options[key] !== 'undefined') { |
|
if (typeof payload[claim] !== 'undefined') { |
|
return failure(new Error('Bad "options.' + key + '" option. The payload already has an "' + claim + '" property.')); |
|
} |
|
payload[claim] = options[key]; |
|
} |
|
}); |
Typically, return failure() throws which exits the forEach and the function. But when a callback is provided, it only returns from the forEach. The rest of the keys are checked and the rest of the function still runs.