@molecule/api-jwt 1.0.0
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/LICENSE +115 -0
- package/dist/browser-guard.d.ts +2 -0
- package/dist/browser-guard.d.ts.map +1 -0
- package/dist/browser-guard.js +19 -0
- package/dist/browser-guard.js.map +1 -0
- package/dist/index.d.ts +65 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +65 -0
- package/dist/index.js.map +1 -0
- package/dist/keys.d.ts +46 -0
- package/dist/keys.d.ts.map +1 -0
- package/dist/keys.js +198 -0
- package/dist/keys.js.map +1 -0
- package/dist/provider.d.ts +82 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +131 -0
- package/dist/provider.js.map +1 -0
- package/dist/types.d.ts +105 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +7 -0
- package/dist/types.js.map +1 -0
- package/package.json +48 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work.
|
|
38
|
+
|
|
39
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
40
|
+
form, that is based on (or derived from) the Work and for which the
|
|
41
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
42
|
+
represent, as a whole, an original work of authorship.
|
|
43
|
+
|
|
44
|
+
"Contribution" shall mean any work of authorship, including the
|
|
45
|
+
original version of the Work and any modifications or additions
|
|
46
|
+
to that Work, that is intentionally submitted to the Licensor for
|
|
47
|
+
inclusion in the Work by the copyright owner or by an individual or
|
|
48
|
+
Legal Entity authorized to submit on behalf of the copyright owner.
|
|
49
|
+
|
|
50
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
51
|
+
on behalf of whom a Contribution has been received by the Licensor and
|
|
52
|
+
subsequently incorporated within the Work.
|
|
53
|
+
|
|
54
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
55
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
56
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
57
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
58
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
59
|
+
Work and such Derivative Works in Source or Object form.
|
|
60
|
+
|
|
61
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
62
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
63
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
64
|
+
patent license to make, have made, use, offer to sell, sell, import,
|
|
65
|
+
and otherwise transfer the Work.
|
|
66
|
+
|
|
67
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
68
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
69
|
+
modifications, and in Source or Object form, provided that You
|
|
70
|
+
meet the following conditions:
|
|
71
|
+
|
|
72
|
+
(a) You must give any other recipients of the Work or
|
|
73
|
+
Derivative Works a copy of this License; and
|
|
74
|
+
|
|
75
|
+
(b) You must cause any modified files to carry prominent notices
|
|
76
|
+
stating that You changed the files; and
|
|
77
|
+
|
|
78
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
79
|
+
that You distribute, all copyright, patent, trademark, and
|
|
80
|
+
attribution notices from the Source form of the Work,
|
|
81
|
+
excluding those notices that do not pertain to any part of
|
|
82
|
+
the Derivative Works; and
|
|
83
|
+
|
|
84
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
85
|
+
distribution, then any Derivative Works that You distribute must
|
|
86
|
+
include a readable copy of the attribution notices contained
|
|
87
|
+
within such NOTICE file.
|
|
88
|
+
|
|
89
|
+
5. Submission of Contributions.
|
|
90
|
+
|
|
91
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
92
|
+
names, trademarks, service marks, or product names of the Licensor.
|
|
93
|
+
|
|
94
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
95
|
+
agreed to in writing, Licensor provides the Work on an "AS IS" BASIS,
|
|
96
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND.
|
|
97
|
+
|
|
98
|
+
8. Limitation of Liability. In no event and under no legal theory shall
|
|
99
|
+
any Contributor be liable to You for damages.
|
|
100
|
+
|
|
101
|
+
9. Accepting Warranty or Additional Liability.
|
|
102
|
+
|
|
103
|
+
Copyright 2026 Molecule Dev, Inc.
|
|
104
|
+
|
|
105
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
106
|
+
you may not use this file except in compliance with the License.
|
|
107
|
+
You may obtain a copy of the License at
|
|
108
|
+
|
|
109
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
110
|
+
|
|
111
|
+
Unless required by applicable law or agreed to in writing, software
|
|
112
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
113
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
114
|
+
See the License for the specific language governing permissions and
|
|
115
|
+
limitations under the License.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-guard.d.ts","sourceRoot":"","sources":["../src/browser-guard.ts"],"names":[],"mappings":"AAuBA,OAAO,EAAE,CAAA"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser guard — `@molecule/api-jwt` is SERVER-ONLY.
|
|
3
|
+
*
|
|
4
|
+
* Generated by scripts/gen-browser-guards.mjs (workspace root) — edit THAT, not this.
|
|
5
|
+
* Evaluating a server package in a browser bundle is always an import-graph mistake
|
|
6
|
+
* (node APIs, secrets); without this guard it surfaces as a cryptic downstream crash
|
|
7
|
+
* ("Buffer is not defined") far from the culprit. Throwing here names the package and
|
|
8
|
+
* the fix at the exact moment the client bundle evaluates it. jsdom tests and SSR are
|
|
9
|
+
* unaffected: the throw requires browser globals AND the absence of a node runtime.
|
|
10
|
+
*/
|
|
11
|
+
const g = globalThis;
|
|
12
|
+
if (g.window !== undefined && g.document !== undefined && !g.process?.versions?.node) {
|
|
13
|
+
throw new Error('@molecule/api-jwt is SERVER-ONLY: it was bundled into browser/client code. Import it only ' +
|
|
14
|
+
'from server code (a server route/function or your API), or dynamic-import it inside ' +
|
|
15
|
+
'the server handler — never from components or shared client modules, and never ' +
|
|
16
|
+
'polyfill Buffer/process to silence this.');
|
|
17
|
+
}
|
|
18
|
+
export {};
|
|
19
|
+
//# sourceMappingURL=browser-guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-guard.js","sourceRoot":"","sources":["../src/browser-guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,MAAM,CAAC,GAAG,UAIT,CAAA;AACD,IAAI,CAAC,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,CAAC,QAAQ,KAAK,SAAS,IAAI,CAAC,CAAC,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IACrF,MAAM,IAAI,KAAK,CACb,4FAA4F;QAC1F,sFAAsF;QACtF,iFAAiF;QACjF,0CAA0C,CAC7C,CAAA;AACH,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JWT interface for molecule.dev.
|
|
3
|
+
*
|
|
4
|
+
* Provides an abstract JWT interface that can be backed by any JWT library.
|
|
5
|
+
* Use `setProvider` to provide a concrete implementation
|
|
6
|
+
* such as `@molecule/api-jwt-jsonwebtoken`.
|
|
7
|
+
*
|
|
8
|
+
* @remarks
|
|
9
|
+
* **{@link verify} is the ONLY way to trust a token — {@link decode} does NOT check the
|
|
10
|
+
* signature.** Never make an auth decision from `decode()`: an attacker can forge any
|
|
11
|
+
* payload that `decode()` will happily return. Use `verify()` (it throws — catch it) for
|
|
12
|
+
* anything security-relevant; `decode()` is only for reading a token you do NOT trust.
|
|
13
|
+
*
|
|
14
|
+
* - The signing key (private key / secret) is SERVER-SIDE only — never ship it to the
|
|
15
|
+
* browser. Only an asymmetric PUBLIC key may be published.
|
|
16
|
+
* - A JWT payload is READABLE by anyone (base64, not encrypted) — never put a password,
|
|
17
|
+
* secret, or sensitive PII in it.
|
|
18
|
+
* - Always set + honor expiry ({@link JWT_EXPIRES_TIME}); a non-expiring token can't be
|
|
19
|
+
* revoked.
|
|
20
|
+
* - In a molecule app auth is ALREADY wired: the global `verifyMiddleware` verifies the JWT
|
|
21
|
+
* and populates `res.locals.session`, so a handler calls `getUserId(res)` — do NOT call
|
|
22
|
+
* `verify()`/`sign()` by hand for the session (see the `auth` skill). Use these directly
|
|
23
|
+
* only for a CUSTOM token, e.g. a signed email/reset link.
|
|
24
|
+
* - **Re-signing decoded claims (refresh flows): strip `exp`/`iat` first.** `sign()`
|
|
25
|
+
* always sets `expiresIn` (default {@link JWT_EXPIRES_TIME}), and the underlying library
|
|
26
|
+
* throws (`Bad "options.expiresIn" option the payload already has an "exp" property`)
|
|
27
|
+
* when the payload still carries the old `exp` — so `const { exp, iat, ...claims } =
|
|
28
|
+
* verify(oldToken) as JwtPayload; sign(claims)` is the correct refresh shape.
|
|
29
|
+
* - Set `JWT_PRIVATE_KEY` and `JWT_PUBLIC_KEY` together (or neither). If only the private
|
|
30
|
+
* key is set, the matching public key is DERIVED from it automatically; setting only the
|
|
31
|
+
* public key is for verify-only deployments.
|
|
32
|
+
* - When neither key env var is set, a key pair is auto-generated on disk at
|
|
33
|
+
* `{JWT_KEYS_DIR}/{NODE_ENV}/` — default `JWT_KEYS_DIR`: `process.cwd() + '/.keys'`, a
|
|
34
|
+
* stable app-level directory (NOT inside `node_modules`, so `npm ci`/reinstall never
|
|
35
|
+
* wipes it). Set `JWT_KEYS_DIR` to relocate it (e.g. a persistent volume in production).
|
|
36
|
+
* A pre-existing pair at the legacy `node_modules`-relative location is migrated forward
|
|
37
|
+
* automatically (with a logged warning) instead of being silently regenerated.
|
|
38
|
+
* - `JWT_ALGORITHM` (default `RS256`) is validated at module load against the
|
|
39
|
+
* {@link JwtAlgorithm} union; an unrecognized value (e.g. a typo like `rs256`) logs an
|
|
40
|
+
* actionable warning and falls back to `RS256` instead of failing every `sign()`/`verify()`
|
|
41
|
+
* call later with an opaque "invalid algorithm" error.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```ts
|
|
45
|
+
* import { sign, verify, decode } from '@molecule/api-jwt'
|
|
46
|
+
*
|
|
47
|
+
* const token = sign({ userId }, { expiresIn: '15m' }) // server-side; expiry set
|
|
48
|
+
*
|
|
49
|
+
* try {
|
|
50
|
+
* const claims = verify(token) as JwtPayload // signature CHECKED — safe to trust
|
|
51
|
+
* grantAccess(claims.userId)
|
|
52
|
+
* } catch {
|
|
53
|
+
* res.status(401).json({ error: 'Invalid or expired token.' })
|
|
54
|
+
* }
|
|
55
|
+
*
|
|
56
|
+
* decode(token) // NOT verified — never use its output for an auth decision
|
|
57
|
+
* ```
|
|
58
|
+
*
|
|
59
|
+
* @module
|
|
60
|
+
*/
|
|
61
|
+
export * from './browser-guard.js';
|
|
62
|
+
export * from './keys.js';
|
|
63
|
+
export * from './provider.js';
|
|
64
|
+
export * from './types.js';
|
|
65
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2DG;AAEH,cAAc,oBAAoB,CAAA;AAClC,cAAc,WAAW,CAAA;AACzB,cAAc,eAAe,CAAA;AAC7B,cAAc,YAAY,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JWT interface for molecule.dev.
|
|
3
|
+
*
|
|
4
|
+
* Provides an abstract JWT interface that can be backed by any JWT library.
|
|
5
|
+
* Use `setProvider` to provide a concrete implementation
|
|
6
|
+
* such as `@molecule/api-jwt-jsonwebtoken`.
|
|
7
|
+
*
|
|
8
|
+
* @remarks
|
|
9
|
+
* **{@link verify} is the ONLY way to trust a token — {@link decode} does NOT check the
|
|
10
|
+
* signature.** Never make an auth decision from `decode()`: an attacker can forge any
|
|
11
|
+
* payload that `decode()` will happily return. Use `verify()` (it throws — catch it) for
|
|
12
|
+
* anything security-relevant; `decode()` is only for reading a token you do NOT trust.
|
|
13
|
+
*
|
|
14
|
+
* - The signing key (private key / secret) is SERVER-SIDE only — never ship it to the
|
|
15
|
+
* browser. Only an asymmetric PUBLIC key may be published.
|
|
16
|
+
* - A JWT payload is READABLE by anyone (base64, not encrypted) — never put a password,
|
|
17
|
+
* secret, or sensitive PII in it.
|
|
18
|
+
* - Always set + honor expiry ({@link JWT_EXPIRES_TIME}); a non-expiring token can't be
|
|
19
|
+
* revoked.
|
|
20
|
+
* - In a molecule app auth is ALREADY wired: the global `verifyMiddleware` verifies the JWT
|
|
21
|
+
* and populates `res.locals.session`, so a handler calls `getUserId(res)` — do NOT call
|
|
22
|
+
* `verify()`/`sign()` by hand for the session (see the `auth` skill). Use these directly
|
|
23
|
+
* only for a CUSTOM token, e.g. a signed email/reset link.
|
|
24
|
+
* - **Re-signing decoded claims (refresh flows): strip `exp`/`iat` first.** `sign()`
|
|
25
|
+
* always sets `expiresIn` (default {@link JWT_EXPIRES_TIME}), and the underlying library
|
|
26
|
+
* throws (`Bad "options.expiresIn" option the payload already has an "exp" property`)
|
|
27
|
+
* when the payload still carries the old `exp` — so `const { exp, iat, ...claims } =
|
|
28
|
+
* verify(oldToken) as JwtPayload; sign(claims)` is the correct refresh shape.
|
|
29
|
+
* - Set `JWT_PRIVATE_KEY` and `JWT_PUBLIC_KEY` together (or neither). If only the private
|
|
30
|
+
* key is set, the matching public key is DERIVED from it automatically; setting only the
|
|
31
|
+
* public key is for verify-only deployments.
|
|
32
|
+
* - When neither key env var is set, a key pair is auto-generated on disk at
|
|
33
|
+
* `{JWT_KEYS_DIR}/{NODE_ENV}/` — default `JWT_KEYS_DIR`: `process.cwd() + '/.keys'`, a
|
|
34
|
+
* stable app-level directory (NOT inside `node_modules`, so `npm ci`/reinstall never
|
|
35
|
+
* wipes it). Set `JWT_KEYS_DIR` to relocate it (e.g. a persistent volume in production).
|
|
36
|
+
* A pre-existing pair at the legacy `node_modules`-relative location is migrated forward
|
|
37
|
+
* automatically (with a logged warning) instead of being silently regenerated.
|
|
38
|
+
* - `JWT_ALGORITHM` (default `RS256`) is validated at module load against the
|
|
39
|
+
* {@link JwtAlgorithm} union; an unrecognized value (e.g. a typo like `rs256`) logs an
|
|
40
|
+
* actionable warning and falls back to `RS256` instead of failing every `sign()`/`verify()`
|
|
41
|
+
* call later with an opaque "invalid algorithm" error.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```ts
|
|
45
|
+
* import { sign, verify, decode } from '@molecule/api-jwt'
|
|
46
|
+
*
|
|
47
|
+
* const token = sign({ userId }, { expiresIn: '15m' }) // server-side; expiry set
|
|
48
|
+
*
|
|
49
|
+
* try {
|
|
50
|
+
* const claims = verify(token) as JwtPayload // signature CHECKED — safe to trust
|
|
51
|
+
* grantAccess(claims.userId)
|
|
52
|
+
* } catch {
|
|
53
|
+
* res.status(401).json({ error: 'Invalid or expired token.' })
|
|
54
|
+
* }
|
|
55
|
+
*
|
|
56
|
+
* decode(token) // NOT verified — never use its output for an auth decision
|
|
57
|
+
* ```
|
|
58
|
+
*
|
|
59
|
+
* @module
|
|
60
|
+
*/
|
|
61
|
+
export * from './browser-guard.js';
|
|
62
|
+
export * from './keys.js';
|
|
63
|
+
export * from './provider.js';
|
|
64
|
+
export * from './types.js';
|
|
65
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2DG;AAEH,cAAc,oBAAoB,CAAA;AAClC,cAAc,WAAW,CAAA;AACzB,cAAc,eAAe,CAAA;AAC7B,cAAc,YAAY,CAAA"}
|
package/dist/keys.d.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RSA key pair generation and loading for JWT signing/verification.
|
|
3
|
+
*
|
|
4
|
+
* Keys are loaded from environment variables (`JWT_PRIVATE_KEY`, `JWT_PUBLIC_KEY`)
|
|
5
|
+
* or from PEM files on disk at `{JWT_KEYS_DIR}/{NODE_ENV}/` (default
|
|
6
|
+
* `JWT_KEYS_DIR`: `process.cwd() + '/.keys'` — a stable, app-level directory,
|
|
7
|
+
* NOT inside `node_modules`). If no keys exist, a new RSA-2048 key pair is
|
|
8
|
+
* generated automatically on first import.
|
|
9
|
+
*
|
|
10
|
+
* @module
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Generates an RSA-2048 key pair in PEM format for JWT signing and verification.
|
|
14
|
+
*
|
|
15
|
+
* @returns An object containing `publicKey` and `privateKey` as PEM strings.
|
|
16
|
+
*/
|
|
17
|
+
export declare const generateKeyPairSync: () => {
|
|
18
|
+
publicKey: string;
|
|
19
|
+
privateKey: string;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Writes a freshly generated RSA key pair to disk as PEM files. Creates
|
|
23
|
+
* the output directory if it does not exist.
|
|
24
|
+
*
|
|
25
|
+
* @param outputPath - Directory to write the PEM files into; defaults to `{JWT_KEYS_DIR}/{NODE_ENV}/`.
|
|
26
|
+
*/
|
|
27
|
+
export declare const writeKeys: (outputPath?: string) => void;
|
|
28
|
+
/**
|
|
29
|
+
* The RSA private key for signing JWTs. Read from the `JWT_PRIVATE_KEY`
|
|
30
|
+
* environment variable, or loaded from the PEM file on disk.
|
|
31
|
+
*
|
|
32
|
+
* Throws at startup if neither source provides a key — running with an
|
|
33
|
+
* empty secret would allow anyone to forge valid JWTs.
|
|
34
|
+
*/
|
|
35
|
+
export declare const JWT_PRIVATE_KEY: string | Buffer<ArrayBufferLike>;
|
|
36
|
+
/**
|
|
37
|
+
* The RSA public key for verifying JWTs. Read from the `JWT_PUBLIC_KEY`
|
|
38
|
+
* environment variable; when only `JWT_PRIVATE_KEY` is set, the matching
|
|
39
|
+
* public key is DERIVED from it (a disk fallback could not match an
|
|
40
|
+
* env-provided private key and would make every signed token fail
|
|
41
|
+
* verification); otherwise loaded from the PEM file on disk.
|
|
42
|
+
*
|
|
43
|
+
* Throws at startup if no source provides a key.
|
|
44
|
+
*/
|
|
45
|
+
export declare const JWT_PUBLIC_KEY: string | Buffer<ArrayBufferLike>;
|
|
46
|
+
//# sourceMappingURL=keys.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../src/keys.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAoCH;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,QAAO;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAW1E,CAAA;AAEJ;;;;;GAKG;AACH,eAAO,MAAM,SAAS,GAAI,mBAAqB,KAAG,IAejD,CAAA;AA4GD;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,kCAOtB,CAAA;AAEN;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,kCAQrB,CAAA"}
|
package/dist/keys.js
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RSA key pair generation and loading for JWT signing/verification.
|
|
3
|
+
*
|
|
4
|
+
* Keys are loaded from environment variables (`JWT_PRIVATE_KEY`, `JWT_PUBLIC_KEY`)
|
|
5
|
+
* or from PEM files on disk at `{JWT_KEYS_DIR}/{NODE_ENV}/` (default
|
|
6
|
+
* `JWT_KEYS_DIR`: `process.cwd() + '/.keys'` — a stable, app-level directory,
|
|
7
|
+
* NOT inside `node_modules`). If no keys exist, a new RSA-2048 key pair is
|
|
8
|
+
* generated automatically on first import.
|
|
9
|
+
*
|
|
10
|
+
* @module
|
|
11
|
+
*/
|
|
12
|
+
import crypto from 'crypto';
|
|
13
|
+
import fs from 'fs';
|
|
14
|
+
import path from 'path';
|
|
15
|
+
import process from 'process';
|
|
16
|
+
import { fileURLToPath } from 'url';
|
|
17
|
+
import { getLogger } from '@molecule/api-bond';
|
|
18
|
+
const logger = getLogger();
|
|
19
|
+
const __dirname = fileURLToPath(path.dirname(import.meta.url));
|
|
20
|
+
const nodeEnv = process.env.NODE_ENV || 'development';
|
|
21
|
+
// Stable, app-level default: `<app root>/.keys/{NODE_ENV}`. Explicitly
|
|
22
|
+
// overridable via `JWT_KEYS_DIR` (absolute, or relative to `process.cwd()`)
|
|
23
|
+
// for deployments that want keys on a persistent volume outside the app
|
|
24
|
+
// checkout.
|
|
25
|
+
const keysDir = process.env.JWT_KEYS_DIR
|
|
26
|
+
? path.resolve(process.cwd(), process.env.JWT_KEYS_DIR)
|
|
27
|
+
: path.join(process.cwd(), `.keys`);
|
|
28
|
+
const keysPath = path.join(keysDir, nodeEnv);
|
|
29
|
+
const privateKeyPath = `${keysPath}/jwt_private_key.pem`;
|
|
30
|
+
const publicKeyPath = `${keysPath}/jwt_public_key.pem`;
|
|
31
|
+
// Legacy default location (one level ABOVE this package root — inside
|
|
32
|
+
// `node_modules` for an installed app). Reinstalling/`npm ci` wipes
|
|
33
|
+
// `node_modules`, so any key pair generated there is silently lost,
|
|
34
|
+
// invalidating every session and signed link (email verification/reset)
|
|
35
|
+
// on the next boot. Kept ONLY to migrate a pre-existing pair forward to the
|
|
36
|
+
// new stable location below — never written to again.
|
|
37
|
+
const legacyKeysPath = path.join(__dirname, `../../.keys/${nodeEnv}`);
|
|
38
|
+
const legacyPrivateKeyPath = `${legacyKeysPath}/jwt_private_key.pem`;
|
|
39
|
+
const legacyPublicKeyPath = `${legacyKeysPath}/jwt_public_key.pem`;
|
|
40
|
+
/**
|
|
41
|
+
* Generates an RSA-2048 key pair in PEM format for JWT signing and verification.
|
|
42
|
+
*
|
|
43
|
+
* @returns An object containing `publicKey` and `privateKey` as PEM strings.
|
|
44
|
+
*/
|
|
45
|
+
export const generateKeyPairSync = () => crypto.generateKeyPairSync(`rsa`, {
|
|
46
|
+
modulusLength: 2048,
|
|
47
|
+
publicKeyEncoding: {
|
|
48
|
+
type: `spki`,
|
|
49
|
+
format: `pem`,
|
|
50
|
+
},
|
|
51
|
+
privateKeyEncoding: {
|
|
52
|
+
type: `pkcs8`,
|
|
53
|
+
format: `pem`,
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
/**
|
|
57
|
+
* Writes a freshly generated RSA key pair to disk as PEM files. Creates
|
|
58
|
+
* the output directory if it does not exist.
|
|
59
|
+
*
|
|
60
|
+
* @param outputPath - Directory to write the PEM files into; defaults to `{JWT_KEYS_DIR}/{NODE_ENV}/`.
|
|
61
|
+
*/
|
|
62
|
+
export const writeKeys = (outputPath = keysPath) => {
|
|
63
|
+
try {
|
|
64
|
+
const { publicKey, privateKey } = generateKeyPairSync();
|
|
65
|
+
if (!fs.existsSync(outputPath)) {
|
|
66
|
+
fs.mkdirSync(outputPath, { recursive: true });
|
|
67
|
+
}
|
|
68
|
+
fs.writeFileSync(path.join(outputPath, 'jwt_private_key.pem'), privateKey, `utf8`);
|
|
69
|
+
fs.writeFileSync(path.join(outputPath, 'jwt_public_key.pem'), publicKey, `utf8`);
|
|
70
|
+
logger.info(`JWT key pair successfully written to disk.`);
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
logger.error(`Error writing JWT key pair:`, error);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
// Ensure key files exist ONLY when the private key must come from disk.
|
|
77
|
+
// When `JWT_PRIVATE_KEY` is provided via env, generating a fresh on-disk pair
|
|
78
|
+
// here would be worse than useless: with `JWT_PUBLIC_KEY` unset, the public
|
|
79
|
+
// key would then be read from the freshly generated file — a key that does
|
|
80
|
+
// NOT match the env private key — and every token this process signs would
|
|
81
|
+
// fail verification with an unexplained "invalid signature". (The public key
|
|
82
|
+
// for an env-provided private key is DERIVED from it below instead.)
|
|
83
|
+
if (!process.env.JWT_PRIVATE_KEY) {
|
|
84
|
+
if (!fs.existsSync(privateKeyPath) || !fs.existsSync(publicKeyPath)) {
|
|
85
|
+
// One-release migration: a pair generated by a PRE-fix version of this
|
|
86
|
+
// package lives at the legacy `node_modules`-relative path. Copy it
|
|
87
|
+
// forward to the new stable location instead of silently generating a
|
|
88
|
+
// brand-new pair — regenerating here would invalidate every existing
|
|
89
|
+
// session and signed link (email verification/reset) for a deployment
|
|
90
|
+
// that already has working keys. Once copied, this branch never runs
|
|
91
|
+
// again (the new location is populated).
|
|
92
|
+
if (fs.existsSync(legacyPrivateKeyPath) && fs.existsSync(legacyPublicKeyPath)) {
|
|
93
|
+
try {
|
|
94
|
+
fs.mkdirSync(keysPath, { recursive: true });
|
|
95
|
+
fs.copyFileSync(legacyPrivateKeyPath, privateKeyPath);
|
|
96
|
+
fs.copyFileSync(legacyPublicKeyPath, publicKeyPath);
|
|
97
|
+
logger.warn(`Migrated JWT keys from the legacy node_modules-relative location (${legacyKeysPath}) ` +
|
|
98
|
+
`to the stable location ${keysPath}. The legacy location is wiped by every ` +
|
|
99
|
+
`reinstall/npm ci — set JWT_PRIVATE_KEY/JWT_PUBLIC_KEY (recommended) or JWT_KEYS_DIR ` +
|
|
100
|
+
`explicitly to control where keys live going forward.`);
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
logger.error(`Failed to migrate JWT keys from the legacy location (${legacyKeysPath}) to ` +
|
|
104
|
+
`${keysPath} — generating a fresh pair instead. This INVALIDATES every existing ` +
|
|
105
|
+
`session and signed link.`, error);
|
|
106
|
+
writeKeys();
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
writeKeys();
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Convert PEM read from a process.env value back into a real PEM string.
|
|
116
|
+
*
|
|
117
|
+
* Node's `--env-file` (and many other dotenv readers) deliberately do NOT
|
|
118
|
+
* interpret backslash-escapes inside quoted values, so a `.env` line like
|
|
119
|
+
* JWT_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\nMIIE...\n-----END..."
|
|
120
|
+
* arrives in `process.env` as a string containing literal `\n` characters
|
|
121
|
+
* (backslash + n) instead of newlines. Crypto libraries like `jsonwebtoken`
|
|
122
|
+
* then reject the value with "secretOrPrivateKey must be an asymmetric key
|
|
123
|
+
* when using RS256" because the OpenSSL parser can't find PEM headers
|
|
124
|
+
* separated by real newlines.
|
|
125
|
+
*
|
|
126
|
+
* Normalize here so any consumer of this module gets a usable PEM
|
|
127
|
+
* regardless of how the key was stored in the env source.
|
|
128
|
+
*/
|
|
129
|
+
const normalizePem = (value) => {
|
|
130
|
+
if (typeof value !== 'string')
|
|
131
|
+
return value;
|
|
132
|
+
// Only rewrite if the literal escape is present and there are no real
|
|
133
|
+
// newlines yet — preserves keys that arrived correctly (multi-line .env
|
|
134
|
+
// values, files on disk, etc.).
|
|
135
|
+
if (value.includes('\\n') && !value.includes('\n')) {
|
|
136
|
+
return value.replace(/\\n/g, '\n');
|
|
137
|
+
}
|
|
138
|
+
return value;
|
|
139
|
+
};
|
|
140
|
+
const envPrivateKey = normalizePem(process.env.JWT_PRIVATE_KEY || false);
|
|
141
|
+
const envPublicKey = normalizePem(process.env.JWT_PUBLIC_KEY || false);
|
|
142
|
+
/**
|
|
143
|
+
* Derive the SPKI public key from a private key PEM. Works for any
|
|
144
|
+
* asymmetric key type (RSA, EC, Ed25519) — the public half is embedded
|
|
145
|
+
* in the private key material.
|
|
146
|
+
*
|
|
147
|
+
* Used when `JWT_PRIVATE_KEY` is set but `JWT_PUBLIC_KEY` is not: deriving
|
|
148
|
+
* guarantees the pair MATCHES. Reading a public key from disk (or generating
|
|
149
|
+
* a fresh one) in that half-configured state would silently produce a
|
|
150
|
+
* mismatched pair — every token signed by this process would then fail
|
|
151
|
+
* verification with an unexplained "invalid signature".
|
|
152
|
+
*
|
|
153
|
+
* @param privateKey - The private key PEM (string or Buffer).
|
|
154
|
+
* @returns The matching public key PEM, or `false` when derivation fails
|
|
155
|
+
* (e.g. the private key PEM is malformed — signing would fail anyway).
|
|
156
|
+
*/
|
|
157
|
+
const derivePublicKey = (privateKey) => {
|
|
158
|
+
try {
|
|
159
|
+
return crypto.createPublicKey(privateKey).export({ type: 'spki', format: 'pem' }).toString();
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
logger.error('JWT_PRIVATE_KEY is set but a public key could not be derived from it — the PEM is likely malformed:', error);
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
if (envPublicKey && !envPrivateKey) {
|
|
167
|
+
logger.warn('JWT_PUBLIC_KEY is set but JWT_PRIVATE_KEY is not. The private key will be loaded/generated ' +
|
|
168
|
+
'from disk, so tokens signed by THIS process will NOT verify against JWT_PUBLIC_KEY. ' +
|
|
169
|
+
'Set both env vars (or neither) unless this deployment only verifies tokens.');
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* The RSA private key for signing JWTs. Read from the `JWT_PRIVATE_KEY`
|
|
173
|
+
* environment variable, or loaded from the PEM file on disk.
|
|
174
|
+
*
|
|
175
|
+
* Throws at startup if neither source provides a key — running with an
|
|
176
|
+
* empty secret would allow anyone to forge valid JWTs.
|
|
177
|
+
*/
|
|
178
|
+
export const JWT_PRIVATE_KEY = envPrivateKey ||
|
|
179
|
+
(fs.existsSync(privateKeyPath) && fs.readFileSync(privateKeyPath)) ||
|
|
180
|
+
(() => {
|
|
181
|
+
throw new Error('JWT private key not found. Set JWT_PRIVATE_KEY env var or ensure key files exist.');
|
|
182
|
+
})();
|
|
183
|
+
/**
|
|
184
|
+
* The RSA public key for verifying JWTs. Read from the `JWT_PUBLIC_KEY`
|
|
185
|
+
* environment variable; when only `JWT_PRIVATE_KEY` is set, the matching
|
|
186
|
+
* public key is DERIVED from it (a disk fallback could not match an
|
|
187
|
+
* env-provided private key and would make every signed token fail
|
|
188
|
+
* verification); otherwise loaded from the PEM file on disk.
|
|
189
|
+
*
|
|
190
|
+
* Throws at startup if no source provides a key.
|
|
191
|
+
*/
|
|
192
|
+
export const JWT_PUBLIC_KEY = envPublicKey ||
|
|
193
|
+
(envPrivateKey && derivePublicKey(envPrivateKey)) ||
|
|
194
|
+
(fs.existsSync(publicKeyPath) && fs.readFileSync(publicKeyPath)) ||
|
|
195
|
+
(() => {
|
|
196
|
+
throw new Error('JWT public key not found. Set JWT_PUBLIC_KEY env var or ensure key files exist.');
|
|
197
|
+
})();
|
|
198
|
+
//# sourceMappingURL=keys.js.map
|
package/dist/keys.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keys.js","sourceRoot":"","sources":["../src/keys.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JWT provider bond accessor and convenience functions.
|
|
3
|
+
*
|
|
4
|
+
* Bond packages (e.g. `@molecule/api-jwt-jsonwebtoken`) call `setProvider()`
|
|
5
|
+
* during setup. Application code uses `sign()`, `verify()`, and `decode()` directly.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import type { JSONObject, JwtAlgorithm, JwtDecodeOptions, JwtPayload, JwtProvider, JwtSignOptions, JwtVerifyOptions } from './types.js';
|
|
10
|
+
/**
|
|
11
|
+
* The signing algorithm used for JWT operations. Read from the `JWT_ALGORITHM`
|
|
12
|
+
* environment variable, defaulting to `RS256`. An unrecognized value logs an
|
|
13
|
+
* actionable warning at module load and falls back to `RS256` rather than
|
|
14
|
+
* failing every `sign()`/`verify()` call later with an unexplained
|
|
15
|
+
* "invalid algorithm" error.
|
|
16
|
+
*/
|
|
17
|
+
export declare const JWT_ALGORITHM: JwtAlgorithm;
|
|
18
|
+
/**
|
|
19
|
+
* Token lifetime in seconds. Read from the `JWT_EXPIRES_TIME` environment
|
|
20
|
+
* variable, defaulting to 604800 (1 week).
|
|
21
|
+
*/
|
|
22
|
+
export declare const JWT_EXPIRES_TIME: number;
|
|
23
|
+
/**
|
|
24
|
+
* Refresh window in seconds — tokens are refreshed if they will expire within
|
|
25
|
+
* this period. Read from the `JWT_REFRESH_TIME` environment variable,
|
|
26
|
+
* defaulting to 3600 (1 hour).
|
|
27
|
+
*/
|
|
28
|
+
export declare const JWT_REFRESH_TIME: number;
|
|
29
|
+
/**
|
|
30
|
+
* Registers a JWT provider as the active singleton. Called by bond
|
|
31
|
+
* packages during application startup.
|
|
32
|
+
*
|
|
33
|
+
* @param provider - The JWT provider implementation to bond.
|
|
34
|
+
*/
|
|
35
|
+
export declare const setProvider: (provider: JwtProvider) => void;
|
|
36
|
+
/**
|
|
37
|
+
* Retrieves the bonded JWT provider, throwing if none is configured.
|
|
38
|
+
*
|
|
39
|
+
* @returns The bonded JWT provider.
|
|
40
|
+
* @throws {Error} If no JWT provider has been bonded.
|
|
41
|
+
*/
|
|
42
|
+
export declare const getProvider: () => JwtProvider;
|
|
43
|
+
/**
|
|
44
|
+
* Checks whether a JWT provider is currently bonded.
|
|
45
|
+
*
|
|
46
|
+
* @returns `true` if a JWT provider is bonded.
|
|
47
|
+
*/
|
|
48
|
+
export declare const hasProvider: () => boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Signs a payload into a JWT string using the bonded provider. Uses the
|
|
51
|
+
* configured algorithm, expiry, and private key as defaults.
|
|
52
|
+
*
|
|
53
|
+
* @param object - The JSON payload to sign.
|
|
54
|
+
* @param options - Signing options; `algorithm` defaults to `JWT_ALGORITHM`, `expiresIn` defaults to `JWT_EXPIRES_TIME`.
|
|
55
|
+
* @param options.algorithm - The signing algorithm (e.g. `RS256`, `HS256`).
|
|
56
|
+
* @param options.expiresIn - Token lifetime in seconds.
|
|
57
|
+
* @param privateKey - The private key for signing; defaults to `JWT_PRIVATE_KEY`.
|
|
58
|
+
* @returns The signed JWT string.
|
|
59
|
+
*/
|
|
60
|
+
export declare const sign: (object: JSONObject, { algorithm, expiresIn, ...rest }?: JwtSignOptions, privateKey?: string | Buffer) => string;
|
|
61
|
+
/**
|
|
62
|
+
* Verifies a JWT string and returns the decoded payload. Uses the configured
|
|
63
|
+
* algorithm and public key as defaults.
|
|
64
|
+
*
|
|
65
|
+
* @param token - The JWT string to verify.
|
|
66
|
+
* @param options - Verification options; `algorithms` defaults to `[JWT_ALGORITHM]`.
|
|
67
|
+
* @param options.algorithms - The allowed signing algorithms for verification.
|
|
68
|
+
* @param publicKey - The public key for verification; defaults to `JWT_PUBLIC_KEY`.
|
|
69
|
+
* @returns The decoded payload string or object.
|
|
70
|
+
* @throws {Error} If the token is invalid, expired, or verification fails.
|
|
71
|
+
*/
|
|
72
|
+
export declare const verify: (token: string, { algorithms, ...rest }?: JwtVerifyOptions, publicKey?: string | Buffer) => string | JwtPayload;
|
|
73
|
+
/**
|
|
74
|
+
* Decodes a JWT string without verifying its signature. Useful for inspecting
|
|
75
|
+
* token contents when verification is handled elsewhere.
|
|
76
|
+
*
|
|
77
|
+
* @param token - The JWT string to decode.
|
|
78
|
+
* @param options - Decode options such as `complete` for full header+payload output.
|
|
79
|
+
* @returns The decoded payload, or `null` if the token cannot be decoded.
|
|
80
|
+
*/
|
|
81
|
+
export declare const decode: (token: string, options?: JwtDecodeOptions) => string | JwtPayload | null;
|
|
82
|
+
//# sourceMappingURL=provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,KAAK,EACV,UAAU,EACV,YAAY,EACZ,gBAAgB,EAChB,UAAU,EACV,WAAW,EACX,cAAc,EACd,gBAAgB,EACjB,MAAM,YAAY,CAAA;AAkDnB;;;;;;GAMG;AACH,eAAO,MAAM,aAAa,cAAwB,CAAA;AAElD;;;GAGG;AACH,eAAO,MAAM,gBAAgB,QAA2D,CAAA;AAExF;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,QAAkD,CAAA;AAE/E;;;;;GAKG;AACH,eAAO,MAAM,WAAW,GAAI,UAAU,WAAW,KAAG,IAEnD,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,WAAW,QAAO,WAE9B,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,WAAW,QAAO,OAE9B,CAAA;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,IAAI,GACf,QAAQ,UAAU,EAClB,oCAAsE,cAAmB,EACzF,aAAY,MAAM,GAAG,MAAwB,KAC5C,MAAmF,CAAA;AAEtF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,MAAM,GACjB,OAAO,MAAM,EACb,0BAA2C,gBAAqB,EAChE,YAAW,MAAM,GAAG,MAAuB,KAC1C,MAAM,GAAG,UAA6E,CAAA;AAEzF;;;;;;;GAOG;AACH,eAAO,MAAM,MAAM,GAAI,OAAO,MAAM,EAAE,UAAU,gBAAgB,KAAG,MAAM,GAAG,UAAU,GAAG,IACnD,CAAA"}
|
package/dist/provider.js
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JWT provider bond accessor and convenience functions.
|
|
3
|
+
*
|
|
4
|
+
* Bond packages (e.g. `@molecule/api-jwt-jsonwebtoken`) call `setProvider()`
|
|
5
|
+
* during setup. Application code uses `sign()`, `verify()`, and `decode()` directly.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import { bond, expectBond, getLogger, isBonded, require as bondRequire } from '@molecule/api-bond';
|
|
10
|
+
import { JWT_PRIVATE_KEY, JWT_PUBLIC_KEY } from './keys.js';
|
|
11
|
+
const logger = getLogger();
|
|
12
|
+
const BOND_TYPE = 'jwt';
|
|
13
|
+
expectBond(BOND_TYPE);
|
|
14
|
+
/** Every algorithm the {@link JwtAlgorithm} union accepts. */
|
|
15
|
+
const VALID_JWT_ALGORITHMS = [
|
|
16
|
+
'RS256',
|
|
17
|
+
'RS384',
|
|
18
|
+
'RS512',
|
|
19
|
+
'HS256',
|
|
20
|
+
'HS384',
|
|
21
|
+
'HS512',
|
|
22
|
+
'ES256',
|
|
23
|
+
'ES384',
|
|
24
|
+
'ES512',
|
|
25
|
+
'PS256',
|
|
26
|
+
'PS384',
|
|
27
|
+
'PS512',
|
|
28
|
+
];
|
|
29
|
+
/**
|
|
30
|
+
* Resolves and validates `JWT_ALGORITHM` at module load. An unset env var
|
|
31
|
+
* silently defaults to `RS256` (already-supported behavior). A SET-but-invalid
|
|
32
|
+
* value (a typo like `rs256` or `RS-256`) would otherwise produce no boot-time
|
|
33
|
+
* signal at all — every `sign()`/`verify()` call would instead fail later, at
|
|
34
|
+
* request time, with the underlying library's generic "invalid algorithm"
|
|
35
|
+
* error, which reads like a token problem rather than a config problem. Warn
|
|
36
|
+
* here, naming the bad value, and fall back to `RS256` so the process still
|
|
37
|
+
* boots and serves traffic while the misconfiguration gets fixed.
|
|
38
|
+
*
|
|
39
|
+
* @returns A valid `JwtAlgorithm`.
|
|
40
|
+
*/
|
|
41
|
+
const resolveJwtAlgorithm = () => {
|
|
42
|
+
const raw = process.env.JWT_ALGORITHM;
|
|
43
|
+
if (!raw) {
|
|
44
|
+
return `RS256`;
|
|
45
|
+
}
|
|
46
|
+
if (VALID_JWT_ALGORITHMS.includes(raw)) {
|
|
47
|
+
return raw;
|
|
48
|
+
}
|
|
49
|
+
logger.warn(`JWT_ALGORITHM is set to "${raw}", which is not a supported JWT algorithm. ` +
|
|
50
|
+
`Falling back to RS256. Supported values: ${VALID_JWT_ALGORITHMS.join(', ')}.`);
|
|
51
|
+
return `RS256`;
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* The signing algorithm used for JWT operations. Read from the `JWT_ALGORITHM`
|
|
55
|
+
* environment variable, defaulting to `RS256`. An unrecognized value logs an
|
|
56
|
+
* actionable warning at module load and falls back to `RS256` rather than
|
|
57
|
+
* failing every `sign()`/`verify()` call later with an unexplained
|
|
58
|
+
* "invalid algorithm" error.
|
|
59
|
+
*/
|
|
60
|
+
export const JWT_ALGORITHM = resolveJwtAlgorithm();
|
|
61
|
+
/**
|
|
62
|
+
* Token lifetime in seconds. Read from the `JWT_EXPIRES_TIME` environment
|
|
63
|
+
* variable, defaulting to 604800 (1 week).
|
|
64
|
+
*/
|
|
65
|
+
export const JWT_EXPIRES_TIME = Number(process.env.JWT_EXPIRES_TIME) || 60 * 60 * 24 * 7; // 1 week
|
|
66
|
+
/**
|
|
67
|
+
* Refresh window in seconds — tokens are refreshed if they will expire within
|
|
68
|
+
* this period. Read from the `JWT_REFRESH_TIME` environment variable,
|
|
69
|
+
* defaulting to 3600 (1 hour).
|
|
70
|
+
*/
|
|
71
|
+
export const JWT_REFRESH_TIME = Number(process.env.JWT_REFRESH_TIME) || 60 * 60; // 1 hour
|
|
72
|
+
/**
|
|
73
|
+
* Registers a JWT provider as the active singleton. Called by bond
|
|
74
|
+
* packages during application startup.
|
|
75
|
+
*
|
|
76
|
+
* @param provider - The JWT provider implementation to bond.
|
|
77
|
+
*/
|
|
78
|
+
export const setProvider = (provider) => {
|
|
79
|
+
bond(BOND_TYPE, provider);
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* Retrieves the bonded JWT provider, throwing if none is configured.
|
|
83
|
+
*
|
|
84
|
+
* @returns The bonded JWT provider.
|
|
85
|
+
* @throws {Error} If no JWT provider has been bonded.
|
|
86
|
+
*/
|
|
87
|
+
export const getProvider = () => {
|
|
88
|
+
return bondRequire(BOND_TYPE);
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* Checks whether a JWT provider is currently bonded.
|
|
92
|
+
*
|
|
93
|
+
* @returns `true` if a JWT provider is bonded.
|
|
94
|
+
*/
|
|
95
|
+
export const hasProvider = () => {
|
|
96
|
+
return isBonded(BOND_TYPE);
|
|
97
|
+
};
|
|
98
|
+
/**
|
|
99
|
+
* Signs a payload into a JWT string using the bonded provider. Uses the
|
|
100
|
+
* configured algorithm, expiry, and private key as defaults.
|
|
101
|
+
*
|
|
102
|
+
* @param object - The JSON payload to sign.
|
|
103
|
+
* @param options - Signing options; `algorithm` defaults to `JWT_ALGORITHM`, `expiresIn` defaults to `JWT_EXPIRES_TIME`.
|
|
104
|
+
* @param options.algorithm - The signing algorithm (e.g. `RS256`, `HS256`).
|
|
105
|
+
* @param options.expiresIn - Token lifetime in seconds.
|
|
106
|
+
* @param privateKey - The private key for signing; defaults to `JWT_PRIVATE_KEY`.
|
|
107
|
+
* @returns The signed JWT string.
|
|
108
|
+
*/
|
|
109
|
+
export const sign = (object, { algorithm = JWT_ALGORITHM, expiresIn = JWT_EXPIRES_TIME, ...rest } = {}, privateKey = JWT_PRIVATE_KEY) => getProvider().sign(object, { algorithm, expiresIn, ...rest }, privateKey);
|
|
110
|
+
/**
|
|
111
|
+
* Verifies a JWT string and returns the decoded payload. Uses the configured
|
|
112
|
+
* algorithm and public key as defaults.
|
|
113
|
+
*
|
|
114
|
+
* @param token - The JWT string to verify.
|
|
115
|
+
* @param options - Verification options; `algorithms` defaults to `[JWT_ALGORITHM]`.
|
|
116
|
+
* @param options.algorithms - The allowed signing algorithms for verification.
|
|
117
|
+
* @param publicKey - The public key for verification; defaults to `JWT_PUBLIC_KEY`.
|
|
118
|
+
* @returns The decoded payload string or object.
|
|
119
|
+
* @throws {Error} If the token is invalid, expired, or verification fails.
|
|
120
|
+
*/
|
|
121
|
+
export const verify = (token, { algorithms = [JWT_ALGORITHM], ...rest } = {}, publicKey = JWT_PUBLIC_KEY) => getProvider().verify(token, { algorithms, ...rest }, publicKey);
|
|
122
|
+
/**
|
|
123
|
+
* Decodes a JWT string without verifying its signature. Useful for inspecting
|
|
124
|
+
* token contents when verification is handled elsewhere.
|
|
125
|
+
*
|
|
126
|
+
* @param token - The JWT string to decode.
|
|
127
|
+
* @param options - Decode options such as `complete` for full header+payload output.
|
|
128
|
+
* @returns The decoded payload, or `null` if the token cannot be decoded.
|
|
129
|
+
*/
|
|
130
|
+
export const decode = (token, options) => getProvider().decode(token, options);
|
|
131
|
+
//# sourceMappingURL=provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for the JWT core interface.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Supported JWT signing algorithms.
|
|
8
|
+
*/
|
|
9
|
+
export type JwtAlgorithm = 'RS256' | 'RS384' | 'RS512' | 'HS256' | 'HS384' | 'HS512' | 'ES256' | 'ES384' | 'ES512' | 'PS256' | 'PS384' | 'PS512';
|
|
10
|
+
/**
|
|
11
|
+
* Options for signing a JWT.
|
|
12
|
+
*/
|
|
13
|
+
export interface JwtSignOptions {
|
|
14
|
+
algorithm?: JwtAlgorithm;
|
|
15
|
+
expiresIn?: number | string;
|
|
16
|
+
notBefore?: number | string;
|
|
17
|
+
audience?: string | string[];
|
|
18
|
+
issuer?: string;
|
|
19
|
+
subject?: string;
|
|
20
|
+
jwtid?: string;
|
|
21
|
+
keyid?: string;
|
|
22
|
+
header?: Record<string, unknown>;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Options for verifying a JWT.
|
|
26
|
+
*
|
|
27
|
+
* Note: security-hardened bonds (e.g. `@molecule/api-jwt-jsonwebtoken`)
|
|
28
|
+
* REFUSE to honor `ignoreExpiration`/`ignoreNotBefore` — an expired token
|
|
29
|
+
* always fails verification regardless of these flags. To tolerate clock
|
|
30
|
+
* skew or slow flows, use `clockTolerance` (seconds) instead.
|
|
31
|
+
*/
|
|
32
|
+
export interface JwtVerifyOptions {
|
|
33
|
+
algorithms?: JwtAlgorithm[];
|
|
34
|
+
audience?: string | string[];
|
|
35
|
+
issuer?: string | string[];
|
|
36
|
+
subject?: string;
|
|
37
|
+
clockTolerance?: number;
|
|
38
|
+
maxAge?: string | number;
|
|
39
|
+
complete?: boolean;
|
|
40
|
+
ignoreExpiration?: boolean;
|
|
41
|
+
ignoreNotBefore?: boolean;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Options for decoding a JWT (without verification).
|
|
45
|
+
*/
|
|
46
|
+
export interface JwtDecodeOptions {
|
|
47
|
+
complete?: boolean;
|
|
48
|
+
json?: boolean;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Decoded JWT payload.
|
|
52
|
+
*/
|
|
53
|
+
export interface JwtPayload {
|
|
54
|
+
[key: string]: unknown;
|
|
55
|
+
iss?: string;
|
|
56
|
+
sub?: string;
|
|
57
|
+
aud?: string | string[];
|
|
58
|
+
exp?: number;
|
|
59
|
+
nbf?: number;
|
|
60
|
+
iat?: number;
|
|
61
|
+
jti?: string;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Recursive JSON value type representing any valid JSON primitive, array, or object.
|
|
65
|
+
*/
|
|
66
|
+
export type JSONValue = string | number | boolean | null | JSONValue[] | {
|
|
67
|
+
[key: string]: JSONValue;
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* A plain JSON object whose values are `JSONValue`s. Used as the payload
|
|
71
|
+
* type for JWT signing operations.
|
|
72
|
+
*/
|
|
73
|
+
export type JSONObject = {
|
|
74
|
+
[key: string]: JSONValue;
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* JWT provider interface that all JWT bond packages must implement.
|
|
78
|
+
*
|
|
79
|
+
* Provides `sign`, `verify`, and `decode` operations. Key management
|
|
80
|
+
* and algorithm configuration are handled by the core package.
|
|
81
|
+
*/
|
|
82
|
+
export interface JwtProvider {
|
|
83
|
+
sign(payload: JSONObject, options?: JwtSignOptions, privateKey?: string | Buffer): string;
|
|
84
|
+
verify(token: string, options?: JwtVerifyOptions, publicKey?: string | Buffer): string | JwtPayload;
|
|
85
|
+
decode(token: string, options?: JwtDecodeOptions): string | JwtPayload | null;
|
|
86
|
+
}
|
|
87
|
+
declare global {
|
|
88
|
+
namespace NodeJS {
|
|
89
|
+
/** JWT-related environment variables. */
|
|
90
|
+
interface ProcessEnv {
|
|
91
|
+
JWT_PRIVATE_KEY?: string;
|
|
92
|
+
JWT_PUBLIC_KEY?: string;
|
|
93
|
+
JWT_ALGORITHM?: string;
|
|
94
|
+
JWT_EXPIRES_TIME?: string;
|
|
95
|
+
JWT_REFRESH_TIME?: string;
|
|
96
|
+
/**
|
|
97
|
+
* Directory to store auto-generated JWT key pairs in (only used when
|
|
98
|
+
* `JWT_PRIVATE_KEY`/`JWT_PUBLIC_KEY` are unset). Absolute, or resolved
|
|
99
|
+
* relative to `process.cwd()`. Defaults to `process.cwd() + '/.keys'`.
|
|
100
|
+
*/
|
|
101
|
+
JWT_KEYS_DIR?: string;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@molecule/api-jwt",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "JWT interface and key management for molecule.dev",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "tsc",
|
|
10
|
+
"test": "vitest run",
|
|
11
|
+
"test:watch": "vitest"
|
|
12
|
+
},
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"import": "./dist/index.js"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist"
|
|
21
|
+
],
|
|
22
|
+
"keywords": [
|
|
23
|
+
"molecule",
|
|
24
|
+
"jwt",
|
|
25
|
+
"json-web-token",
|
|
26
|
+
"auth"
|
|
27
|
+
],
|
|
28
|
+
"license": "Apache-2.0",
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"@molecule/api-bond": "^1.0.0"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@molecule/api-bond": "1.0.0",
|
|
34
|
+
"@types/node": "26.1.2",
|
|
35
|
+
"typescript": "6.0.3",
|
|
36
|
+
"vitest": "4.1.10"
|
|
37
|
+
},
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "https://github.com/molecule-dev/molecule.git",
|
|
41
|
+
"directory": "packages/api/core/jwt"
|
|
42
|
+
},
|
|
43
|
+
"homepage": "https://github.com/molecule-dev/molecule/tree/main/packages/api/core/jwt",
|
|
44
|
+
"bugs": "https://github.com/molecule-dev/molecule/issues",
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"access": "public"
|
|
47
|
+
}
|
|
48
|
+
}
|