@molecule/api-jwt 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +399 -0
- package/package.json +5 -4
package/README.md
ADDED
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
AUTO-GENERATED — DO NOT EDIT THIS FILE.
|
|
3
|
+
Generated by `mlcl sync-docs` from the package's src/index.ts JSDoc + mlcl/registry.json.
|
|
4
|
+
Edits here are overwritten on the next commit (molecule's pre-commit hook regenerates).
|
|
5
|
+
To change this document, edit the module-level JSDoc in src/index.ts.
|
|
6
|
+
Generated: 2026-08-04T01:48:23.941Z
|
|
7
|
+
-->
|
|
8
|
+
|
|
9
|
+
# @molecule/api-jwt
|
|
10
|
+
|
|
11
|
+
> **Auto-generated, AI-first package reference** for the [molecule.dev](https://molecule.dev) ecosystem.
|
|
12
|
+
> It is written to be read by coding agents as much as by people, and is generated from this
|
|
13
|
+
> package's source — edit `src/index.ts` JSDoc, not this file.
|
|
14
|
+
|
|
15
|
+
JWT interface for molecule.dev.
|
|
16
|
+
|
|
17
|
+
Provides an abstract JWT interface that can be backed by any JWT library.
|
|
18
|
+
Use `setProvider` to provide a concrete implementation
|
|
19
|
+
such as `@molecule/api-jwt-jsonwebtoken`.
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import { sign, verify, decode } from '@molecule/api-jwt'
|
|
25
|
+
|
|
26
|
+
const token = sign({ userId }, { expiresIn: '15m' }) // server-side; expiry set
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
const claims = verify(token) as JwtPayload // signature CHECKED — safe to trust
|
|
30
|
+
grantAccess(claims.userId)
|
|
31
|
+
} catch {
|
|
32
|
+
res.status(401).json({ error: 'Invalid or expired token.' })
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
decode(token) // NOT verified — never use its output for an auth decision
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Type
|
|
39
|
+
|
|
40
|
+
`core`
|
|
41
|
+
|
|
42
|
+
## Installation
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
npm install @molecule/api-jwt @molecule/api-bond
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## API
|
|
49
|
+
|
|
50
|
+
### Interfaces
|
|
51
|
+
|
|
52
|
+
#### `JwtDecodeOptions`
|
|
53
|
+
|
|
54
|
+
Options for decoding a JWT (without verification).
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
interface JwtDecodeOptions {
|
|
58
|
+
complete?: boolean
|
|
59
|
+
json?: boolean
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
#### `JwtPayload`
|
|
64
|
+
|
|
65
|
+
Decoded JWT payload.
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
interface JwtPayload {
|
|
69
|
+
[key: string]: unknown
|
|
70
|
+
iss?: string
|
|
71
|
+
sub?: string
|
|
72
|
+
aud?: string | string[]
|
|
73
|
+
exp?: number
|
|
74
|
+
nbf?: number
|
|
75
|
+
iat?: number
|
|
76
|
+
jti?: string
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
#### `JwtProvider`
|
|
81
|
+
|
|
82
|
+
JWT provider interface that all JWT bond packages must implement.
|
|
83
|
+
|
|
84
|
+
Provides `sign`, `verify`, and `decode` operations. Key management
|
|
85
|
+
and algorithm configuration are handled by the core package.
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
interface JwtProvider {
|
|
89
|
+
sign(payload: JSONObject, options?: JwtSignOptions, privateKey?: string | Buffer): string
|
|
90
|
+
|
|
91
|
+
verify(
|
|
92
|
+
token: string,
|
|
93
|
+
options?: JwtVerifyOptions,
|
|
94
|
+
publicKey?: string | Buffer,
|
|
95
|
+
): string | JwtPayload
|
|
96
|
+
|
|
97
|
+
decode(token: string, options?: JwtDecodeOptions): string | JwtPayload | null
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
#### `JwtSignOptions`
|
|
102
|
+
|
|
103
|
+
Options for signing a JWT.
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
interface JwtSignOptions {
|
|
107
|
+
algorithm?: JwtAlgorithm
|
|
108
|
+
expiresIn?: number | string
|
|
109
|
+
notBefore?: number | string
|
|
110
|
+
audience?: string | string[]
|
|
111
|
+
issuer?: string
|
|
112
|
+
subject?: string
|
|
113
|
+
jwtid?: string
|
|
114
|
+
keyid?: string
|
|
115
|
+
header?: Record<string, unknown>
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
#### `JwtVerifyOptions`
|
|
120
|
+
|
|
121
|
+
Options for verifying a JWT.
|
|
122
|
+
|
|
123
|
+
Note: security-hardened bonds (e.g. `@molecule/api-jwt-jsonwebtoken`)
|
|
124
|
+
REFUSE to honor `ignoreExpiration`/`ignoreNotBefore` — an expired token
|
|
125
|
+
always fails verification regardless of these flags. To tolerate clock
|
|
126
|
+
skew or slow flows, use `clockTolerance` (seconds) instead.
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
interface JwtVerifyOptions {
|
|
130
|
+
algorithms?: JwtAlgorithm[]
|
|
131
|
+
audience?: string | string[]
|
|
132
|
+
issuer?: string | string[]
|
|
133
|
+
subject?: string
|
|
134
|
+
clockTolerance?: number
|
|
135
|
+
maxAge?: string | number
|
|
136
|
+
complete?: boolean
|
|
137
|
+
ignoreExpiration?: boolean
|
|
138
|
+
ignoreNotBefore?: boolean
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Types
|
|
143
|
+
|
|
144
|
+
#### `JSONObject`
|
|
145
|
+
|
|
146
|
+
A plain JSON object whose values are `JSONValue`s. Used as the payload
|
|
147
|
+
type for JWT signing operations.
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
type JSONObject = { [key: string]: JSONValue }
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
#### `JSONValue`
|
|
154
|
+
|
|
155
|
+
Recursive JSON value type representing any valid JSON primitive, array, or object.
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
type JSONValue = string | number | boolean | null | JSONValue[] | { [key: string]: JSONValue }
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
#### `JwtAlgorithm`
|
|
162
|
+
|
|
163
|
+
Supported JWT signing algorithms.
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
type JwtAlgorithm =
|
|
167
|
+
| 'RS256'
|
|
168
|
+
| 'RS384'
|
|
169
|
+
| 'RS512'
|
|
170
|
+
| 'HS256'
|
|
171
|
+
| 'HS384'
|
|
172
|
+
| 'HS512'
|
|
173
|
+
| 'ES256'
|
|
174
|
+
| 'ES384'
|
|
175
|
+
| 'ES512'
|
|
176
|
+
| 'PS256'
|
|
177
|
+
| 'PS384'
|
|
178
|
+
| 'PS512'
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Functions
|
|
182
|
+
|
|
183
|
+
#### `decode(token, options)`
|
|
184
|
+
|
|
185
|
+
Decodes a JWT string without verifying its signature. Useful for inspecting
|
|
186
|
+
token contents when verification is handled elsewhere.
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
function decode(token: string, options?: JwtDecodeOptions): string | JwtPayload | null
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
- `token` — The JWT string to decode.
|
|
193
|
+
- `options` — Decode options such as `complete` for full header+payload output.
|
|
194
|
+
|
|
195
|
+
**Returns:** The decoded payload, or `null` if the token cannot be decoded.
|
|
196
|
+
|
|
197
|
+
#### `generateKeyPairSync()`
|
|
198
|
+
|
|
199
|
+
Generates an RSA-2048 key pair in PEM format for JWT signing and verification.
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
function generateKeyPairSync(): { publicKey: string; privateKey: string }
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
**Returns:** An object containing `publicKey` and `privateKey` as PEM strings.
|
|
206
|
+
|
|
207
|
+
#### `getProvider()`
|
|
208
|
+
|
|
209
|
+
Retrieves the bonded JWT provider, throwing if none is configured.
|
|
210
|
+
|
|
211
|
+
```typescript
|
|
212
|
+
function getProvider(): JwtProvider
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
**Returns:** The bonded JWT provider.
|
|
216
|
+
|
|
217
|
+
#### `hasProvider()`
|
|
218
|
+
|
|
219
|
+
Checks whether a JWT provider is currently bonded.
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
function hasProvider(): boolean
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
**Returns:** `true` if a JWT provider is bonded.
|
|
226
|
+
|
|
227
|
+
#### `setProvider(provider)`
|
|
228
|
+
|
|
229
|
+
Registers a JWT provider as the active singleton. Called by bond
|
|
230
|
+
packages during application startup.
|
|
231
|
+
|
|
232
|
+
```typescript
|
|
233
|
+
function setProvider(provider: JwtProvider): void
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
- `provider` — The JWT provider implementation to bond.
|
|
237
|
+
|
|
238
|
+
#### `sign(object, options, privateKey)`
|
|
239
|
+
|
|
240
|
+
Signs a payload into a JWT string using the bonded provider. Uses the
|
|
241
|
+
configured algorithm, expiry, and private key as defaults.
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
function sign(
|
|
245
|
+
object: JSONObject,
|
|
246
|
+
{ algorithm = JWT_ALGORITHM, expiresIn = JWT_EXPIRES_TIME, ...rest }?: JwtSignOptions,
|
|
247
|
+
privateKey?: string | Buffer<ArrayBufferLike>,
|
|
248
|
+
): string
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
- `object` — The JSON payload to sign.
|
|
252
|
+
- `options` — Signing options; `algorithm` defaults to `JWT_ALGORITHM`, `expiresIn` defaults to `JWT_EXPIRES_TIME`.
|
|
253
|
+
- `options.algorithm` — The signing algorithm (e.g. `RS256`, `HS256`).
|
|
254
|
+
- `options.expiresIn` — Token lifetime in seconds.
|
|
255
|
+
- `privateKey` — The private key for signing; defaults to `JWT_PRIVATE_KEY`.
|
|
256
|
+
|
|
257
|
+
**Returns:** The signed JWT string.
|
|
258
|
+
|
|
259
|
+
#### `verify(token, options, publicKey)`
|
|
260
|
+
|
|
261
|
+
Verifies a JWT string and returns the decoded payload. Uses the configured
|
|
262
|
+
algorithm and public key as defaults.
|
|
263
|
+
|
|
264
|
+
```typescript
|
|
265
|
+
function verify(
|
|
266
|
+
token: string,
|
|
267
|
+
{ algorithms = [JWT_ALGORITHM], ...rest }?: JwtVerifyOptions,
|
|
268
|
+
publicKey?: string | Buffer<ArrayBufferLike>,
|
|
269
|
+
): string | JwtPayload
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
- `token` — The JWT string to verify.
|
|
273
|
+
- `options` — Verification options; `algorithms` defaults to `[JWT_ALGORITHM]`.
|
|
274
|
+
- `options.algorithms` — The allowed signing algorithms for verification.
|
|
275
|
+
- `publicKey` — The public key for verification; defaults to `JWT_PUBLIC_KEY`.
|
|
276
|
+
|
|
277
|
+
**Returns:** The decoded payload string or object.
|
|
278
|
+
|
|
279
|
+
#### `writeKeys(outputPath)`
|
|
280
|
+
|
|
281
|
+
Writes a freshly generated RSA key pair to disk as PEM files. Creates
|
|
282
|
+
the output directory if it does not exist.
|
|
283
|
+
|
|
284
|
+
```typescript
|
|
285
|
+
function writeKeys(outputPath?: string): void
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
- `outputPath` — Directory to write the PEM files into; defaults to `{JWT_KEYS_DIR}/{NODE_ENV}/`.
|
|
289
|
+
|
|
290
|
+
### Constants
|
|
291
|
+
|
|
292
|
+
#### `JWT_ALGORITHM`
|
|
293
|
+
|
|
294
|
+
The signing algorithm used for JWT operations. Read from the `JWT_ALGORITHM`
|
|
295
|
+
environment variable, defaulting to `RS256`. An unrecognized value logs an
|
|
296
|
+
actionable warning at module load and falls back to `RS256` rather than
|
|
297
|
+
failing every `sign()`/`verify()` call later with an unexplained
|
|
298
|
+
"invalid algorithm" error.
|
|
299
|
+
|
|
300
|
+
```typescript
|
|
301
|
+
const JWT_ALGORITHM: JwtAlgorithm
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
#### `JWT_EXPIRES_TIME`
|
|
305
|
+
|
|
306
|
+
Token lifetime in seconds. Read from the `JWT_EXPIRES_TIME` environment
|
|
307
|
+
variable, defaulting to 604800 (1 week).
|
|
308
|
+
|
|
309
|
+
```typescript
|
|
310
|
+
const JWT_EXPIRES_TIME: number
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
#### `JWT_PRIVATE_KEY`
|
|
314
|
+
|
|
315
|
+
The RSA private key for signing JWTs. Read from the `JWT_PRIVATE_KEY`
|
|
316
|
+
environment variable, or loaded from the PEM file on disk.
|
|
317
|
+
|
|
318
|
+
Throws at startup if neither source provides a key — running with an
|
|
319
|
+
empty secret would allow anyone to forge valid JWTs.
|
|
320
|
+
|
|
321
|
+
```typescript
|
|
322
|
+
const JWT_PRIVATE_KEY: string | Buffer<ArrayBufferLike>
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
#### `JWT_PUBLIC_KEY`
|
|
326
|
+
|
|
327
|
+
The RSA public key for verifying JWTs. Read from the `JWT_PUBLIC_KEY`
|
|
328
|
+
environment variable; when only `JWT_PRIVATE_KEY` is set, the matching
|
|
329
|
+
public key is DERIVED from it (a disk fallback could not match an
|
|
330
|
+
env-provided private key and would make every signed token fail
|
|
331
|
+
verification); otherwise loaded from the PEM file on disk.
|
|
332
|
+
|
|
333
|
+
Throws at startup if no source provides a key.
|
|
334
|
+
|
|
335
|
+
```typescript
|
|
336
|
+
const JWT_PUBLIC_KEY: string | Buffer<ArrayBufferLike>
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
#### `JWT_REFRESH_TIME`
|
|
340
|
+
|
|
341
|
+
Refresh window in seconds — tokens are refreshed if they will expire within
|
|
342
|
+
this period. Read from the `JWT_REFRESH_TIME` environment variable,
|
|
343
|
+
defaulting to 3600 (1 hour).
|
|
344
|
+
|
|
345
|
+
```typescript
|
|
346
|
+
const JWT_REFRESH_TIME: number
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
## Available Providers
|
|
350
|
+
|
|
351
|
+
| Provider | Package |
|
|
352
|
+
| ---------------- | -------------------------------- |
|
|
353
|
+
| JWT signing keys | `@molecule/api-jwt-jsonwebtoken` |
|
|
354
|
+
|
|
355
|
+
## Injection Notes
|
|
356
|
+
|
|
357
|
+
### Requirements
|
|
358
|
+
|
|
359
|
+
Peer dependencies:
|
|
360
|
+
|
|
361
|
+
- `@molecule/api-bond` ^1.0.1
|
|
362
|
+
|
|
363
|
+
### Runtime Dependencies
|
|
364
|
+
|
|
365
|
+
- `@molecule/api-bond`
|
|
366
|
+
|
|
367
|
+
**{@link verify} is the ONLY way to trust a token — {@link decode} does NOT check the
|
|
368
|
+
signature.** Never make an auth decision from `decode()`: an attacker can forge any
|
|
369
|
+
payload that `decode()` will happily return. Use `verify()` (it throws — catch it) for
|
|
370
|
+
anything security-relevant; `decode()` is only for reading a token you do NOT trust.
|
|
371
|
+
|
|
372
|
+
- The signing key (private key / secret) is SERVER-SIDE only — never ship it to the
|
|
373
|
+
browser. Only an asymmetric PUBLIC key may be published.
|
|
374
|
+
- A JWT payload is READABLE by anyone (base64, not encrypted) — never put a password,
|
|
375
|
+
secret, or sensitive PII in it.
|
|
376
|
+
- Always set + honor expiry ({@link JWT_EXPIRES_TIME}); a non-expiring token can't be
|
|
377
|
+
revoked.
|
|
378
|
+
- In a molecule app auth is ALREADY wired: the global `verifyMiddleware` verifies the JWT
|
|
379
|
+
and populates `res.locals.session`, so a handler calls `getUserId(res)` — do NOT call
|
|
380
|
+
`verify()`/`sign()` by hand for the session (see the `auth` skill). Use these directly
|
|
381
|
+
only for a CUSTOM token, e.g. a signed email/reset link.
|
|
382
|
+
- **Re-signing decoded claims (refresh flows): strip `exp`/`iat` first.** `sign()`
|
|
383
|
+
always sets `expiresIn` (default {@link JWT_EXPIRES_TIME}), and the underlying library
|
|
384
|
+
throws (`Bad "options.expiresIn" option the payload already has an "exp" property`)
|
|
385
|
+
when the payload still carries the old `exp` — so `const { exp, iat, ...claims } =
|
|
386
|
+
verify(oldToken) as JwtPayload; sign(claims)` is the correct refresh shape.
|
|
387
|
+
- Set `JWT_PRIVATE_KEY` and `JWT_PUBLIC_KEY` together (or neither). If only the private
|
|
388
|
+
key is set, the matching public key is DERIVED from it automatically; setting only the
|
|
389
|
+
public key is for verify-only deployments.
|
|
390
|
+
- When neither key env var is set, a key pair is auto-generated on disk at
|
|
391
|
+
`{JWT_KEYS_DIR}/{NODE_ENV}/` — default `JWT_KEYS_DIR`: `process.cwd() + '/.keys'`, a
|
|
392
|
+
stable app-level directory (NOT inside `node_modules`, so `npm ci`/reinstall never
|
|
393
|
+
wipes it). Set `JWT_KEYS_DIR` to relocate it (e.g. a persistent volume in production).
|
|
394
|
+
A pre-existing pair at the legacy `node_modules`-relative location is migrated forward
|
|
395
|
+
automatically (with a logged warning) instead of being silently regenerated.
|
|
396
|
+
- `JWT_ALGORITHM` (default `RS256`) is validated at module load against the
|
|
397
|
+
{@link JwtAlgorithm} union; an unrecognized value (e.g. a typo like `rs256`) logs an
|
|
398
|
+
actionable warning and falls back to `RS256` instead of failing every `sign()`/`verify()`
|
|
399
|
+
call later with an opaque "invalid algorithm" error.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@molecule/api-jwt",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "JWT interface and key management for molecule.dev",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
}
|
|
18
18
|
},
|
|
19
19
|
"files": [
|
|
20
|
-
"dist"
|
|
20
|
+
"dist",
|
|
21
|
+
"README.md"
|
|
21
22
|
],
|
|
22
23
|
"keywords": [
|
|
23
24
|
"molecule",
|
|
@@ -27,10 +28,10 @@
|
|
|
27
28
|
],
|
|
28
29
|
"license": "Apache-2.0",
|
|
29
30
|
"peerDependencies": {
|
|
30
|
-
"@molecule/api-bond": "^1.0.
|
|
31
|
+
"@molecule/api-bond": "^1.0.1"
|
|
31
32
|
},
|
|
32
33
|
"devDependencies": {
|
|
33
|
-
"@molecule/api-bond": "1.0.
|
|
34
|
+
"@molecule/api-bond": "1.0.1",
|
|
34
35
|
"@types/node": "26.1.2",
|
|
35
36
|
"typescript": "6.0.3",
|
|
36
37
|
"vitest": "4.1.10"
|