CodeVetter · Repo Unpack · Contributor handoff
auth0/node-jsonwebtoken
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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.