# Contributor handoff — auth0/node-jsonwebtoken

> Canonical page: https://codevetter.com/unpack/jsonwebtoken/agent-handoff

# Contributor handoff — auth0/node-jsonwebtoken

Scanned commit b924272f29192e12926b5414546f7c5bfcc9579d (2026-06-25). Generated by codevetter codevetter 1.12.0 on 2026-09-22.

Treat this as a security library first and a JavaScript project second: the check ordering in verify.js and the guard cascade in sign.js are the product, and reordering them is never a safe refactor even when it looks like cleanup. Style is intentionally conservative CommonJS ES5/ES6 with no build step, no TypeScript, no async/await, and no strict mode — match it rather than modernize it. Read the four root JS files and `lib/validateAsymmetricKey.js` before touching anything; they are under 600 lines combined. Commits must be Conventional Commits because commitlint gates PRs via husky locally and a workflow in CI, and because semantic-release derives the version from them.
- READ FIRST, in order: `index.js`, `sign.js`, `verify.js`, `decode.js`, `lib/validateAsymmetricKey.js`. That is the entire library — under 600 lines total — and everything else in `lib/` is a three-line helper. — [index.js](https://github.com/auth0/node-jsonwebtoken/blob/b924272f29192e12926b5414546f7c5bfcc9579d/index.js) [sign.js](https://github.com/auth0/node-jsonwebtoken/blob/b924272f29192e12926b5414546f7c5bfcc9579d/sign.js) [verify.js](https://github.com/auth0/node-jsonwebtoken/blob/b924272f29192e12926b5414546f7c5bfcc9579d/verify.js) [decode.js](https://github.com/auth0/node-jsonwebtoken/blob/b924272f29192e12926b5414546f7c5bfcc9579d/decode.js) [lib/validateAsymmetricKey.js](https://github.com/auth0/node-jsonwebtoken/blob/b924272f29192e12926b5414546f7c5bfcc9579d/lib/validateAsymmetricKey.js)
- RUN: `npm test` for the fast loop (mocha over `test/`), `npm run coverage` before proposing a merge (nyc enforces 95/95/100/95 and will fail the command), and `npm run lint` manually since no workflow runs it. — [package.json#L21-27](https://github.com/auth0/node-jsonwebtoken/blob/b924272f29192e12926b5414546f7c5bfcc9579d/package.json#L21-L27) [package.json#L6-20](https://github.com/auth0/node-jsonwebtoken/blob/b924272f29192e12926b5414546f7c5bfcc9579d/package.json#L6-L20) [.github/workflows/test.yml#L31-38](https://github.com/auth0/node-jsonwebtoken/blob/b924272f29192e12926b5414546f7c5bfcc9579d/.github/workflows/test.yml#L31-L38)
- COMMIT FORMAT: Conventional Commits are mandatory. `.husky/commit-msg` runs `npx --no -- commitlint --edit $1` locally and `commitlint.yml` re-validates every commit in a PR range; `@commitlint/config-conventional` is the ruleset. — [.husky/commit-msg](https://github.com/auth0/node-jsonwebtoken/blob/b924272f29192e12926b5414546f7c5bfcc9579d/.husky/commit-msg) [.commitlintrc.json](https://github.com/auth0/node-jsonwebtoken/blob/b924272f29192e12926b5414546f7c5bfcc9579d/.commitlintrc.json) [.github/workflows/commitlint.yml#L18-20](https://github.com/auth0/node-jsonwebtoken/blob/b924272f29192e12926b5414546f7c5bfcc9579d/.github/workflows/commitlint.yml#L18-L20)
- STYLE: 2-space indent enforced by both `.editorconfig` and the eslint `indent` rule; no semicolon rule, no quote rule. eslint is pinned at v4 with `ecmaVersion: 6`, so avoid syntax newer than ES2015 in new code paths or the lint run will fail to parse. — [.editorconfig](https://github.com/auth0/node-jsonwebtoken/blob/b924272f29192e12926b5414546f7c5bfcc9579d/.editorconfig) [.eslintrc.json#L1-17](https://github.com/auth0/node-jsonwebtoken/blob/b924272f29192e12926b5414546f7c5bfcc9579d/.eslintrc.json#L1-L17) [package.json#L60](https://github.com/auth0/node-jsonwebtoken/blob/b924272f29192e12926b5414546f7c5bfcc9579d/package.json#L60)
- TRAP — check ordering: in verify.js, lines 144 (allow-list), 148-152 (key-type symmetry), and 154-160 (key/alg matrix) must all remain before line 165 (`jws.verify`). Moving or short-circuiting any of them is a vulnerability, not a refactor. — [verify.js#L144-172](https://github.com/auth0/node-jsonwebtoken/blob/b924272f29192e12926b5414546f7c5bfcc9579d/verify.js#L144-L172)
- TRAP — dual calling convention: every change to sign or verify must work identically in sync (throws) and async (callback) mode. `test/test-utils.js` asserts byte-equality of tokens and deep-equality of errors across the two, so a one-sided fix will fail loudly. — [test/test-utils.js#L59-118](https://github.com/auth0/node-jsonwebtoken/blob/b924272f29192e12926b5414546f7c5bfcc9579d/test/test-utils.js#L59-L118) [sign.js#L103-108](https://github.com/auth0/node-jsonwebtoken/blob/b924272f29192e12926b5414546f7c5bfcc9579d/sign.js#L103-L108) [verify.js#L34-43](https://github.com/auth0/node-jsonwebtoken/blob/b924272f29192e12926b5414546f7c5bfcc9579d/verify.js#L34-L43)
- TRAP — `return` inside `forEach`: the existing `options_to_payload` loop already has this bug (a `return failure(...)` that does not abort the function). Do not copy that pattern into new claim-copy logic, and be aware that fixing it changes observable async callback behavior. — [sign.js#L217-225](https://github.com/auth0/node-jsonwebtoken/blob/b924272f29192e12926b5414546f7c5bfcc9579d/sign.js#L217-L225)
- TRAP — Node-conditional code: three `semver.satisfies` booleans are evaluated at module load. Code behind `ASYMMETRIC_KEY_DETAILS_SUPPORTED` and `RSA_PSS_KEY_DETAILS_SUPPORTED` will not execute on your local Node unless it is new enough, and is excluded from coverage — verify such changes against the CI matrix (16/18/20/22/24), not just locally. — [lib/asymmetricKeyDetailsSupported.js](https://github.com/auth0/node-jsonwebtoken/blob/b924272f29192e12926b5414546f7c5bfcc9579d/lib/asymmetricKeyDetailsSupported.js) [lib/rsaPssKeyDetailsSupported.js](https://github.com/auth0/node-jsonwebtoken/blob/b924272f29192e12926b5414546f7c5bfcc9579d/lib/rsaPssKeyDetailsSupported.js) [lib/validateAsymmetricKey.js#L32-65](https://github.com/auth0/node-jsonwebtoken/blob/b924272f29192e12926b5414546f7c5bfcc9579d/lib/validateAsymmetricKey.js#L32-L65) [.github/workflows/test.yml#L16-19](https://github.com/auth0/node-jsonwebtoken/blob/b924272f29192e12926b5414546f7c5bfcc9579d/.github/workflows/test.yml#L16-L19)
- SAFE EDIT BOUNDARY: adding a claim check, a validated option, or a test is low-risk and well-patterned. Changing key coercion (`createPrivateKey`/`createPublicKey`/`createSecretKey` cascades), the algorithm arrays, or `validateAsymmetricKey` is high-risk and touches published security behavior. — [sign.js#L114-138](https://github.com/auth0/node-jsonwebtoken/blob/b924272f29192e12926b5414546f7c5bfcc9579d/sign.js#L114-L138) [verify.js#L120-160](https://github.com/auth0/node-jsonwebtoken/blob/b924272f29192e12926b5414546f7c5bfcc9579d/verify.js#L120-L160) [lib/validateAsymmetricKey.js#L4-30](https://github.com/auth0/node-jsonwebtoken/blob/b924272f29192e12926b5414546f7c5bfcc9579d/lib/validateAsymmetricKey.js#L4-L30)
- DO NOT EDIT casually: `CHANGELOG.md` is generated by `npm run update-changelog` during the prepare-release workflow, and `package.json` `version` is bumped by `npm version --no-git-tag-version` in that same workflow. Hand-editing either will conflict with the release automation. — [.github/workflows/prepare-release.yml#L53-59](https://github.com/auth0/node-jsonwebtoken/blob/b924272f29192e12926b5414546f7c5bfcc9579d/.github/workflows/prepare-release.yml#L53-L59) [package.json#L26](https://github.com/auth0/node-jsonwebtoken/blob/b924272f29192e12926b5414546f7c5bfcc9579d/package.json#L26)
- RELEASE SHAPE: pushing to master triggers `prepare-release.yml`, which opens a `release/vX.Y.Z` PR titled `chore(release): X.Y.Z`; merging that PR matches the `startsWith(..., 'chore(release):')` guard and triggers `release.yml` to run `npx semantic-release`. The two workflows are mutually exclusive by that guard. — [.github/workflows/prepare-release.yml#L19-20](https://github.com/auth0/node-jsonwebtoken/blob/b924272f29192e12926b5414546f7c5bfcc9579d/.github/workflows/prepare-release.yml#L19-L20) [.github/workflows/release.yml#L17](https://github.com/auth0/node-jsonwebtoken/blob/b924272f29192e12926b5414546f7c5bfcc9579d/.github/workflows/release.yml#L17) [.github/workflows/prepare-release.yml#L61-79](https://github.com/auth0/node-jsonwebtoken/blob/b924272f29192e12926b5414546f7c5bfcc9579d/.github/workflows/prepare-release.yml#L61-L79)
- New public API or option changes must be reflected in `README.md`, which is the only user documentation and is structured as `jwt.sign` / `jwt.verify` / `jwt.decode` / Errors & Codes / Algorithms supported. — [README.md#L23-25](https://github.com/auth0/node-jsonwebtoken/blob/b924272f29192e12926b5414546f7c5bfcc9579d/README.md#L23-L25) [README.md#L126](https://github.com/auth0/node-jsonwebtoken/blob/b924272f29192e12926b5414546f7c5bfcc9579d/README.md#L126) [README.md#L246](https://github.com/auth0/node-jsonwebtoken/blob/b924272f29192e12926b5414546f7c5bfcc9579d/README.md#L246) [README.md#L276](https://github.com/auth0/node-jsonwebtoken/blob/b924272f29192e12926b5414546f7c5bfcc9579d/README.md#L276) [README.md#L355](https://github.com/auth0/node-jsonwebtoken/blob/b924272f29192e12926b5414546f7c5bfcc9579d/README.md#L355)
- Any PR touching `lib/`, `sign.js`, or `verify.js` will require review from `@auth0/project-iam-protocols-engineer-codeowner` — the CODEOWNERS glob is `*`, so every path is covered. — [CODEOWNERS](https://github.com/auth0/node-jsonwebtoken/blob/b924272f29192e12926b5414546f7c5bfcc9579d/CODEOWNERS)
- Do not add PEM fixtures casually: 17 already exist covering RSA, RSA-PSS (valid and invalid salt length), EC on three curves, DSA, and invalid public keys — reuse them via the `loadKey(filename)` helper convention. — [test/jwt.asymmetric_signing.tests.js#L10-12](https://github.com/auth0/node-jsonwebtoken/blob/b924272f29192e12926b5414546f7c5bfcc9579d/test/jwt.asymmetric_signing.tests.js#L10-L12) [test/validateAsymmetricKey.tests.js#L10-14](https://github.com/auth0/node-jsonwebtoken/blob/b924272f29192e12926b5414546f7c5bfcc9579d/test/validateAsymmetricKey.tests.js#L10-L14)
- Security vulnerabilities must not be filed as GitHub issues — the README directs them to Auth0's Responsible Disclosure Program. An agent discovering one should report it to the user rather than opening a public issue. — [README.md#L386-388](https://github.com/auth0/node-jsonwebtoken/blob/b924272f29192e12926b5414546f7c5bfcc9579d/README.md#L386-L388)

Every claim links to its cited files at the pinned commit. Citations identify source evidence; they do not establish runtime correctness. Independent CodeVetter scan — not reviewed by upstream maintainers.

## Public product links

- [CodeVetter](https://codevetter.com/)
- [Download](https://codevetter.com/download)
- [Documentation](https://codevetter.com/docs/)
- [Source](https://github.com/Codevetter/codevetter)
