{"_id":"@apinecka/jwt","_rev":"2-db88f52e483adf7e99b42c23cafd2d76","name":"@apinecka/jwt","dist-tags":{"latest":"1.0.1"},"versions":{"1.0.0":{"name":"@apinecka/jwt","version":"1.0.0","keywords":["nestjs","jwt","jsonwebtoken","auth"],"author":{"name":"Tomáš Cupák","email":"tomcupak@gmail.com"},"license":"MIT","_id":"@apinecka/jwt@1.0.0","maintainers":[{"name":"tomcupak","email":"tomcupak@gmail.com"}],"homepage":"https://github.com/tomcupak/apinecka-libs/tree/master/libs/jwt#readme","bugs":{"url":"https://github.com/tomcupak/apinecka-libs/issues"},"dist":{"shasum":"bd1f32d451857bf654f75fba40a922c499686984","tarball":"https://registry.npmjs.org/@apinecka/jwt/-/jwt-1.0.0.tgz","fileCount":16,"integrity":"sha512-MSBCHD4sj5DrChhzmKi7Nd2a1+uoAs6L/t2mDZjP6Jdvny52IIJx+aAPeGj4vynZHXQwn7trhnlFirgJxZT6WQ==","signatures":[{"sig":"MEUCIQCN5u9O8YZcu4WZESInyDgarGy5IsV95rGEI4hIjyFgtgIgXKz0TIBjtMaXKcdGOOfB9l1S/Qk5+PltOvUQKaDS2NE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":167162},"main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"gitHead":"98edf21044238a08e2ae6047c72930dead79a240","scripts":{"build":"tsc","clean":"del-cli dist","prepublishOnly":"npm run clean && npm run build"},"_npmUser":{"name":"tomcupak","email":"tomcupak@gmail.com"},"repository":{"url":"git+https://github.com/tomcupak/apinecka-libs.git","type":"git","directory":"libs/jwt"},"_npmVersion":"11.6.1","description":"NestJS module for unified JWT handling: RS512-signed tokens, multi-key rotation via key id (kid), and header/payload helpers.","directories":{},"_nodeVersion":"24.10.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"del-cli":"^7.0.0","typescript":"^5.8.3","@types/node":"^22.15.33","jsonwebtoken":"^9.0.2","@nestjs/common":"^11.1.7","@types/jsonwebtoken":"^9.0.10"},"peerDependencies":{"jsonwebtoken":"^9.0","@nestjs/common":"^11.0.0"},"_npmOperationalInternal":{"tmp":"tmp/jwt_1.0.0_1788442629037_0.2867909881045674","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@apinecka/jwt","version":"1.0.1","description":"NestJS module for unified JWT handling: RS512-signed tokens, multi-key rotation via key id (kid), and header/payload helpers.","keywords":["nestjs","jwt","jsonwebtoken","auth"],"main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"scripts":{"build":"tsc","clean":"del-cli dist","prepublishOnly":"npm run clean && npm run build"},"author":{"name":"Tomáš Cupák","email":"tomcupak@gmail.com"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/tomcupak/apinecka-libs.git","directory":"libs/jwt"},"homepage":"https://github.com/tomcupak/apinecka-libs/tree/master/libs/jwt#readme","bugs":{"url":"https://github.com/tomcupak/apinecka-libs/issues"},"publishConfig":{"access":"public"},"peerDependencies":{"@nestjs/common":"^11.0.0","jsonwebtoken":"^9.0"},"devDependencies":{"@nestjs/common":"^11.1.7","@types/jsonwebtoken":"^9.0.10","@types/node":"^22.15.33","del-cli":"^7.0.0","jsonwebtoken":"^9.0.2","typescript":"^5.8.3"},"gitHead":"cc8e721adb3bf2e60c2ae046afdb902f7b7519e0","_id":"@apinecka/jwt@1.0.1","_nodeVersion":"24.10.0","_npmVersion":"11.6.1","dist":{"integrity":"sha512-93nKdeVh0WG2AItFABabvwKNb0pTsbvacaAA1hwcemA6JjcCoQ4JFx9RFaFTsz+SdMsXbu4XkZUPlZWXa3q88A==","shasum":"fdf7e0053296648c073a809893502af2bf4b723f","tarball":"https://registry.npmjs.org/@apinecka/jwt/-/jwt-1.0.1.tgz","fileCount":16,"unpackedSize":167436,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIG699FFJ6gihRZQx0ZSbHkalfZk4FY/ltjzgiZl2uifmAiAMYS+OounHGGuCtIvOUE9n5eSkwNyl8O7eX7ttJG2R9A=="}]},"_npmUser":{"name":"tomcupak","email":"tomcupak@gmail.com"},"directories":{},"maintainers":[{"name":"tomcupak","email":"tomcupak@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/jwt_1.0.1_1788503087826_0.3766358822330804"},"_hasShrinkwrap":false}},"time":{"created":"2026-09-03T13:37:08.801Z","modified":"2026-09-04T06:24:48.125Z","1.0.0":"2026-09-03T13:37:09.189Z","1.0.1":"2026-09-04T06:24:47.975Z"},"bugs":{"url":"https://github.com/tomcupak/apinecka-libs/issues"},"author":{"name":"Tomáš Cupák","email":"tomcupak@gmail.com"},"license":"MIT","homepage":"https://github.com/tomcupak/apinecka-libs/tree/master/libs/jwt#readme","keywords":["nestjs","jwt","jsonwebtoken","auth"],"repository":{"type":"git","url":"git+https://github.com/tomcupak/apinecka-libs.git","directory":"libs/jwt"},"description":"NestJS module for unified JWT handling: RS512-signed tokens, multi-key rotation via key id (kid), and header/payload helpers.","maintainers":[{"name":"tomcupak","email":"tomcupak@gmail.com"}],"readme":"# @apinecka/jwt\n\nNestJS module for unified JWT handling, built on\n[`jsonwebtoken`](https://www.npmjs.com/package/jsonwebtoken): RS512-signed tokens, multi-key\nrotation via key id (`kid`), and request/payload helpers.\n\n## Install\n\n```bash\nnpm install @apinecka/jwt jsonwebtoken @nestjs/common\n```\n\n## Usage\n\n```ts\nimport { JwtModule } from '@apinecka/jwt'\n\n@Module({\n\timports: [\n\t\tJwtModule.forRoot({\n\t\t\tissuer: 'my-app',\n\t\t\tactiveKid: 'main',\n\t\t\tkeys: [\n\t\t\t\t{ kid: 'main', publicKeyPath: '../../data/keys/public', privateKeyPath: '../../data/keys/private' },\n\t\t\t],\n\t\t}),\n\t],\n})\nexport class AppModule {}\n```\n\n```ts\nimport { JwtService } from '@apinecka/jwt'\n\n@Injectable()\nexport class AuthService {\n\tconstructor(private jwt: JwtService) {}\n\n\tlogin(userId: string) {\n\t\treturn this.jwt.createToken({ sub: userId }, 60 * 60) // expires in 1 hour\n\t}\n\n\tauthenticate(token: string) {\n\t\treturn this.jwt.validateToken<{ sub: string }>(token) // throws when invalid/expired\n\t}\n}\n```\n\n`JwtService` also exposes `getPublicKey()`, returning the active key's public key (e.g. for a\nJWKS-style endpoint).\n\n`JwtModule.forRoot()` registers `JwtService` as a global provider, so it does not need to be\nimported in every feature module.\n\n### Request helpers\n\n`JwtHelpers` provides framework-agnostic helpers for pulling a token out of a request and reading\nits payload without verifying it:\n\n```ts\nimport { JwtHelpers } from '@apinecka/jwt'\n\nconst token = JwtHelpers.extractBearerToken(req.headers) // reads the \"Authorization: Bearer <token>\" header\nconst payload = JwtHelpers.getJWTPayload<{ sub: string }>(token) // decodes the payload without verifying the signature\n```\n\n`JwtHelpers.getReadJwtOptions`/`getWriteJwtOptions` return the `jsonwebtoken` verify/sign options\n(`RS512`, issuer, kid) that `JwtService` itself uses, in case you need to call `jsonwebtoken`\ndirectly. `JwtHelpers.validateToken` is the underlying single-key verify that `JwtService` uses\nfor each candidate key.\n\n### Key rotation\n\n`keys` may list more than one key id. `activeKid` selects which one signs new tokens; it must have\nboth a public and a private key. Every other entry is read-only (public key only) and stays\navailable so tokens it already signed keep validating until they expire — keep the previous key\naround for as long as its longest-lived token can still be presented, then remove it:\n\n```ts\nJwtModule.forRoot({\n\tissuer: 'my-app',\n\tactiveKid: '2026-09',\n\tkeys: [\n\t\t{ kid: '2026-09', publicKeyPath: './keys/2026-09.pub', privateKeyPath: './keys/2026-09.key' },\n\t\t{ kid: '2026-03', publicKeyPath: './keys/2026-03.pub' }, // previous key, verify-only\n\t],\n})\n```\n\n`validateToken` picks the key by the `kid` embedded in the token's header; a token with no\nrecognizable `kid` (or an unknown one) is rejected.\n\n`publicKeyPath`/`privateKeyPath` may be absolute, or relative — relative paths are resolved\nagainst `process.cwd()`, i.e. wherever the app was started from, the same as e.g.\n`@apinecka/drizzle`'s `migrationsPath`. Pass an absolute path (`path.resolve(__dirname, ...)`)\ninstead if your app's cwd isn't guaranteed to be its own root - a bundler that inlines everything\ninto one file, or a process manager that launches from a different directory.\n\nOn construction, `JwtService` signs and validates a throwaway token with the active key pair and\nthrows if that round-trip fails, so a misconfigured key pair (mismatched keys, unreadable file,\nwrong format) fails fast at boot instead of surfacing later on the first real token.\n\n## Generating keys\n\nTokens are signed with `RS512` (RSA + SHA-512), so each key pair must be an RSA key. Generate a\n4096-bit pair:\n\n```bash\nssh-keygen -t rsa -b 4096 -m PEM -E SHA512 -f private\nopenssl rsa -in private -pubout -outform PEM -out public\n```\n\nThis produces `private` (PKCS#1 PEM, keep secret) and `public` (PEM). Point `privateKeyPath` /\n`publicKeyPath` at them. Store the private key outside version control (e.g. mounted as a secret)\nand only commit `public` if you need it checked in for reference.\n\nTo rotate keys: generate a new pair under a new `kid`, add it to `keys` and switch `activeKid` to\nit, keep the previous entry with only its `publicKeyPath` for as long as tokens signed with it can\nstill be outstanding, then remove it once they've all expired.\n\n## Peer dependencies\n\n- `@nestjs/common` — the module is NestJS-specific.\n- `jsonwebtoken` — the underlying JWT implementation.\n\n## License\n\nMIT\n","readmeFilename":"README.md"}