@frontmcp/sdk 0.5.0 → 0.6.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.
Files changed (226) hide show
  1. package/README.md +3 -3
  2. package/package.json +8 -19
  3. package/src/adapter/adapter.instance.js +5 -0
  4. package/src/adapter/adapter.instance.js.map +1 -1
  5. package/src/auth/authorization/authorization.class.d.ts +1 -4
  6. package/src/auth/authorization/authorization.class.js +6 -13
  7. package/src/auth/authorization/authorization.class.js.map +1 -1
  8. package/src/auth/flows/session.verify.flow.d.ts +1 -0
  9. package/src/auth/flows/session.verify.flow.js +11 -1
  10. package/src/auth/flows/session.verify.flow.js.map +1 -1
  11. package/src/auth/flows/well-known.jwks.flow.js +2 -2
  12. package/src/auth/flows/well-known.jwks.flow.js.map +1 -1
  13. package/src/auth/jwks/dev-key-persistence.d.ts +63 -0
  14. package/src/auth/jwks/dev-key-persistence.js +219 -0
  15. package/src/auth/jwks/dev-key-persistence.js.map +1 -0
  16. package/src/auth/jwks/index.d.ts +1 -0
  17. package/src/auth/jwks/index.js +1 -0
  18. package/src/auth/jwks/index.js.map +1 -1
  19. package/src/auth/jwks/jwks.service.d.ts +7 -4
  20. package/src/auth/jwks/jwks.service.js +81 -12
  21. package/src/auth/jwks/jwks.service.js.map +1 -1
  22. package/src/auth/jwks/jwks.types.d.ts +7 -0
  23. package/src/auth/jwks/jwks.types.js.map +1 -1
  24. package/src/auth/machine-id.d.ts +5 -0
  25. package/src/auth/machine-id.js +32 -0
  26. package/src/auth/machine-id.js.map +1 -0
  27. package/src/auth/session/index.d.ts +1 -0
  28. package/src/auth/session/index.js +3 -1
  29. package/src/auth/session/index.js.map +1 -1
  30. package/src/auth/session/record/session.base.js +5 -3
  31. package/src/auth/session/record/session.base.js.map +1 -1
  32. package/src/auth/session/record/session.stateless.d.ts +2 -2
  33. package/src/auth/session/record/session.stateless.js +5 -3
  34. package/src/auth/session/record/session.stateless.js.map +1 -1
  35. package/src/auth/session/redis-session.store.d.ts +64 -0
  36. package/src/auth/session/redis-session.store.js +204 -0
  37. package/src/auth/session/redis-session.store.js.map +1 -0
  38. package/src/auth/session/session.service.d.ts +0 -2
  39. package/src/auth/session/session.service.js +1 -7
  40. package/src/auth/session/session.service.js.map +1 -1
  41. package/src/auth/session/transport-session.manager.js +3 -5
  42. package/src/auth/session/transport-session.manager.js.map +1 -1
  43. package/src/auth/session/transport-session.types.d.ts +4 -0
  44. package/src/auth/session/transport-session.types.js +4 -3
  45. package/src/auth/session/transport-session.types.js.map +1 -1
  46. package/src/auth/session/utils/session-id.utils.d.ts +12 -1
  47. package/src/auth/session/utils/session-id.utils.js +48 -9
  48. package/src/auth/session/utils/session-id.utils.js.map +1 -1
  49. package/src/auth/ui/base-layout.d.ts +0 -8
  50. package/src/auth/ui/base-layout.js +1 -14
  51. package/src/auth/ui/base-layout.js.map +1 -1
  52. package/src/auth/ui/index.d.ts +3 -4
  53. package/src/auth/ui/index.js +10 -11
  54. package/src/auth/ui/index.js.map +1 -1
  55. package/src/auth/ui/{htmx-templates.d.ts → templates.d.ts} +5 -6
  56. package/src/auth/ui/{htmx-templates.js → templates.js} +8 -15
  57. package/src/auth/ui/templates.js.map +1 -0
  58. package/src/common/decorators/decorator-utils.js.map +1 -1
  59. package/src/common/decorators/front-mcp.decorator.js +28 -2
  60. package/src/common/decorators/front-mcp.decorator.js.map +1 -1
  61. package/src/common/index.d.ts +0 -1
  62. package/src/common/index.js +0 -1
  63. package/src/common/index.js.map +1 -1
  64. package/src/common/interfaces/adapter.interface.d.ts +6 -0
  65. package/src/common/interfaces/adapter.interface.js.map +1 -1
  66. package/src/common/interfaces/execution-context.interface.d.ts +52 -3
  67. package/src/common/interfaces/execution-context.interface.js +88 -3
  68. package/src/common/interfaces/execution-context.interface.js.map +1 -1
  69. package/src/common/interfaces/flow.interface.d.ts +13 -0
  70. package/src/common/interfaces/flow.interface.js +24 -0
  71. package/src/common/interfaces/flow.interface.js.map +1 -1
  72. package/src/common/interfaces/server.interface.d.ts +9 -0
  73. package/src/common/interfaces/server.interface.js.map +1 -1
  74. package/src/common/metadata/app.metadata.d.ts +108 -0
  75. package/src/common/metadata/front-mcp.metadata.d.ts +659 -2
  76. package/src/common/metadata/front-mcp.metadata.js +3 -1
  77. package/src/common/metadata/front-mcp.metadata.js.map +1 -1
  78. package/src/common/metadata/provider.metadata.d.ts +14 -0
  79. package/src/common/metadata/provider.metadata.js +18 -2
  80. package/src/common/metadata/provider.metadata.js.map +1 -1
  81. package/src/common/metadata/tool.metadata.d.ts +33 -1
  82. package/src/common/metadata/tool.metadata.js.map +1 -1
  83. package/src/common/migrate/auth-transport.migrate.d.ts +62 -0
  84. package/src/common/migrate/auth-transport.migrate.js +140 -0
  85. package/src/common/migrate/auth-transport.migrate.js.map +1 -0
  86. package/src/common/migrate/index.d.ts +1 -0
  87. package/src/common/migrate/index.js +6 -0
  88. package/src/common/migrate/index.js.map +1 -0
  89. package/src/common/schemas/http-output.schema.d.ts +10 -2
  90. package/src/common/schemas/index.d.ts +1 -0
  91. package/src/common/schemas/index.js +1 -0
  92. package/src/common/schemas/index.js.map +1 -1
  93. package/src/common/schemas/session-header.schema.d.ts +16 -0
  94. package/src/common/schemas/session-header.schema.js +42 -0
  95. package/src/common/schemas/session-header.schema.js.map +1 -0
  96. package/src/common/tokens/front-mcp.tokens.js +3 -1
  97. package/src/common/tokens/front-mcp.tokens.js.map +1 -1
  98. package/src/common/types/options/auth.options.d.ts +233 -3
  99. package/src/common/types/options/auth.options.js +29 -40
  100. package/src/common/types/options/auth.options.js.map +1 -1
  101. package/src/common/types/options/index.d.ts +2 -0
  102. package/src/common/types/options/index.js +2 -0
  103. package/src/common/types/options/index.js.map +1 -1
  104. package/src/common/types/options/redis.options.d.ts +22 -0
  105. package/src/common/types/options/redis.options.js +45 -0
  106. package/src/common/types/options/redis.options.js.map +1 -0
  107. package/src/common/types/options/transport.options.d.ts +84 -0
  108. package/src/common/types/options/transport.options.js +121 -0
  109. package/src/common/types/options/transport.options.js.map +1 -0
  110. package/src/completion/flows/complete.flow.d.ts +17 -2
  111. package/src/context/frontmcp-context-storage.d.ts +94 -0
  112. package/src/context/frontmcp-context-storage.js +183 -0
  113. package/src/context/frontmcp-context-storage.js.map +1 -0
  114. package/src/context/frontmcp-context.d.ts +269 -0
  115. package/src/context/frontmcp-context.js +360 -0
  116. package/src/context/frontmcp-context.js.map +1 -0
  117. package/src/context/frontmcp-context.provider.d.ts +43 -0
  118. package/src/context/frontmcp-context.provider.js +61 -0
  119. package/src/context/frontmcp-context.provider.js.map +1 -0
  120. package/src/context/index.d.ts +34 -0
  121. package/src/context/index.js +64 -0
  122. package/src/context/index.js.map +1 -0
  123. package/src/context/request-context-storage.d.ts +89 -0
  124. package/src/context/request-context-storage.js +183 -0
  125. package/src/context/request-context-storage.js.map +1 -0
  126. package/src/context/request-context.d.ts +184 -0
  127. package/src/context/request-context.js +209 -0
  128. package/src/context/request-context.js.map +1 -0
  129. package/src/context/request-context.provider.d.ts +37 -0
  130. package/src/context/request-context.provider.js +51 -0
  131. package/src/context/request-context.provider.js.map +1 -0
  132. package/src/context/session-key.provider.d.ts +45 -0
  133. package/src/context/session-key.provider.js +65 -0
  134. package/src/context/session-key.provider.js.map +1 -0
  135. package/src/context/trace-context.d.ts +43 -0
  136. package/src/context/trace-context.js +142 -0
  137. package/src/context/trace-context.js.map +1 -0
  138. package/src/errors/index.d.ts +1 -1
  139. package/src/errors/index.js +3 -1
  140. package/src/errors/index.js.map +1 -1
  141. package/src/errors/mcp.error.d.ts +7 -0
  142. package/src/errors/mcp.error.js +11 -1
  143. package/src/errors/mcp.error.js.map +1 -1
  144. package/src/flows/flow.instance.d.ts +16 -0
  145. package/src/flows/flow.instance.js +166 -80
  146. package/src/flows/flow.instance.js.map +1 -1
  147. package/src/flows/flow.registry.d.ts +5 -0
  148. package/src/flows/flow.registry.js +45 -3
  149. package/src/flows/flow.registry.js.map +1 -1
  150. package/src/front-mcp/front-mcp.d.ts +12 -0
  151. package/src/front-mcp/front-mcp.js +22 -3
  152. package/src/front-mcp/front-mcp.js.map +1 -1
  153. package/src/front-mcp/front-mcp.providers.d.ts +266 -1
  154. package/src/front-mcp/front-mcp.providers.js +2 -1
  155. package/src/front-mcp/front-mcp.providers.js.map +1 -1
  156. package/src/front-mcp/serverless-handler.d.ts +28 -0
  157. package/src/front-mcp/serverless-handler.js +61 -0
  158. package/src/front-mcp/serverless-handler.js.map +1 -0
  159. package/src/hooks/hooks.utils.d.ts +1 -1
  160. package/src/hooks/hooks.utils.js +10 -3
  161. package/src/hooks/hooks.utils.js.map +1 -1
  162. package/src/index.d.ts +8 -4
  163. package/src/index.js +20 -1
  164. package/src/index.js.map +1 -1
  165. package/src/logger/instances/instance.logger.js +0 -1
  166. package/src/logger/instances/instance.logger.js.map +1 -1
  167. package/src/logging/flows/set-level.flow.d.ts +17 -2
  168. package/src/notification/notification.service.js +5 -1
  169. package/src/notification/notification.service.js.map +1 -1
  170. package/src/prompt/flows/get-prompt.flow.d.ts +97 -2
  171. package/src/prompt/flows/prompts-list.flow.d.ts +12 -1
  172. package/src/provider/provider.registry.d.ts +97 -5
  173. package/src/provider/provider.registry.js +306 -9
  174. package/src/provider/provider.registry.js.map +1 -1
  175. package/src/provider/provider.types.d.ts +21 -3
  176. package/src/provider/provider.types.js.map +1 -1
  177. package/src/resource/flows/read-resource.flow.d.ts +22 -3
  178. package/src/resource/flows/resource-templates-list.flow.d.ts +20 -1
  179. package/src/resource/flows/resources-list.flow.d.ts +20 -1
  180. package/src/resource/flows/subscribe-resource.flow.d.ts +17 -2
  181. package/src/resource/flows/unsubscribe-resource.flow.d.ts +17 -2
  182. package/src/scope/flows/http.request.flow.js +43 -7
  183. package/src/scope/flows/http.request.flow.js.map +1 -1
  184. package/src/scope/scope.instance.js +12 -5
  185. package/src/scope/scope.instance.js.map +1 -1
  186. package/src/server/adapters/base.host.adapter.d.ts +9 -0
  187. package/src/server/adapters/base.host.adapter.js.map +1 -1
  188. package/src/server/adapters/express.host.adapter.d.ts +12 -0
  189. package/src/server/adapters/express.host.adapter.js +21 -1
  190. package/src/server/adapters/express.host.adapter.js.map +1 -1
  191. package/src/server/server.instance.d.ts +3 -0
  192. package/src/server/server.instance.js +14 -7
  193. package/src/server/server.instance.js.map +1 -1
  194. package/src/tool/flows/call-tool.flow.d.ts +118 -13
  195. package/src/tool/flows/call-tool.flow.js +240 -194
  196. package/src/tool/flows/call-tool.flow.js.map +1 -1
  197. package/src/tool/flows/tools-list.flow.d.ts +25 -11
  198. package/src/tool/flows/tools-list.flow.js +82 -31
  199. package/src/tool/flows/tools-list.flow.js.map +1 -1
  200. package/src/tool/tool.instance.d.ts +1 -4
  201. package/src/transport/adapters/transport.streamable-http.adapter.js +1 -0
  202. package/src/transport/adapters/transport.streamable-http.adapter.js.map +1 -1
  203. package/src/transport/flows/handle.sse.flow.js +9 -2
  204. package/src/transport/flows/handle.sse.flow.js.map +1 -1
  205. package/src/transport/flows/handle.streamable-http.flow.js +63 -6
  206. package/src/transport/flows/handle.streamable-http.flow.js.map +1 -1
  207. package/src/transport/mcp-handlers/complete-request.handler.d.ts +27 -1
  208. package/src/transport/mcp-handlers/get-prompt-request.handler.d.ts +52 -1
  209. package/src/transport/mcp-handlers/index.d.ts +413 -7
  210. package/src/transport/mcp-handlers/initialize-request.handler.js +12 -2
  211. package/src/transport/mcp-handlers/initialize-request.handler.js.map +1 -1
  212. package/src/transport/mcp-handlers/list-prompts-request.handler.d.ts +27 -1
  213. package/src/transport/mcp-handlers/list-resource-templates-request.handler.d.ts +32 -1
  214. package/src/transport/mcp-handlers/list-resources-request.handler.d.ts +32 -1
  215. package/src/transport/mcp-handlers/list-tools-request.handler.d.ts +30 -1
  216. package/src/transport/mcp-handlers/logging-set-level-request.handler.d.ts +20 -0
  217. package/src/transport/mcp-handlers/read-resource-request.handler.d.ts +27 -1
  218. package/src/transport/mcp-handlers/subscribe-request.handler.d.ts +20 -0
  219. package/src/transport/mcp-handlers/unsubscribe-request.handler.d.ts +20 -0
  220. package/src/transport/transport.registry.d.ts +68 -4
  221. package/src/transport/transport.registry.js +313 -11
  222. package/src/transport/transport.registry.js.map +1 -1
  223. package/src/auth/ui/htmx-templates.js.map +0 -1
  224. package/src/common/providers/session.provider.d.ts +0 -13
  225. package/src/common/providers/session.provider.js +0 -27
  226. package/src/common/providers/session.provider.js.map +0 -1
@@ -0,0 +1,219 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isDevKeyPersistenceEnabled = isDevKeyPersistenceEnabled;
4
+ exports.resolveKeyPath = resolveKeyPath;
5
+ exports.loadDevKey = loadDevKey;
6
+ exports.saveDevKey = saveDevKey;
7
+ exports.deleteDevKey = deleteDevKey;
8
+ const tslib_1 = require("tslib");
9
+ // auth/jwks/dev-key-persistence.ts
10
+ const fs = tslib_1.__importStar(require("fs/promises"));
11
+ const path = tslib_1.__importStar(require("path"));
12
+ const crypto = tslib_1.__importStar(require("crypto"));
13
+ const zod_1 = require("zod");
14
+ const DEFAULT_KEY_PATH = '.frontmcp/dev-keys.json';
15
+ /**
16
+ * Zod schema for RSA JWK private key
17
+ */
18
+ const rsaPrivateKeySchema = zod_1.z
19
+ .object({
20
+ kty: zod_1.z.literal('RSA'),
21
+ n: zod_1.z.string().min(1),
22
+ e: zod_1.z.string().min(1),
23
+ d: zod_1.z.string().min(1),
24
+ p: zod_1.z.string().optional(),
25
+ q: zod_1.z.string().optional(),
26
+ dp: zod_1.z.string().optional(),
27
+ dq: zod_1.z.string().optional(),
28
+ qi: zod_1.z.string().optional(),
29
+ })
30
+ .passthrough();
31
+ /**
32
+ * Zod schema for EC JWK private key
33
+ */
34
+ const ecPrivateKeySchema = zod_1.z
35
+ .object({
36
+ kty: zod_1.z.literal('EC'),
37
+ crv: zod_1.z.string().min(1),
38
+ x: zod_1.z.string().min(1),
39
+ y: zod_1.z.string().min(1),
40
+ d: zod_1.z.string().min(1),
41
+ })
42
+ .passthrough();
43
+ /**
44
+ * Zod schema for public JWK (used in JWKS)
45
+ */
46
+ const publicJwkSchema = zod_1.z
47
+ .object({
48
+ kty: zod_1.z.enum(['RSA', 'EC']),
49
+ kid: zod_1.z.string().min(1),
50
+ alg: zod_1.z.enum(['RS256', 'ES256']),
51
+ use: zod_1.z.literal('sig'),
52
+ })
53
+ .passthrough();
54
+ /**
55
+ * Zod schema for JWKS
56
+ */
57
+ const jwksSchema = zod_1.z.object({
58
+ keys: zod_1.z.array(publicJwkSchema).min(1),
59
+ });
60
+ /**
61
+ * Zod schema for DevKeyData
62
+ */
63
+ const devKeyDataSchema = zod_1.z.object({
64
+ kid: zod_1.z.string().min(1),
65
+ privateKey: zod_1.z.union([rsaPrivateKeySchema, ecPrivateKeySchema]),
66
+ publicJwk: jwksSchema,
67
+ createdAt: zod_1.z.number().positive().int(),
68
+ alg: zod_1.z.enum(['RS256', 'ES256']),
69
+ });
70
+ /**
71
+ * Validate JWK structure based on algorithm
72
+ */
73
+ function validateJwkStructure(data) {
74
+ const result = devKeyDataSchema.safeParse(data);
75
+ if (!result.success) {
76
+ return { valid: false, error: result.error.issues[0]?.message ?? 'Invalid JWK structure' };
77
+ }
78
+ const parsed = result.data;
79
+ // Verify algorithm matches key type
80
+ if (parsed.alg === 'RS256' && parsed.privateKey.kty !== 'RSA') {
81
+ return { valid: false, error: 'Algorithm RS256 requires RSA key type' };
82
+ }
83
+ if (parsed.alg === 'ES256' && parsed.privateKey.kty !== 'EC') {
84
+ return { valid: false, error: 'Algorithm ES256 requires EC key type' };
85
+ }
86
+ // Verify public key matches private key algorithm
87
+ const publicKey = parsed.publicJwk.keys[0];
88
+ if (publicKey.kty !== parsed.privateKey.kty) {
89
+ return { valid: false, error: 'Public and private key types do not match' };
90
+ }
91
+ // Verify kid consistency between top-level and publicJwk
92
+ if (publicKey.kid !== parsed.kid) {
93
+ return { valid: false, error: 'kid mismatch between top-level and publicJwk' };
94
+ }
95
+ // Verify createdAt is not in the future and not too old (100 years)
96
+ const now = Date.now();
97
+ const hundredYearsMs = 100 * 365 * 24 * 60 * 60 * 1000;
98
+ if (parsed.createdAt > now) {
99
+ return { valid: false, error: 'createdAt is in the future' };
100
+ }
101
+ if (parsed.createdAt < now - hundredYearsMs) {
102
+ return { valid: false, error: 'createdAt is too old' };
103
+ }
104
+ return { valid: true };
105
+ }
106
+ /**
107
+ * Check if dev key persistence is enabled based on environment and options
108
+ */
109
+ function isDevKeyPersistenceEnabled(options) {
110
+ const isProduction = process.env['NODE_ENV'] === 'production';
111
+ // In production, only enable if explicitly forced
112
+ if (isProduction) {
113
+ return options?.forceEnable === true;
114
+ }
115
+ // In development, enabled by default
116
+ return true;
117
+ }
118
+ /**
119
+ * Resolve the key file path
120
+ */
121
+ function resolveKeyPath(options) {
122
+ const keyPath = options?.keyPath ?? DEFAULT_KEY_PATH;
123
+ // If absolute path, use as-is
124
+ if (path.isAbsolute(keyPath)) {
125
+ return keyPath;
126
+ }
127
+ // Relative paths are resolved from current working directory
128
+ return path.resolve(process.cwd(), keyPath);
129
+ }
130
+ /**
131
+ * Load persisted dev key from file
132
+ *
133
+ * @param options - Persistence options
134
+ * @returns The loaded key data or null if not found/invalid
135
+ */
136
+ async function loadDevKey(options) {
137
+ if (!isDevKeyPersistenceEnabled(options)) {
138
+ return null;
139
+ }
140
+ const keyPath = resolveKeyPath(options);
141
+ try {
142
+ const content = await fs.readFile(keyPath, 'utf8');
143
+ const data = JSON.parse(content);
144
+ // Validate JWK structure using Zod schema
145
+ const validation = validateJwkStructure(data);
146
+ if (!validation.valid) {
147
+ console.warn(`[DevKeyPersistence] Invalid key file format at ${keyPath}: ${validation.error}, will regenerate`);
148
+ return null;
149
+ }
150
+ console.log(`[DevKeyPersistence] Loaded key (kid=${data.kid}) from ${keyPath}`);
151
+ return data;
152
+ }
153
+ catch (error) {
154
+ if (error.code === 'ENOENT') {
155
+ // File doesn't exist - normal for first run
156
+ return null;
157
+ }
158
+ console.warn(`[DevKeyPersistence] Failed to load key from ${keyPath}: ${error.message}`);
159
+ return null;
160
+ }
161
+ }
162
+ /**
163
+ * Save dev key to file
164
+ *
165
+ * Uses atomic write (temp file + rename) to prevent corruption.
166
+ * Sets file permissions to 0o600 (owner read/write only) for security.
167
+ *
168
+ * @param keyData - Key data to persist
169
+ * @param options - Persistence options
170
+ * @returns true if save succeeded, false otherwise
171
+ */
172
+ async function saveDevKey(keyData, options) {
173
+ if (!isDevKeyPersistenceEnabled(options)) {
174
+ return true; // Not enabled is not a failure
175
+ }
176
+ const keyPath = resolveKeyPath(options);
177
+ const dir = path.dirname(keyPath);
178
+ const tempPath = `${keyPath}.tmp.${Date.now()}.${crypto.randomBytes(8).toString('hex')}`;
179
+ try {
180
+ // Ensure directory exists with restricted permissions
181
+ await fs.mkdir(dir, { recursive: true, mode: 0o700 });
182
+ // Write to temp file first (atomic write pattern)
183
+ const content = JSON.stringify(keyData, null, 2);
184
+ await fs.writeFile(tempPath, content, { mode: 0o600, encoding: 'utf8' });
185
+ // Atomic rename to target path
186
+ await fs.rename(tempPath, keyPath);
187
+ console.log(`[DevKeyPersistence] Saved key (kid=${keyData.kid}) to ${keyPath}`);
188
+ return true;
189
+ }
190
+ catch (error) {
191
+ console.error(`[DevKeyPersistence] Failed to save key to ${keyPath}: ${error.message}`);
192
+ // Clean up temp file if it exists
193
+ try {
194
+ await fs.unlink(tempPath);
195
+ }
196
+ catch {
197
+ // Ignore cleanup errors
198
+ }
199
+ return false;
200
+ }
201
+ }
202
+ /**
203
+ * Delete persisted dev key
204
+ *
205
+ * @param options - Persistence options
206
+ */
207
+ async function deleteDevKey(options) {
208
+ const keyPath = resolveKeyPath(options);
209
+ try {
210
+ await fs.unlink(keyPath);
211
+ console.log(`[DevKeyPersistence] Deleted key at ${keyPath}`);
212
+ }
213
+ catch (error) {
214
+ if (error.code !== 'ENOENT') {
215
+ console.warn(`[DevKeyPersistence] Failed to delete key at ${keyPath}: ${error.message}`);
216
+ }
217
+ }
218
+ }
219
+ //# sourceMappingURL=dev-key-persistence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev-key-persistence.js","sourceRoot":"","sources":["../../../../src/auth/jwks/dev-key-persistence.ts"],"names":[],"mappings":"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","sourcesContent":["// auth/jwks/dev-key-persistence.ts\nimport * as fs from 'fs/promises';\nimport * as path from 'path';\nimport * as crypto from 'crypto';\nimport { JSONWebKeySet } from 'jose';\nimport { z } from 'zod';\n\n/**\n * Data structure for persisted development keys\n */\nexport interface DevKeyData {\n /** Key ID (kid) */\n kid: string;\n /** Private key in JWK format (portable) */\n privateKey: JsonWebKey;\n /** Public JWKS for verification */\n publicJwk: JSONWebKeySet;\n /** Key creation timestamp (ms) */\n createdAt: number;\n /** Algorithm used */\n alg: 'RS256' | 'ES256';\n}\n\n/**\n * Options for dev key persistence\n */\nexport interface DevKeyPersistenceOptions {\n /**\n * Path to store dev keys\n * @default '.frontmcp/dev-keys.json'\n */\n keyPath?: string;\n /**\n * Enable persistence in production (NOT RECOMMENDED)\n * @default false\n */\n forceEnable?: boolean;\n}\n\nconst DEFAULT_KEY_PATH = '.frontmcp/dev-keys.json';\n\n/**\n * Zod schema for RSA JWK private key\n */\nconst rsaPrivateKeySchema = z\n .object({\n kty: z.literal('RSA'),\n n: z.string().min(1),\n e: z.string().min(1),\n d: z.string().min(1),\n p: z.string().optional(),\n q: z.string().optional(),\n dp: z.string().optional(),\n dq: z.string().optional(),\n qi: z.string().optional(),\n })\n .passthrough();\n\n/**\n * Zod schema for EC JWK private key\n */\nconst ecPrivateKeySchema = z\n .object({\n kty: z.literal('EC'),\n crv: z.string().min(1),\n x: z.string().min(1),\n y: z.string().min(1),\n d: z.string().min(1),\n })\n .passthrough();\n\n/**\n * Zod schema for public JWK (used in JWKS)\n */\nconst publicJwkSchema = z\n .object({\n kty: z.enum(['RSA', 'EC']),\n kid: z.string().min(1),\n alg: z.enum(['RS256', 'ES256']),\n use: z.literal('sig'),\n })\n .passthrough();\n\n/**\n * Zod schema for JWKS\n */\nconst jwksSchema = z.object({\n keys: z.array(publicJwkSchema).min(1),\n});\n\n/**\n * Zod schema for DevKeyData\n */\nconst devKeyDataSchema = z.object({\n kid: z.string().min(1),\n privateKey: z.union([rsaPrivateKeySchema, ecPrivateKeySchema]),\n publicJwk: jwksSchema,\n createdAt: z.number().positive().int(),\n alg: z.enum(['RS256', 'ES256']),\n});\n\n/**\n * Validate JWK structure based on algorithm\n */\nfunction validateJwkStructure(data: unknown): { valid: boolean; error?: string } {\n const result = devKeyDataSchema.safeParse(data);\n if (!result.success) {\n return { valid: false, error: result.error.issues[0]?.message ?? 'Invalid JWK structure' };\n }\n\n const parsed = result.data;\n\n // Verify algorithm matches key type\n if (parsed.alg === 'RS256' && parsed.privateKey.kty !== 'RSA') {\n return { valid: false, error: 'Algorithm RS256 requires RSA key type' };\n }\n if (parsed.alg === 'ES256' && parsed.privateKey.kty !== 'EC') {\n return { valid: false, error: 'Algorithm ES256 requires EC key type' };\n }\n\n // Verify public key matches private key algorithm\n const publicKey = parsed.publicJwk.keys[0];\n if (publicKey.kty !== parsed.privateKey.kty) {\n return { valid: false, error: 'Public and private key types do not match' };\n }\n\n // Verify kid consistency between top-level and publicJwk\n if (publicKey.kid !== parsed.kid) {\n return { valid: false, error: 'kid mismatch between top-level and publicJwk' };\n }\n\n // Verify createdAt is not in the future and not too old (100 years)\n const now = Date.now();\n const hundredYearsMs = 100 * 365 * 24 * 60 * 60 * 1000;\n if (parsed.createdAt > now) {\n return { valid: false, error: 'createdAt is in the future' };\n }\n if (parsed.createdAt < now - hundredYearsMs) {\n return { valid: false, error: 'createdAt is too old' };\n }\n\n return { valid: true };\n}\n\n/**\n * Check if dev key persistence is enabled based on environment and options\n */\nexport function isDevKeyPersistenceEnabled(options?: DevKeyPersistenceOptions): boolean {\n const isProduction = process.env['NODE_ENV'] === 'production';\n\n // In production, only enable if explicitly forced\n if (isProduction) {\n return options?.forceEnable === true;\n }\n\n // In development, enabled by default\n return true;\n}\n\n/**\n * Resolve the key file path\n */\nexport function resolveKeyPath(options?: DevKeyPersistenceOptions): string {\n const keyPath = options?.keyPath ?? DEFAULT_KEY_PATH;\n\n // If absolute path, use as-is\n if (path.isAbsolute(keyPath)) {\n return keyPath;\n }\n\n // Relative paths are resolved from current working directory\n return path.resolve(process.cwd(), keyPath);\n}\n\n/**\n * Load persisted dev key from file\n *\n * @param options - Persistence options\n * @returns The loaded key data or null if not found/invalid\n */\nexport async function loadDevKey(options?: DevKeyPersistenceOptions): Promise<DevKeyData | null> {\n if (!isDevKeyPersistenceEnabled(options)) {\n return null;\n }\n\n const keyPath = resolveKeyPath(options);\n\n try {\n const content = await fs.readFile(keyPath, 'utf8');\n const data = JSON.parse(content);\n\n // Validate JWK structure using Zod schema\n const validation = validateJwkStructure(data);\n if (!validation.valid) {\n console.warn(`[DevKeyPersistence] Invalid key file format at ${keyPath}: ${validation.error}, will regenerate`);\n return null;\n }\n\n console.log(`[DevKeyPersistence] Loaded key (kid=${data.kid}) from ${keyPath}`);\n return data as DevKeyData;\n } catch (error: unknown) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n // File doesn't exist - normal for first run\n return null;\n }\n\n console.warn(`[DevKeyPersistence] Failed to load key from ${keyPath}: ${(error as Error).message}`);\n return null;\n }\n}\n\n/**\n * Save dev key to file\n *\n * Uses atomic write (temp file + rename) to prevent corruption.\n * Sets file permissions to 0o600 (owner read/write only) for security.\n *\n * @param keyData - Key data to persist\n * @param options - Persistence options\n * @returns true if save succeeded, false otherwise\n */\nexport async function saveDevKey(keyData: DevKeyData, options?: DevKeyPersistenceOptions): Promise<boolean> {\n if (!isDevKeyPersistenceEnabled(options)) {\n return true; // Not enabled is not a failure\n }\n\n const keyPath = resolveKeyPath(options);\n const dir = path.dirname(keyPath);\n const tempPath = `${keyPath}.tmp.${Date.now()}.${crypto.randomBytes(8).toString('hex')}`;\n\n try {\n // Ensure directory exists with restricted permissions\n await fs.mkdir(dir, { recursive: true, mode: 0o700 });\n\n // Write to temp file first (atomic write pattern)\n const content = JSON.stringify(keyData, null, 2);\n await fs.writeFile(tempPath, content, { mode: 0o600, encoding: 'utf8' });\n\n // Atomic rename to target path\n await fs.rename(tempPath, keyPath);\n\n console.log(`[DevKeyPersistence] Saved key (kid=${keyData.kid}) to ${keyPath}`);\n return true;\n } catch (error: unknown) {\n console.error(`[DevKeyPersistence] Failed to save key to ${keyPath}: ${(error as Error).message}`);\n // Clean up temp file if it exists\n try {\n await fs.unlink(tempPath);\n } catch {\n // Ignore cleanup errors\n }\n return false;\n }\n}\n\n/**\n * Delete persisted dev key\n *\n * @param options - Persistence options\n */\nexport async function deleteDevKey(options?: DevKeyPersistenceOptions): Promise<void> {\n const keyPath = resolveKeyPath(options);\n\n try {\n await fs.unlink(keyPath);\n console.log(`[DevKeyPersistence] Deleted key at ${keyPath}`);\n } catch (error: unknown) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n console.warn(`[DevKeyPersistence] Failed to delete key at ${keyPath}: ${(error as Error).message}`);\n }\n }\n}\n"]}
@@ -1,2 +1,3 @@
1
1
  export * from './jwks.service';
2
2
  export * from './jwks.types';
3
+ export * from './dev-key-persistence';
@@ -3,4 +3,5 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const tslib_1 = require("tslib");
4
4
  tslib_1.__exportStar(require("./jwks.service"), exports);
5
5
  tslib_1.__exportStar(require("./jwks.types"), exports);
6
+ tslib_1.__exportStar(require("./dev-key-persistence"), exports);
6
7
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/auth/jwks/index.ts"],"names":[],"mappings":";;;AAAA,yDAA+B;AAC/B,uDAA6B","sourcesContent":["export * from './jwks.service';\nexport * from './jwks.types';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/auth/jwks/index.ts"],"names":[],"mappings":";;;AAAA,yDAA+B;AAC/B,uDAA6B;AAC7B,gEAAsC","sourcesContent":["export * from './jwks.service';\nexport * from './jwks.types';\nexport * from './dev-key-persistence';\n"]}
@@ -5,9 +5,11 @@ export declare class JwksService {
5
5
  private readonly opts;
6
6
  private orchestratorKey;
7
7
  private providerJwks;
8
+ private keyInitialized;
9
+ private keyInitPromise;
8
10
  constructor(opts?: JwksServiceOptions);
9
11
  /** Gateway's public JWKS (publish at /.well-known/jwks.json when orchestrated). */
10
- getPublicJwks(): JSONWebKeySet;
12
+ getPublicJwks(): Promise<JSONWebKeySet>;
11
13
  /** Verify a token issued by the gateway itself (orchestrated mode). */
12
14
  verifyGatewayToken(token: string, expectedIssuer: string): Promise<VerifyResult>;
13
15
  /**
@@ -26,16 +28,17 @@ export declare class JwksService {
26
28
  */
27
29
  getJwksForProvider(ref: ProviderVerifyRef): Promise<JSONWebKeySet | undefined>;
28
30
  /** Return the orchestrator public JWKS (generates/rotates as needed). */
29
- getOrchestratorJwks(): JSONWebKeySet;
31
+ getOrchestratorJwks(): Promise<JSONWebKeySet>;
30
32
  /** Return private signing key + kid for issuing orchestrator tokens. */
31
- getOrchestratorSigningKey(): {
33
+ getOrchestratorSigningKey(): Promise<{
32
34
  kid: string;
33
35
  key: crypto.KeyObject;
34
36
  alg: string;
35
- };
37
+ }>;
36
38
  private tryFetchJwks;
37
39
  private tryFetchAsMeta;
38
40
  private fetchJson;
39
41
  private ensureOrchestratorKey;
42
+ private initializeOrchestratorKey;
40
43
  private generateKey;
41
44
  }
@@ -6,25 +6,31 @@ const tslib_1 = require("tslib");
6
6
  const node_crypto_1 = tslib_1.__importDefault(require("node:crypto"));
7
7
  const jose_1 = require("jose");
8
8
  const jwks_utils_1 = require("./jwks.utils");
9
+ const dev_key_persistence_1 = require("./dev-key-persistence");
9
10
  class JwksService {
10
11
  opts;
11
12
  // Orchestrator signing material
12
13
  orchestratorKey;
13
14
  // Provider JWKS cache (providerId -> jwks + fetchedAt)
14
15
  providerJwks = new Map();
16
+ // Track if key has been initialized (for async loading)
17
+ keyInitialized = false;
18
+ // Promise guard to prevent concurrent key generation
19
+ keyInitPromise;
15
20
  constructor(opts) {
16
21
  this.opts = {
17
22
  orchestratorAlg: opts?.orchestratorAlg ?? 'RS256',
18
23
  rotateDays: opts?.rotateDays ?? 30,
19
24
  providerJwksTtlMs: opts?.providerJwksTtlMs ?? 6 * 60 * 60 * 1000, // 6h
20
25
  networkTimeoutMs: opts?.networkTimeoutMs ?? 5000, // 5s
26
+ devKeyPersistence: opts?.devKeyPersistence,
21
27
  };
22
28
  }
23
29
  // ===========================================================================
24
30
  // Public JWKS (what /.well-known/jwks.json serves)
25
31
  // ===========================================================================
26
32
  /** Gateway's public JWKS (publish at /.well-known/jwks.json when orchestrated). */
27
- getPublicJwks() {
33
+ async getPublicJwks() {
28
34
  return this.getOrchestratorJwks();
29
35
  }
30
36
  // ===========================================================================
@@ -51,7 +57,7 @@ class JwksService {
51
57
  if (!payload) {
52
58
  return {
53
59
  ok: false,
54
- error: 'invalid bearer token'
60
+ error: 'invalid bearer token',
55
61
  };
56
62
  }
57
63
  return {
@@ -90,9 +96,7 @@ class JwksService {
90
96
  const draftPayload = (0, jwks_utils_1.decodeJwtPayloadSafe)(token);
91
97
  const JWKS = (0, jose_1.createLocalJWKSet)(jwks);
92
98
  const { payload, protectedHeader } = await (0, jose_1.jwtVerify)(token, JWKS, {
93
- issuer: [
94
- (0, jwks_utils_1.normalizeIssuer)(p.issuerUrl),
95
- ].concat((draftPayload?.['iss'] ? [draftPayload['iss']] : [])), // used because current cloud gateway have invalid issuer
99
+ issuer: [(0, jwks_utils_1.normalizeIssuer)(p.issuerUrl)].concat((draftPayload?.['iss'] ? [draftPayload['iss']] : [])), // used because current cloud gateway have invalid issuer
96
100
  });
97
101
  return {
98
102
  ok: true,
@@ -156,13 +160,13 @@ class JwksService {
156
160
  // Orchestrator keys (generation/rotation)
157
161
  // ===========================================================================
158
162
  /** Return the orchestrator public JWKS (generates/rotates as needed). */
159
- getOrchestratorJwks() {
160
- this.ensureOrchestratorKey();
163
+ async getOrchestratorJwks() {
164
+ await this.ensureOrchestratorKey();
161
165
  return this.orchestratorKey.publicJwk;
162
166
  }
163
167
  /** Return private signing key + kid for issuing orchestrator tokens. */
164
- getOrchestratorSigningKey() {
165
- this.ensureOrchestratorKey();
168
+ async getOrchestratorSigningKey() {
169
+ await this.ensureOrchestratorKey();
166
170
  return { kid: this.orchestratorKey.kid, key: this.orchestratorKey.privateKey, alg: this.opts.orchestratorAlg };
167
171
  }
168
172
  // ===========================================================================
@@ -206,11 +210,76 @@ class JwksService {
206
210
  clearTimeout(timer);
207
211
  }
208
212
  }
209
- ensureOrchestratorKey() {
213
+ async ensureOrchestratorKey() {
210
214
  const now = Date.now();
211
215
  const maxAge = this.opts.rotateDays * 24 * 60 * 60 * 1000;
212
- if (!this.orchestratorKey || now - this.orchestratorKey.createdAt > maxAge) {
213
- this.orchestratorKey = this.generateKey(this.opts.orchestratorAlg);
216
+ // If key exists and not expired, use it
217
+ if (this.orchestratorKey && now - this.orchestratorKey.createdAt <= maxAge) {
218
+ return;
219
+ }
220
+ // Use promise guard to prevent concurrent key generation (race condition fix)
221
+ if (this.keyInitPromise) {
222
+ await this.keyInitPromise;
223
+ return;
224
+ }
225
+ // Create promise guard and initialize key
226
+ this.keyInitPromise = this.initializeOrchestratorKey(now, maxAge);
227
+ try {
228
+ await this.keyInitPromise;
229
+ }
230
+ finally {
231
+ // Clear promise guard after initialization to allow future rotation
232
+ this.keyInitPromise = undefined;
233
+ }
234
+ }
235
+ async initializeOrchestratorKey(now, maxAge) {
236
+ // Try to load persisted key (in development mode)
237
+ if ((0, dev_key_persistence_1.isDevKeyPersistenceEnabled)(this.opts.devKeyPersistence) && !this.keyInitialized) {
238
+ this.keyInitialized = true;
239
+ const loaded = await (0, dev_key_persistence_1.loadDevKey)(this.opts.devKeyPersistence);
240
+ if (loaded && now - loaded.createdAt <= maxAge) {
241
+ // Validate algorithm matches config
242
+ if (loaded.alg !== this.opts.orchestratorAlg) {
243
+ console.warn(`[JwksService] Persisted key algorithm (${loaded.alg}) doesn't match config (${this.opts.orchestratorAlg}), generating new key`);
244
+ }
245
+ else {
246
+ // Reconstruct KeyObject from JWK
247
+ try {
248
+ // Cast to crypto.JsonWebKey to satisfy TypeScript
249
+ const privateKey = node_crypto_1.default.createPrivateKey({
250
+ key: loaded.privateKey,
251
+ format: 'jwk',
252
+ });
253
+ this.orchestratorKey = {
254
+ kid: loaded.kid,
255
+ privateKey,
256
+ publicJwk: loaded.publicJwk,
257
+ createdAt: loaded.createdAt,
258
+ };
259
+ return;
260
+ }
261
+ catch (error) {
262
+ console.warn(`[JwksService] Failed to load persisted key: ${error.message}, generating new key`);
263
+ }
264
+ }
265
+ }
266
+ }
267
+ // Generate new key
268
+ this.orchestratorKey = this.generateKey(this.opts.orchestratorAlg);
269
+ this.keyInitialized = true;
270
+ // Save in development mode
271
+ if ((0, dev_key_persistence_1.isDevKeyPersistenceEnabled)(this.opts.devKeyPersistence)) {
272
+ const keyData = {
273
+ kid: this.orchestratorKey.kid,
274
+ privateKey: this.orchestratorKey.privateKey.export({ format: 'jwk' }),
275
+ publicJwk: this.orchestratorKey.publicJwk,
276
+ createdAt: this.orchestratorKey.createdAt,
277
+ alg: this.opts.orchestratorAlg,
278
+ };
279
+ const saved = await (0, dev_key_persistence_1.saveDevKey)(keyData, this.opts.devKeyPersistence);
280
+ if (!saved) {
281
+ console.warn('[JwksService] Failed to persist dev key - key will be regenerated on next restart');
282
+ }
214
283
  }
215
284
  }
216
285
  generateKey(alg) {
@@ -1 +1 @@
1
- {"version":3,"file":"jwks.service.js","sourceRoot":"","sources":["../../../../src/auth/jwks/jwks.service.ts"],"names":[],"mappings":"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","sourcesContent":["// auth/jwks/jwks.service.ts\nimport crypto from 'node:crypto';\nimport {jwtVerify, createLocalJWKSet, decodeProtectedHeader, JSONWebKeySet} from 'jose';\nimport {JwksServiceOptions, ProviderVerifyRef, VerifyResult} from './jwks.types';\nimport {normalizeIssuer, trimSlash, decodeJwtPayloadSafe} from './jwks.utils';\n\nexport class JwksService {\n private readonly opts: Required<JwksServiceOptions>;\n\n // Orchestrator signing material\n private orchestratorKey!: {\n kid: string;\n privateKey: crypto.KeyObject;\n publicJwk: JSONWebKeySet;\n createdAt: number;\n };\n\n // Provider JWKS cache (providerId -> jwks + fetchedAt)\n private providerJwks = new Map<string, { jwks: JSONWebKeySet; fetchedAt: number }>();\n\n constructor(opts?: JwksServiceOptions) {\n this.opts = {\n orchestratorAlg: opts?.orchestratorAlg ?? 'RS256',\n rotateDays: opts?.rotateDays ?? 30,\n providerJwksTtlMs: opts?.providerJwksTtlMs ?? 6 * 60 * 60 * 1000, // 6h\n networkTimeoutMs: opts?.networkTimeoutMs ?? 5000, // 5s\n };\n }\n\n // ===========================================================================\n // Public JWKS (what /.well-known/jwks.json serves)\n // ===========================================================================\n\n /** Gateway's public JWKS (publish at /.well-known/jwks.json when orchestrated). */\n getPublicJwks(): JSONWebKeySet {\n return this.getOrchestratorJwks();\n }\n\n // ===========================================================================\n // Scope-aware verification API\n // ===========================================================================\n\n /** Verify a token issued by the gateway itself (orchestrated mode). */\n async verifyGatewayToken(token: string, expectedIssuer: string): Promise<VerifyResult> {\n try {\n // TODO: add support for local/remote proxy mode\n // current implementation for anonymous mode only\n\n // const jwks = this.getPublicJwks();\n // const JWKS = createLocalJWKSet(jwks);\n // const {payload, protectedHeader} = await jwtVerify(token, JWKS, {\n // issuer: normalizeIssuer(expectedIssuer),\n // });\n // return {\n // ok: true,\n // issuer: payload?.iss as string | undefined,\n // sub: payload?.sub as string | undefined,\n // header: protectedHeader,\n // payload,\n // };\n\n const payload = decodeJwtPayloadSafe(token);\n if (!payload) {\n return {\n ok: false,\n error: 'invalid bearer token'\n }\n }\n return {\n ok: true,\n issuer: expectedIssuer,\n sub: payload['sub'] as string,\n payload,\n header: decodeProtectedHeader(token),\n }\n } catch (err: any) {\n return {ok: false, error: err?.message ?? 'verification_failed'};\n }\n }\n\n /**\n * Verify a token against candidate transparent providers.\n * Ensures JWKS are available (cached/TTL/AS discovery) per provider.\n */\n async verifyTransparentToken(token: string, candidates: ProviderVerifyRef[]): Promise<VerifyResult> {\n if (!candidates?.length) return {ok: false, error: 'no_providers'};\n\n // Helpful only for error messages\n let kid: string | undefined;\n try {\n const header = decodeProtectedHeader(token);\n\n kid = typeof header?.kid === 'string' ? header.kid : undefined;\n } catch {\n /* empty */\n }\n\n for (const p of candidates) {\n try {\n const jwks = await this.getJwksForProvider(p);\n if (!jwks?.keys?.length) continue;\n const draftPayload = decodeJwtPayloadSafe(token);\n const JWKS = createLocalJWKSet(jwks);\n const {payload, protectedHeader} = await jwtVerify(token, JWKS, {\n issuer: [\n normalizeIssuer(p.issuerUrl),\n ].concat((draftPayload?.['iss'] ? [draftPayload['iss']] : []) as string[]), // used because current cloud gateway have invalid issuer\n });\n\n return {\n ok: true,\n issuer: payload?.iss as string | undefined,\n sub: payload?.sub as string | undefined,\n providerId: p.id,\n header: protectedHeader,\n payload,\n };\n } catch (e) {\n console.log('failed to verify token for provider: ', p.id, e);\n // try next provider\n }\n }\n\n return {ok: false, error: `no_provider_verified${kid ? ` (kid=${kid})` : ''}`};\n }\n\n // ===========================================================================\n // Provider JWKS (cache + preload + discovery)\n // ===========================================================================\n\n /** Directly set provider JWKS (e.g., inline keys from config). */\n setProviderJwks(providerId: string, jwks: JSONWebKeySet) {\n this.providerJwks.set(providerId, {jwks, fetchedAt: Date.now()});\n }\n\n /**\n * Ensure JWKS for a provider:\n * 1) inline jwks (if provided) → cache & return\n * 2) cached & fresh (TTL) → return\n * 3) explicit jwksUri → fetch, cache, return\n * 4) discover jwks_uri via AS → fetch AS metadata, then jwks_uri, cache, return\n */\n async getJwksForProvider(ref: ProviderVerifyRef): Promise<JSONWebKeySet | undefined> {\n // Inline keys win\n if (ref.jwks?.keys?.length) {\n this.setProviderJwks(ref.id, ref.jwks);\n return ref.jwks;\n }\n\n // Cache hit and fresh?\n const cached = this.providerJwks.get(ref.id);\n if (cached && Date.now() - cached.fetchedAt < this.opts.providerJwksTtlMs) {\n return cached.jwks;\n }\n\n // If we have a jwksUri, try it\n if (ref.jwksUri) {\n const fromUri = await this.tryFetchJwks(ref.id, ref.jwksUri);\n if (fromUri?.keys?.length) return fromUri;\n }\n\n // Discover via AS .well-known\n const issuer = trimSlash(ref.issuerUrl);\n const meta = await this.tryFetchAsMeta(`${issuer}/.well-known/oauth-authorization-server`);\n const uri = meta && typeof meta === 'object' && meta.jwks_uri ? String(meta.jwks_uri) : undefined;\n if (uri) {\n const fromMeta = await this.tryFetchJwks(ref.id, uri);\n if (fromMeta?.keys?.length) return fromMeta;\n }\n\n return cached?.jwks; // return stale if we had anything, else undefined\n }\n\n // ===========================================================================\n // Orchestrator keys (generation/rotation)\n // ===========================================================================\n\n /** Return the orchestrator public JWKS (generates/rotates as needed). */\n getOrchestratorJwks(): JSONWebKeySet {\n this.ensureOrchestratorKey();\n return this.orchestratorKey.publicJwk;\n }\n\n /** Return private signing key + kid for issuing orchestrator tokens. */\n getOrchestratorSigningKey(): { kid: string; key: crypto.KeyObject; alg: string } {\n this.ensureOrchestratorKey();\n return {kid: this.orchestratorKey.kid, key: this.orchestratorKey.privateKey, alg: this.opts.orchestratorAlg};\n }\n\n // ===========================================================================\n // Internals (fetch, rotation, helpers)\n // ===========================================================================\n\n private async tryFetchJwks(providerId: string, uri: string): Promise<JSONWebKeySet | undefined> {\n try {\n const jwks = await this.fetchJson<JSONWebKeySet>(uri);\n if (jwks?.keys?.length) {\n this.setProviderJwks(providerId, jwks);\n return jwks;\n }\n } catch {\n /* empty */\n }\n return undefined;\n }\n\n private async tryFetchAsMeta(url: string): Promise<any | undefined> {\n try {\n return await this.fetchJson(url);\n } catch {\n return undefined;\n }\n }\n\n private async fetchJson<T = any>(url: string): Promise<T> {\n const ctl = typeof AbortController !== 'undefined' ? new AbortController() : undefined;\n const timer = setTimeout(() => ctl?.abort(), this.opts.networkTimeoutMs);\n try {\n const res = await fetch(url, {\n method: 'GET',\n headers: {accept: 'application/json'},\n signal: ctl?.signal,\n });\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\n return (await res.json()) as T;\n } finally {\n clearTimeout(timer);\n }\n }\n\n private ensureOrchestratorKey() {\n const now = Date.now();\n const maxAge = this.opts.rotateDays * 24 * 60 * 60 * 1000;\n if (!this.orchestratorKey || now - this.orchestratorKey.createdAt > maxAge) {\n this.orchestratorKey = this.generateKey(this.opts.orchestratorAlg);\n }\n }\n\n private generateKey(alg: 'RS256' | 'ES256') {\n if (alg === 'RS256') {\n const {privateKey, publicKey} = crypto.generateKeyPairSync('rsa', {modulusLength: 2048});\n const kid = crypto.randomBytes(8).toString('hex');\n const publicJwk = publicKey.export({format: 'jwk'});\n Object.assign(publicJwk, {kid, alg: 'RS256', use: 'sig', kty: 'RSA'});\n return {kid, privateKey, publicJwk: {keys: [publicJwk]}, createdAt: Date.now()};\n } else {\n const {privateKey, publicKey} = crypto.generateKeyPairSync('ec', {namedCurve: 'P-256'});\n const kid = crypto.randomBytes(8).toString('hex');\n const publicJwk = publicKey.export({format: 'jwk'});\n Object.assign(publicJwk, {kid, alg: 'ES256', use: 'sig', kty: 'EC'});\n return {kid, privateKey, publicJwk: {keys: [publicJwk]}, createdAt: Date.now()};\n }\n }\n}\n"]}
1
+ {"version":3,"file":"jwks.service.js","sourceRoot":"","sources":["../../../../src/auth/jwks/jwks.service.ts"],"names":[],"mappings":"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","sourcesContent":["// auth/jwks/jwks.service.ts\nimport crypto from 'node:crypto';\nimport { jwtVerify, createLocalJWKSet, decodeProtectedHeader, JSONWebKeySet } from 'jose';\nimport { JwksServiceOptions, ProviderVerifyRef, VerifyResult } from './jwks.types';\nimport { normalizeIssuer, trimSlash, decodeJwtPayloadSafe } from './jwks.utils';\nimport { isDevKeyPersistenceEnabled, loadDevKey, saveDevKey, DevKeyData } from './dev-key-persistence';\n\nexport class JwksService {\n private readonly opts: Required<Omit<JwksServiceOptions, 'devKeyPersistence'>> & {\n devKeyPersistence?: JwksServiceOptions['devKeyPersistence'];\n };\n\n // Orchestrator signing material\n private orchestratorKey!: {\n kid: string;\n privateKey: crypto.KeyObject;\n publicJwk: JSONWebKeySet;\n createdAt: number;\n };\n\n // Provider JWKS cache (providerId -> jwks + fetchedAt)\n private providerJwks = new Map<string, { jwks: JSONWebKeySet; fetchedAt: number }>();\n\n // Track if key has been initialized (for async loading)\n private keyInitialized = false;\n // Promise guard to prevent concurrent key generation\n private keyInitPromise: Promise<void> | undefined;\n\n constructor(opts?: JwksServiceOptions) {\n this.opts = {\n orchestratorAlg: opts?.orchestratorAlg ?? 'RS256',\n rotateDays: opts?.rotateDays ?? 30,\n providerJwksTtlMs: opts?.providerJwksTtlMs ?? 6 * 60 * 60 * 1000, // 6h\n networkTimeoutMs: opts?.networkTimeoutMs ?? 5000, // 5s\n devKeyPersistence: opts?.devKeyPersistence,\n };\n }\n\n // ===========================================================================\n // Public JWKS (what /.well-known/jwks.json serves)\n // ===========================================================================\n\n /** Gateway's public JWKS (publish at /.well-known/jwks.json when orchestrated). */\n async getPublicJwks(): Promise<JSONWebKeySet> {\n return this.getOrchestratorJwks();\n }\n\n // ===========================================================================\n // Scope-aware verification API\n // ===========================================================================\n\n /** Verify a token issued by the gateway itself (orchestrated mode). */\n async verifyGatewayToken(token: string, expectedIssuer: string): Promise<VerifyResult> {\n try {\n // TODO: add support for local/remote proxy mode\n // current implementation for anonymous mode only\n\n // const jwks = this.getPublicJwks();\n // const JWKS = createLocalJWKSet(jwks);\n // const {payload, protectedHeader} = await jwtVerify(token, JWKS, {\n // issuer: normalizeIssuer(expectedIssuer),\n // });\n // return {\n // ok: true,\n // issuer: payload?.iss as string | undefined,\n // sub: payload?.sub as string | undefined,\n // header: protectedHeader,\n // payload,\n // };\n\n const payload = decodeJwtPayloadSafe(token);\n if (!payload) {\n return {\n ok: false,\n error: 'invalid bearer token',\n };\n }\n return {\n ok: true,\n issuer: expectedIssuer,\n sub: payload['sub'] as string,\n payload,\n header: decodeProtectedHeader(token),\n };\n } catch (err: any) {\n return { ok: false, error: err?.message ?? 'verification_failed' };\n }\n }\n\n /**\n * Verify a token against candidate transparent providers.\n * Ensures JWKS are available (cached/TTL/AS discovery) per provider.\n */\n async verifyTransparentToken(token: string, candidates: ProviderVerifyRef[]): Promise<VerifyResult> {\n if (!candidates?.length) return { ok: false, error: 'no_providers' };\n\n // Helpful only for error messages\n let kid: string | undefined;\n try {\n const header = decodeProtectedHeader(token);\n\n kid = typeof header?.kid === 'string' ? header.kid : undefined;\n } catch {\n /* empty */\n }\n\n for (const p of candidates) {\n try {\n const jwks = await this.getJwksForProvider(p);\n if (!jwks?.keys?.length) continue;\n const draftPayload = decodeJwtPayloadSafe(token);\n const JWKS = createLocalJWKSet(jwks);\n const { payload, protectedHeader } = await jwtVerify(token, JWKS, {\n issuer: [normalizeIssuer(p.issuerUrl)].concat(\n (draftPayload?.['iss'] ? [draftPayload['iss']] : []) as string[],\n ), // used because current cloud gateway have invalid issuer\n });\n\n return {\n ok: true,\n issuer: payload?.iss as string | undefined,\n sub: payload?.sub as string | undefined,\n providerId: p.id,\n header: protectedHeader,\n payload,\n };\n } catch (e) {\n console.log('failed to verify token for provider: ', p.id, e);\n // try next provider\n }\n }\n\n return { ok: false, error: `no_provider_verified${kid ? ` (kid=${kid})` : ''}` };\n }\n\n // ===========================================================================\n // Provider JWKS (cache + preload + discovery)\n // ===========================================================================\n\n /** Directly set provider JWKS (e.g., inline keys from config). */\n setProviderJwks(providerId: string, jwks: JSONWebKeySet) {\n this.providerJwks.set(providerId, { jwks, fetchedAt: Date.now() });\n }\n\n /**\n * Ensure JWKS for a provider:\n * 1) inline jwks (if provided) → cache & return\n * 2) cached & fresh (TTL) → return\n * 3) explicit jwksUri → fetch, cache, return\n * 4) discover jwks_uri via AS → fetch AS metadata, then jwks_uri, cache, return\n */\n async getJwksForProvider(ref: ProviderVerifyRef): Promise<JSONWebKeySet | undefined> {\n // Inline keys win\n if (ref.jwks?.keys?.length) {\n this.setProviderJwks(ref.id, ref.jwks);\n return ref.jwks;\n }\n\n // Cache hit and fresh?\n const cached = this.providerJwks.get(ref.id);\n if (cached && Date.now() - cached.fetchedAt < this.opts.providerJwksTtlMs) {\n return cached.jwks;\n }\n\n // If we have a jwksUri, try it\n if (ref.jwksUri) {\n const fromUri = await this.tryFetchJwks(ref.id, ref.jwksUri);\n if (fromUri?.keys?.length) return fromUri;\n }\n\n // Discover via AS .well-known\n const issuer = trimSlash(ref.issuerUrl);\n const meta = await this.tryFetchAsMeta(`${issuer}/.well-known/oauth-authorization-server`);\n const uri = meta && typeof meta === 'object' && meta.jwks_uri ? String(meta.jwks_uri) : undefined;\n if (uri) {\n const fromMeta = await this.tryFetchJwks(ref.id, uri);\n if (fromMeta?.keys?.length) return fromMeta;\n }\n\n return cached?.jwks; // return stale if we had anything, else undefined\n }\n\n // ===========================================================================\n // Orchestrator keys (generation/rotation)\n // ===========================================================================\n\n /** Return the orchestrator public JWKS (generates/rotates as needed). */\n async getOrchestratorJwks(): Promise<JSONWebKeySet> {\n await this.ensureOrchestratorKey();\n return this.orchestratorKey.publicJwk;\n }\n\n /** Return private signing key + kid for issuing orchestrator tokens. */\n async getOrchestratorSigningKey(): Promise<{ kid: string; key: crypto.KeyObject; alg: string }> {\n await this.ensureOrchestratorKey();\n return { kid: this.orchestratorKey.kid, key: this.orchestratorKey.privateKey, alg: this.opts.orchestratorAlg };\n }\n\n // ===========================================================================\n // Internals (fetch, rotation, helpers)\n // ===========================================================================\n\n private async tryFetchJwks(providerId: string, uri: string): Promise<JSONWebKeySet | undefined> {\n try {\n const jwks = await this.fetchJson<JSONWebKeySet>(uri);\n if (jwks?.keys?.length) {\n this.setProviderJwks(providerId, jwks);\n return jwks;\n }\n } catch {\n /* empty */\n }\n return undefined;\n }\n\n private async tryFetchAsMeta(url: string): Promise<any | undefined> {\n try {\n return await this.fetchJson(url);\n } catch {\n return undefined;\n }\n }\n\n private async fetchJson<T = any>(url: string): Promise<T> {\n const ctl = typeof AbortController !== 'undefined' ? new AbortController() : undefined;\n const timer = setTimeout(() => ctl?.abort(), this.opts.networkTimeoutMs);\n try {\n const res = await fetch(url, {\n method: 'GET',\n headers: { accept: 'application/json' },\n signal: ctl?.signal,\n });\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\n return (await res.json()) as T;\n } finally {\n clearTimeout(timer);\n }\n }\n\n private async ensureOrchestratorKey() {\n const now = Date.now();\n const maxAge = this.opts.rotateDays * 24 * 60 * 60 * 1000;\n\n // If key exists and not expired, use it\n if (this.orchestratorKey && now - this.orchestratorKey.createdAt <= maxAge) {\n return;\n }\n\n // Use promise guard to prevent concurrent key generation (race condition fix)\n if (this.keyInitPromise) {\n await this.keyInitPromise;\n return;\n }\n\n // Create promise guard and initialize key\n this.keyInitPromise = this.initializeOrchestratorKey(now, maxAge);\n try {\n await this.keyInitPromise;\n } finally {\n // Clear promise guard after initialization to allow future rotation\n this.keyInitPromise = undefined;\n }\n }\n\n private async initializeOrchestratorKey(now: number, maxAge: number) {\n // Try to load persisted key (in development mode)\n if (isDevKeyPersistenceEnabled(this.opts.devKeyPersistence) && !this.keyInitialized) {\n this.keyInitialized = true;\n const loaded = await loadDevKey(this.opts.devKeyPersistence);\n\n if (loaded && now - loaded.createdAt <= maxAge) {\n // Validate algorithm matches config\n if (loaded.alg !== this.opts.orchestratorAlg) {\n console.warn(\n `[JwksService] Persisted key algorithm (${loaded.alg}) doesn't match config (${this.opts.orchestratorAlg}), generating new key`,\n );\n } else {\n // Reconstruct KeyObject from JWK\n try {\n // Cast to crypto.JsonWebKey to satisfy TypeScript\n const privateKey = crypto.createPrivateKey({\n key: loaded.privateKey as crypto.JsonWebKey,\n format: 'jwk',\n });\n this.orchestratorKey = {\n kid: loaded.kid,\n privateKey,\n publicJwk: loaded.publicJwk,\n createdAt: loaded.createdAt,\n };\n return;\n } catch (error) {\n console.warn(`[JwksService] Failed to load persisted key: ${(error as Error).message}, generating new key`);\n }\n }\n }\n }\n\n // Generate new key\n this.orchestratorKey = this.generateKey(this.opts.orchestratorAlg);\n this.keyInitialized = true;\n\n // Save in development mode\n if (isDevKeyPersistenceEnabled(this.opts.devKeyPersistence)) {\n const keyData: DevKeyData = {\n kid: this.orchestratorKey.kid,\n privateKey: this.orchestratorKey.privateKey.export({ format: 'jwk' }) as JsonWebKey,\n publicJwk: this.orchestratorKey.publicJwk,\n createdAt: this.orchestratorKey.createdAt,\n alg: this.opts.orchestratorAlg,\n };\n const saved = await saveDevKey(keyData, this.opts.devKeyPersistence);\n if (!saved) {\n console.warn('[JwksService] Failed to persist dev key - key will be regenerated on next restart');\n }\n }\n }\n\n private generateKey(alg: 'RS256' | 'ES256') {\n if (alg === 'RS256') {\n const { privateKey, publicKey } = crypto.generateKeyPairSync('rsa', { modulusLength: 2048 });\n const kid = crypto.randomBytes(8).toString('hex');\n const publicJwk = publicKey.export({ format: 'jwk' });\n Object.assign(publicJwk, { kid, alg: 'RS256', use: 'sig', kty: 'RSA' });\n return { kid, privateKey, publicJwk: { keys: [publicJwk] }, createdAt: Date.now() };\n } else {\n const { privateKey, publicKey } = crypto.generateKeyPairSync('ec', { namedCurve: 'P-256' });\n const kid = crypto.randomBytes(8).toString('hex');\n const publicJwk = publicKey.export({ format: 'jwk' });\n Object.assign(publicJwk, { kid, alg: 'ES256', use: 'sig', kty: 'EC' });\n return { kid, privateKey, publicJwk: { keys: [publicJwk] }, createdAt: Date.now() };\n }\n }\n}\n"]}
@@ -1,4 +1,5 @@
1
1
  import { JSONWebKeySet } from 'jose';
2
+ import { DevKeyPersistenceOptions } from './dev-key-persistence';
2
3
  export type JwksServiceOptions = {
3
4
  orchestratorAlg?: 'RS256' | 'ES256';
4
5
  rotateDays?: number;
@@ -6,7 +7,13 @@ export type JwksServiceOptions = {
6
7
  providerJwksTtlMs?: number;
7
8
  /** Timeout (ms) for network metadata/JWKS fetches. Default: 5s */
8
9
  networkTimeoutMs?: number;
10
+ /**
11
+ * Options for dev key persistence (development mode only by default).
12
+ * When enabled, keys are saved to a file and reloaded on server restart.
13
+ */
14
+ devKeyPersistence?: DevKeyPersistenceOptions;
9
15
  };
16
+ export type { DevKeyPersistenceOptions };
10
17
  /** Rich descriptor used by verification & fetching */
11
18
  export type ProviderVerifyRef = {
12
19
  id: string;
@@ -1 +1 @@
1
- {"version":3,"file":"jwks.types.js","sourceRoot":"","sources":["../../../../src/auth/jwks/jwks.types.ts"],"names":[],"mappings":"","sourcesContent":["import { JSONWebKeySet } from 'jose';\n\nexport type JwksServiceOptions = {\n orchestratorAlg?: 'RS256' | 'ES256';\n rotateDays?: number;\n /** TTL (ms) for cached provider JWKS before attempting refresh. Default: 6h */\n providerJwksTtlMs?: number;\n /** Timeout (ms) for network metadata/JWKS fetches. Default: 5s */\n networkTimeoutMs?: number;\n};\n\n/** Rich descriptor used by verification & fetching */\nexport type ProviderVerifyRef = {\n id: string;\n issuerUrl: string; // upstream issuer (e.g., https://idp.example.com)\n jwksUri?: string; // optional explicit JWKS uri\n jwks?: JSONWebKeySet; // optional inline keys (prioritized)\n};\n\nexport type VerifyResult = {\n ok: boolean;\n issuer?: string;\n sub?: string;\n providerId?: string;\n header?: any;\n payload?: any;\n error?: string;\n};\n"]}
1
+ {"version":3,"file":"jwks.types.js","sourceRoot":"","sources":["../../../../src/auth/jwks/jwks.types.ts"],"names":[],"mappings":"","sourcesContent":["import { JSONWebKeySet } from 'jose';\nimport { DevKeyPersistenceOptions } from './dev-key-persistence';\n\nexport type JwksServiceOptions = {\n orchestratorAlg?: 'RS256' | 'ES256';\n rotateDays?: number;\n /** TTL (ms) for cached provider JWKS before attempting refresh. Default: 6h */\n providerJwksTtlMs?: number;\n /** Timeout (ms) for network metadata/JWKS fetches. Default: 5s */\n networkTimeoutMs?: number;\n /**\n * Options for dev key persistence (development mode only by default).\n * When enabled, keys are saved to a file and reloaded on server restart.\n */\n devKeyPersistence?: DevKeyPersistenceOptions;\n};\n\nexport type { DevKeyPersistenceOptions };\n\n/** Rich descriptor used by verification & fetching */\nexport type ProviderVerifyRef = {\n id: string;\n issuerUrl: string; // upstream issuer (e.g., https://idp.example.com)\n jwksUri?: string; // optional explicit JWKS uri\n jwks?: JSONWebKeySet; // optional inline keys (prioritized)\n};\n\nexport type VerifyResult = {\n ok: boolean;\n issuer?: string;\n sub?: string;\n providerId?: string;\n header?: any;\n payload?: any;\n error?: string;\n};\n"]}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Get the current machine ID.
3
+ * This value is stable for the lifetime of the process.
4
+ */
5
+ export declare function getMachineId(): string;
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getMachineId = getMachineId;
4
+ // auth/machine-id.ts
5
+ // Single source of truth for the machine ID used across session management
6
+ const crypto_1 = require("crypto");
7
+ /**
8
+ * Single-process machine ID generated at server launch.
9
+ * Used for:
10
+ * - Session encryption key derivation
11
+ * - Session validation (nodeId matching)
12
+ *
13
+ * All session-related modules should import getMachineId from this module
14
+ * to ensure consistency across the application.
15
+ *
16
+ * IMPORTANT: If MACHINE_ID env var is not set, a random UUID is generated
17
+ * on each restart, invalidating all existing sessions. In production with
18
+ * multiple instances, set MACHINE_ID to enable session portability across
19
+ * nodes, or use instance-specific IDs to enforce session affinity.
20
+ */
21
+ const MACHINE_ID = (() => {
22
+ // Prefer an injected env (stable across restarts) if you have one; else random per launch
23
+ return process.env['MACHINE_ID'] || (0, crypto_1.randomUUID)();
24
+ })();
25
+ /**
26
+ * Get the current machine ID.
27
+ * This value is stable for the lifetime of the process.
28
+ */
29
+ function getMachineId() {
30
+ return MACHINE_ID;
31
+ }
32
+ //# sourceMappingURL=machine-id.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"machine-id.js","sourceRoot":"","sources":["../../../src/auth/machine-id.ts"],"names":[],"mappings":";;AA2BA,oCAEC;AA7BD,qBAAqB;AACrB,2EAA2E;AAC3E,mCAAoC;AAEpC;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,GAAG,CAAC,GAAG,EAAE;IACvB,0FAA0F;IAC1F,OAAO,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,IAAA,mBAAU,GAAE,CAAC;AACnD,CAAC,CAAC,EAAE,CAAC;AAEL;;;GAGG;AACH,SAAgB,YAAY;IAC1B,OAAO,UAAU,CAAC;AACpB,CAAC","sourcesContent":["// auth/machine-id.ts\n// Single source of truth for the machine ID used across session management\nimport { randomUUID } from 'crypto';\n\n/**\n * Single-process machine ID generated at server launch.\n * Used for:\n * - Session encryption key derivation\n * - Session validation (nodeId matching)\n *\n * All session-related modules should import getMachineId from this module\n * to ensure consistency across the application.\n *\n * IMPORTANT: If MACHINE_ID env var is not set, a random UUID is generated\n * on each restart, invalidating all existing sessions. In production with\n * multiple instances, set MACHINE_ID to enable session portability across\n * nodes, or use instance-specific IDs to enforce session affinity.\n */\nconst MACHINE_ID = (() => {\n // Prefer an injected env (stable across restarts) if you have one; else random per launch\n return process.env['MACHINE_ID'] || randomUUID();\n})();\n\n/**\n * Get the current machine ID.\n * This value is stable for the lifetime of the process.\n */\nexport function getMachineId(): string {\n return MACHINE_ID;\n}\n"]}
@@ -1,4 +1,5 @@
1
1
  export * from './transport-session.types';
2
2
  export { TransportSessionManager, InMemorySessionStore } from './transport-session.manager';
3
+ export { RedisSessionStore } from './redis-session.store';
3
4
  export * from './authorization.store';
4
5
  export * from './authorization-vault';