auth0/node-jsonwebtoken
JsonWebToken implementation for node.js http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html
Healthy across the board
weakest axisPermissive license, no critical CVEs, actively maintained — safe to depend on.
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓Last commit 5d ago
- ✓40+ active contributors
- ✓Distributed ownership (top contributor 26% of recent commits)
- ✓MIT licensed
- ✓CI configured
- ✓Tests present
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the “Healthy” badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/auth0/node-jsonwebtoken)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/auth0/node-jsonwebtoken on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: auth0/node-jsonwebtoken
Generated by RepoPilot · 2026-05-06 · Source
Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/auth0/node-jsonwebtoken shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
Verdict
GO — Healthy across the board
- Last commit 5d ago
- 40+ active contributors
- Distributed ownership (top contributor 26% of recent commits)
- MIT licensed
- CI configured
- Tests present
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live auth0/node-jsonwebtoken
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/auth0/node-jsonwebtoken.
What it runs against: a local clone of auth0/node-jsonwebtoken — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in auth0/node-jsonwebtoken | Confirms the artifact applies here, not a fork |
| 2 | License is still MIT | Catches relicense before you depend on it |
| 3 | Default branch master exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 35 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of auth0/node-jsonwebtoken. If you don't
# have one yet, run these first:
#
# git clone https://github.com/auth0/node-jsonwebtoken.git
# cd node-jsonwebtoken
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of auth0/node-jsonwebtoken and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "auth0/node-jsonwebtoken(\\.git)?\\b" \\
&& ok "origin remote is auth0/node-jsonwebtoken" \\
|| miss "origin remote is not auth0/node-jsonwebtoken (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
&& ok "license is MIT" \\
|| miss "license drift — was MIT at generation time"
# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
&& ok "default branch master exists" \\
|| miss "default branch master no longer exists"
# 4. Critical files exist
test -f "index.js" \\
&& ok "index.js" \\
|| miss "missing critical file: index.js"
test -f "sign.js" \\
&& ok "sign.js" \\
|| miss "missing critical file: sign.js"
test -f "verify.js" \\
&& ok "verify.js" \\
|| miss "missing critical file: verify.js"
test -f "decode.js" \\
&& ok "decode.js" \\
|| miss "missing critical file: decode.js"
test -f "lib/validateAsymmetricKey.js" \\
&& ok "lib/validateAsymmetricKey.js" \\
|| miss "missing critical file: lib/validateAsymmetricKey.js"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 35 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~5d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/auth0/node-jsonwebtoken"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
TL;DR
Auth0's production-grade JWT (JSON Web Token) implementation for Node.js that signs, verifies, and decodes tokens compliant with RFC 7519. It handles both symmetric (HMAC) and asymmetric (RSA, ECDSA) signing algorithms with support for claims validation (exp, nbf, aud, iss, sub, jti), key rotation via kid headers, and error cases like token expiration or invalid signatures. Single-entry monolithic structure: index.js exports the main API (sign/verify/decode), with sign.js and decode.js handling core logic, verify.js for validation, and lib/ containing error classes (JsonWebTokenError, TokenExpiredError, NotBeforeError) plus utility modules for key validation and timespan parsing. Tests are colocated in test/ with one file per claim type.
Who it's for
Backend Node.js developers building authentication systems, API gateways, or microservices that need standards-compliant JWT issuance and verification. Also used by Auth0 platform itself and developers integrating with Auth0 or similar OAuth/OIDC providers.
Maturity & risk
Production-ready and actively maintained at v9.0.3. The repo has 95%+ code coverage enforced via nyc, comprehensive claim-specific test files (test/claim-*.test.js), GitHub Actions CI running on every commit (test.yml, sca-scan.yml, release.yml), and semantic versioning with changelogs. This is a battle-tested library used by thousands of Node.js applications.
Low risk overall—only 8 production dependencies (jws, lodash utilities, ms, semver) with no bloat. Last version bump to 9.0.3 indicates active maintenance. Single maintainer risk exists (Auth0 org maintains it), but the library is stable with slow-changing API surface. Breaking changes are rare and documented in wiki migration notes (v7→v8, v8→v9).
Active areas of work
No specific PR or milestone data is visible in the provided snapshot, but the semantic-release and commitlint setup (v20.3.1, v25.0.2) indicates automated versioning. The presence of recent husky (v9.1.7) and ESLint (v4.19.1) configs suggests ongoing quality maintenance. The sca-scan.yml workflow implies active security scanning.
Get running
git clone https://github.com/auth0/node-jsonwebtoken.git
cd node-jsonwebtoken
npm install
npm test
Daily commands:
npm test # Run mocha test suite
npm run coverage # Generate NYC coverage reports (targets 95% lines/statements)
npm run lint # Run ESLint on all files
npm run prepare # Install husky git hooks
Map of the codebase
index.js— Main entry point that exports sign, verify, and decode functions; all consumers start heresign.js— Core JWT signing logic; implements token creation with algorithm selection and payload encodingverify.js— Core JWT verification logic; validates signatures, expiration, and claims—security-criticaldecode.js— Token decoding without verification; used to inspect JWT structure safelylib/validateAsymmetricKey.js— Validates RSA/ECDSA key formats and configurations; prevents cryptographic misconfigurationslib/JsonWebTokenError.js— Custom error hierarchy; all error handling flows through this for consistent failure modes
Components & responsibilities
- sign.js (jws, Node.js crypto) — Encodes payload into a signed JWT using specified algorithm and private key; applies claim transformations (exp, iat, nbf)
- Failure mode: Throws JsonWebTokenError if algorithm unsupported, key invalid, or payload serialization fails
- verify.js (jws, Node.js crypto) — Validates JWT signature and all registered claims (expiration, not-before, audience, issuer); returns decoded payload
- Failure mode: Throws TokenExpiredError for exp violations, NotBeforeError for nbf, or JsonWebTokenError for signature/claim mismatches
- decode.js (jws) — Parses JWT structure without verification; useful for inspecting tokens before validation
- Failure mode: Throws JsonWebTokenError if JWT format is invalid (missing parts)
- validateAsymmetricKey.js (Node.js crypto key details APIs) — Runtime validation of RSA, ECDSA, and EdDSA keys; checks key format, size, and Node.js compatibility
- Failure mode: Throws JsonWebTokenError if key is malformed, too small, or unsupported on this Node.js version
- Error hierarchy (JsonWebTokenError, Token — undefined
How to make changes
Add support for a new registered claim
- Create a test file to define claim validation logic (e.g., test/claim-xxx.test.js) (
test/claim-xxx.test.js) - In sign.js, add payload transformations under the registerPayloadTransforms section (
sign.js) - In verify.js, add claim validation in the verify options handler (verifyOptions.xxx) (
verify.js) - Document the claim in README.md with examples and validation rules (
README.md)
Add a new asymmetric signing algorithm
- Check lib/psSupported.js and lib/asymmetricKeyDetailsSupported.js for Node.js capability detection pattern (
lib/psSupported.js) - Create feature detection helper in lib/ folder (e.g., lib/xxxSupported.js) (
lib/psSupported.js) - Update sign.js to handle the new algorithm in the encoding switch statement (
sign.js) - Update verify.js to handle the new algorithm in the verification switch statement (
verify.js) - Add test case in test/jwt.asymmetric_signing.tests.js with before/after key fixtures (
test/jwt.asymmetric_signing.tests.js)
Add stricter key validation for a specific algorithm
- Update lib/validateAsymmetricKey.js to add algorithm-specific checks in the switch statement (
lib/validateAsymmetricKey.js) - Add test cases in test/validateAsymmetricKey.tests.js to cover edge cases (
test/validateAsymmetricKey.tests.js) - Call validateAsymmetricKey() from sign.js and verify.js where the algorithm is handled (
sign.js)
Why these technologies
- jws (JSON Web Signature) — Provides low-level JWS encoding/decoding and signature operations; jsonwebtoken wraps it with JWT-specific claims validation
- Node.js crypto module — Native cryptographic operations (RSA, ECDSA, EdDSA, HMAC); avoids external C++ bindings for better compatibility
- Mocha + NYC (code coverage) — Standard Node.js testing stack with strict coverage thresholds (95% lines, 100% functions) to ensure security-critical paths are tested
Trade-offs already made
-
Synchronous and asynchronous APIs for sign/verify
- Why: Supports both callback-heavy (legacy) and Promise-based (modern) codebases without breaking changes
- Consequence: Doubles API surface area; maintainers must test both paths; async version wraps sync with setImmediate
-
Custom error types (TokenExpiredError, NotBeforeError) extending Error
- Why: Allows callers to catch specific JWT failures without string matching
- Consequence: Callers must check error instanceof rather than error.code; slightly less performant than error codes
-
Runtime detection of Node.js feature support (psSupported, asymmetricKeyDetailsSupported)
- Why: Supports both older and newer Node.js versions with graceful degradation
- Consequence: Runtime overhead on each operation; requires careful version-gating logic
Non-goals (don't propose these)
- Does not implement JWE (JSON Web Encryption); only JWS (signatures)
- Does not validate certificates or certificate chains; caller must provide trusted public keys
- Does not implement custom header extensions or JWK Set (RFC 7517) fetching
- Does not provide token refresh or session management; it is stateless token generation/validation only
- Does not support key rotation strategies or key versioning (kid header is accepted but not enforced)
Traps & gotchas
Key passphrase handling: When using a private key with a passphrase, you must pass { key: privateKeyPEM, passphrase: '...' } as the secretOrPrivateKey AND specify the algorithm option explicitly—omitting either causes silent failures. Claim injection only on objects: If you pass a Buffer or string payload, expiresIn/notBefore/audience/issuer/subject/jwtid are ignored (not injected); you must pass a plain object. No automatic time sync: The library uses local system time for exp/nbf validation—clock skew can cause unexpected validation failures (mitigate with clockTimestamp or clockTolerance options in verify()). RSA key size enforcement: RSA private keys < 2048 bits are rejected unless allowInsecureKeySizes: true is explicitly set (security default). Synchronous vs async: sign() has both callback and sync modes; if you forget the callback, it returns synchronously, which can block the event loop on large keys.
Architecture
Concepts to learn
- JSON Web Token (JWT) Structure — Understanding the three-part structure (header.payload.signature) is essential to using this library correctly—decode() lets you inspect all parts, sign() constructs them, and verify() validates the signature cryptographically
- Registered Claims (exp, nbf, aud, iss, sub, jti, iat) — This library implements validation for RFC 7519 registered claims; knowing which claims do what (e.g., exp = expiration time, nbf = not-before time) is required to configure tokens correctly and understand why verification fails
- Asymmetric vs Symmetric Cryptography — The library supports both HMAC (symmetric—single secret) and RSA/ECDSA (asymmetric—public/private key pair); choosing the right algorithm depends on whether you need decentralized verification (public key) or centralized trust (shared secret)
- Key ID (kid) Header — The optional kid field in the JWT header allows rotating between multiple signing keys without breaking existing tokens; used heavily in OIDC and multi-tenant systems where key rotation is frequent
- Token Expiration & Clock Skew — The exp claim and NotBeforeError handling require understanding that distributed systems have clock drift; this library provides clockTolerance and clockTimestamp options to handle time synchronization issues gracefully
- PEM-Encoded Keys & Key Objects — The library accepts both PEM-encoded strings and Node.js KeyObject instances for asymmetric signing; understanding PEM format and passphrase-protected keys is critical when integrating with real cryptographic material
- JWS (JSON Web Signature) — jsonwebtoken wraps jws library for the actual signing/verification; understanding JWS (RFC 7515) helps you debug why signatures fail and what verify() is actually checking
Related repos
brianloveswords/node-jws— The JWS (JSON Web Signature) implementation that jsonwebtoken depends on for the low-level signing/verification cryptographyauth0/node-jwks-rsa— Companion Auth0 library for fetching and caching public keys from JWKS endpoints, commonly used with jsonwebtoken in OAuth/OIDC flowsauth0/express-jwt— Express.js middleware that uses jsonwebtoken internally for automatic JWT extraction and verification from HTTP requestsauth0/java-jwt— The Java equivalent of jsonwebtoken, useful for understanding library design patterns across ecosystems and for polyglot architecturesencode/crypt— Alternative Node.js JWT library with slightly different API; useful reference if you need different feature trade-offs or design philosophies
PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add comprehensive test coverage for verify.js with edge cases
The package.json indicates verify.js is a published file in the 'files' array, but there's no dedicated test file (verify.tests.js or similar) in the test directory. The existing test files focus on signing, async operations, and specific claims, but verify.js deserves its own systematic test suite covering error handling, key validation, algorithm mismatches, and malformed tokens. This would improve reliability and catch regressions early.
- [ ] Create test/verify.tests.js with systematic test cases for all verify() code paths
- [ ] Add tests for verify() with invalid keys using lib/validateAsymmetricKey.js validation
- [ ] Add edge case tests: expired tokens, future NBF claims, invalid algorithms, missing required claims
- [ ] Add tests for verify() options (audience, issuer, subject, ignoreExpiration, etc.)
- [ ] Ensure NYC coverage thresholds (95% lines/statements/branches, 100% functions) are met
Add TypeScript type definitions (index.d.ts) for improved DX
The package is widely used but lacks TypeScript definitions, forcing TypeScript consumers to install @types/jsonwebtoken from DefinitelyTyped or use 'any' types. Given the package exports clear APIs (sign, verify, decode) with specific options objects, adding native type definitions would improve developer experience, enable better IDE support, and reduce confusion about function signatures and acceptable options.
- [ ] Create index.d.ts with TypeScript interfaces for sign(), verify(), and decode() functions
- [ ] Define types for all options parameters: SignOptions, VerifyOptions, DecodeOptions with JSDoc comments
- [ ] Add custom error types: JsonWebTokenError, TokenExpiredError, NotBeforeError matching lib/ exports
- [ ] Include type definitions for algorithm support (HS256, RS256, ES256, PS256, etc.)
- [ ] Add a TypeScript test file (test/types.test.ts) to validate type definitions compile and work correctly
Refactor lodash micro-packages into single lodash import for reduced dependencies
The package currently depends on 6 separate lodash micro-packages (lodash.includes, lodash.isboolean, lodash.isinteger, lodash.isnumber, lodash.isplainobject, lodash.isstring, lodash.once) which increases maintenance burden and bundle size. These can be consolidated into a single 'lodash' dependency or replaced with native JavaScript equivalents, reducing the dependency tree from 6 packages to 1 (or 0 if using native alternatives).
- [ ] Audit all usage of lodash micro-packages across sign.js, decode.js, and lib/ files
- [ ] Replace lodash.isstring, lodash.isinteger, lodash.isnumber with native typeof/Number.isInteger() checks
- [ ] Replace lodash.isboolean, lodash.isplainobject with native checks (typeof x === 'boolean', Object.getPrototypeOf(x) === Object.prototype)
- [ ] Consolidate lodash.includes and lodash.once into a single 'lodash' dependency, or use Array.prototype.includes() and self-implement once()
- [ ] Update package.json dependencies, verify all tests pass (npm test), and update CHANGELOG.md
Good first issues
- Add test coverage for verify.js's handling of the
clockToleranceoption with edge cases (e.g., token expiring within tolerance window). Currently test/claim-exp.test.js covers basic expiration but not tolerance threshold boundary conditions. - Create test/header-kid.test.js (similar to test/claim-*.test.js pattern) to thoroughly test kid (key ID) header injection during sign() and retrieval during decode(). The placeholder file exists but likely lacks comprehensive claim mutation tests.
- Document in README.md the exact behavior and gotchas of passing Buffer vs string vs object payloads to sign()—specifically that claim injection (expiresIn, audience, etc.) only works with object payloads. Add code examples showing common mistakes.
Top contributors
- @ziluvatar — 26 commits
- @MitMaro — 20 commits
- @jakelacey2012 — 8 commits
- @panva — 4 commits
- @SamuelSSalazar — 3 commits
Recent commits
02688b9— ci: switch to conventionalcommits preset for semantic-release (#1020) (SamuelSSalazar)cf33b5f— ci: add semantic-release automation (#1015) (SamuelSSalazar)2db36ae— chore: add CODEOWNERS file (#1014) (SamuelSSalazar)ed59e76— chore: bump jws to 4.0.1 (#1007) (julienwoll)bc28861— Release 9.0.2 (#935) (jakelacey2012)96b8906— refactor: use specific lodash packages (#933) (jakelacey2012)ed35062— security: Updating semver to 7.5.4 to resolve CVE-2022-25883 (#932) (jakelacey2012)84539b2— Updating package version to 9.0.1 (#920) (jakelacey2012)a99fd4b— fix(stubs): allow decode method to be stubbed (#876) (jakelacey2012)e1fa9dc— Merge pull request from GHSA-8cf7-32gw-wr33 (edgarchirivella-okta)
Security observations
The jsonwebtoken library maintains a reasonable security posture with proper dependency management practices. No critical vulnerabilities were identified. Key concerns include outdated development dependencies (ESLint, Mocha, NYC) that should be updated to latest versions for better security coverage detection. The core runtime dependencies appear well-maintained. The project uses proper security practices like file exclusion in npm distribution and comprehensive test coverage requirements (95%+). Recommend updating dev tooling to modern versions and removing the deprecated nsp tool in favor of npm audit or similar modern scanning solutions.
- Medium · Outdated ESLint Dependency —
package.json - devDependencies. ESLint version 4.19.1 is significantly outdated (current major version is 8+). This may miss modern security issues and contains known vulnerabilities in the linter itself. Fix: Update ESLint to the latest stable version (>=8.0.0) to benefit from security fixes and modern linting rules. - Medium · Outdated Mocha Test Framework —
package.json - devDependencies. Mocha version 5.2.0 is significantly outdated (current version is 10+). This may contain known vulnerabilities and lack security-related test framework improvements. Fix: Update Mocha to the latest stable version (>=10.0.0) to ensure modern test execution and security improvements. - Medium · Outdated NYC Coverage Tool —
package.json - devDependencies. NYC (code coverage tool) version 11.9.0 is outdated (current version is 15+). This may result in incomplete or inaccurate coverage reporting. Fix: Update nyc to the latest stable version (>=15.0.0) for improved coverage accuracy and security. - Low · Deprecated NSP Package —
package.json - devDependencies. The 'nsp' (Node Security Platform) package in devDependencies is deprecated. It has been superseded by npm audit and other security scanning tools. Fix: Remove the nsp dependency and rely on 'npm audit' or integrate dedicated security scanning tools like Snyk or OWASP Dependency-Check. - Low · Test Files Included in Distribution —
test/*.pem files. Test PEM files and test data are located in the /test directory, which is properly excluded from the npm package via .npmignore. However, review whether test keys should be part of the repository at all. Fix: Consider generating test keys during test setup rather than committing them, or ensure all test keys are clearly marked as non-production test material only.
LLM-derived; treat as a starting point, not a security audit.
Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.