@blokjs/runner 0.2.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 (307) hide show
  1. package/dist/Blok.d.ts +19 -0
  2. package/dist/Blok.js +184 -0
  3. package/dist/Blok.js.map +1 -0
  4. package/dist/BlokResponse.d.ts +16 -0
  5. package/dist/BlokResponse.js +28 -0
  6. package/dist/BlokResponse.js.map +1 -0
  7. package/dist/Configuration.d.ts +37 -0
  8. package/dist/Configuration.js +248 -0
  9. package/dist/Configuration.js.map +1 -0
  10. package/dist/ConfigurationResolver.d.ts +7 -0
  11. package/dist/ConfigurationResolver.js +15 -0
  12. package/dist/ConfigurationResolver.js.map +1 -0
  13. package/dist/DefaultLogger.d.ts +65 -0
  14. package/dist/DefaultLogger.js +101 -0
  15. package/dist/DefaultLogger.js.map +1 -0
  16. package/dist/LocalStorage.d.ts +7 -0
  17. package/dist/LocalStorage.js +56 -0
  18. package/dist/LocalStorage.js.map +1 -0
  19. package/dist/MemoryUsage.d.ts +22 -0
  20. package/dist/MemoryUsage.js +83 -0
  21. package/dist/MemoryUsage.js.map +1 -0
  22. package/dist/NodeMap.d.ts +7 -0
  23. package/dist/NodeMap.js +13 -0
  24. package/dist/NodeMap.js.map +1 -0
  25. package/dist/ResolverBase.d.ts +8 -0
  26. package/dist/ResolverBase.js +18 -0
  27. package/dist/ResolverBase.js.map +1 -0
  28. package/dist/Runner.d.ts +25 -0
  29. package/dist/Runner.js +32 -0
  30. package/dist/Runner.js.map +1 -0
  31. package/dist/RunnerNode.d.ts +9 -0
  32. package/dist/RunnerNode.js +8 -0
  33. package/dist/RunnerNode.js.map +1 -0
  34. package/dist/RunnerNodeBase.d.ts +4 -0
  35. package/dist/RunnerNodeBase.js +3 -0
  36. package/dist/RunnerNodeBase.js.map +1 -0
  37. package/dist/RunnerSteps.d.ts +14 -0
  38. package/dist/RunnerSteps.js +110 -0
  39. package/dist/RunnerSteps.js.map +1 -0
  40. package/dist/RuntimeAdapterNode.d.ts +19 -0
  41. package/dist/RuntimeAdapterNode.js +87 -0
  42. package/dist/RuntimeAdapterNode.js.map +1 -0
  43. package/dist/RuntimeRegistry.d.ts +61 -0
  44. package/dist/RuntimeRegistry.js +87 -0
  45. package/dist/RuntimeRegistry.js.map +1 -0
  46. package/dist/TriggerBase.d.ts +119 -0
  47. package/dist/TriggerBase.js +413 -0
  48. package/dist/TriggerBase.js.map +1 -0
  49. package/dist/adapters/BunRuntimeAdapter.d.ts +38 -0
  50. package/dist/adapters/BunRuntimeAdapter.js +169 -0
  51. package/dist/adapters/BunRuntimeAdapter.js.map +1 -0
  52. package/dist/adapters/DockerRuntimeAdapter.d.ts +85 -0
  53. package/dist/adapters/DockerRuntimeAdapter.js +298 -0
  54. package/dist/adapters/DockerRuntimeAdapter.js.map +1 -0
  55. package/dist/adapters/HttpRuntimeAdapter.d.ts +58 -0
  56. package/dist/adapters/HttpRuntimeAdapter.js +152 -0
  57. package/dist/adapters/HttpRuntimeAdapter.js.map +1 -0
  58. package/dist/adapters/NodeJsRuntimeAdapter.d.ts +23 -0
  59. package/dist/adapters/NodeJsRuntimeAdapter.js +67 -0
  60. package/dist/adapters/NodeJsRuntimeAdapter.js.map +1 -0
  61. package/dist/adapters/RuntimeAdapter.d.ts +42 -0
  62. package/dist/adapters/RuntimeAdapter.js +2 -0
  63. package/dist/adapters/RuntimeAdapter.js.map +1 -0
  64. package/dist/adapters/WasmRuntimeAdapter.d.ts +69 -0
  65. package/dist/adapters/WasmRuntimeAdapter.js +279 -0
  66. package/dist/adapters/WasmRuntimeAdapter.js.map +1 -0
  67. package/dist/cache/NodeResultCache.d.ts +286 -0
  68. package/dist/cache/NodeResultCache.js +499 -0
  69. package/dist/cache/NodeResultCache.js.map +1 -0
  70. package/dist/cache/index.d.ts +1 -0
  71. package/dist/cache/index.js +2 -0
  72. package/dist/cache/index.js.map +1 -0
  73. package/dist/cost/CostEstimator.d.ts +57 -0
  74. package/dist/cost/CostEstimator.js +171 -0
  75. package/dist/cost/CostEstimator.js.map +1 -0
  76. package/dist/cost/index.d.ts +4 -0
  77. package/dist/cost/index.js +3 -0
  78. package/dist/cost/index.js.map +1 -0
  79. package/dist/cost/pricing.d.ts +24 -0
  80. package/dist/cost/pricing.js +169 -0
  81. package/dist/cost/pricing.js.map +1 -0
  82. package/dist/defineNode.d.ts +155 -0
  83. package/dist/defineNode.js +191 -0
  84. package/dist/defineNode.js.map +1 -0
  85. package/dist/graphql/GraphQLSchemaGenerator.d.ts +129 -0
  86. package/dist/graphql/GraphQLSchemaGenerator.js +425 -0
  87. package/dist/graphql/GraphQLSchemaGenerator.js.map +1 -0
  88. package/dist/hmr/FileWatcher.d.ts +62 -0
  89. package/dist/hmr/FileWatcher.js +185 -0
  90. package/dist/hmr/FileWatcher.js.map +1 -0
  91. package/dist/hmr/HmrDevConsole.d.ts +13 -0
  92. package/dist/hmr/HmrDevConsole.js +46 -0
  93. package/dist/hmr/HmrDevConsole.js.map +1 -0
  94. package/dist/hmr/HotReloadManager.d.ts +84 -0
  95. package/dist/hmr/HotReloadManager.js +195 -0
  96. package/dist/hmr/HotReloadManager.js.map +1 -0
  97. package/dist/hmr/index.d.ts +39 -0
  98. package/dist/hmr/index.js +38 -0
  99. package/dist/hmr/index.js.map +1 -0
  100. package/dist/index.d.ts +107 -0
  101. package/dist/index.js +107 -0
  102. package/dist/index.js.map +1 -0
  103. package/dist/integrations/APMIntegration.d.ts +141 -0
  104. package/dist/integrations/APMIntegration.js +212 -0
  105. package/dist/integrations/APMIntegration.js.map +1 -0
  106. package/dist/integrations/AzureMonitorIntegration.d.ts +118 -0
  107. package/dist/integrations/AzureMonitorIntegration.js +254 -0
  108. package/dist/integrations/AzureMonitorIntegration.js.map +1 -0
  109. package/dist/integrations/CloudWatchIntegration.d.ts +135 -0
  110. package/dist/integrations/CloudWatchIntegration.js +293 -0
  111. package/dist/integrations/CloudWatchIntegration.js.map +1 -0
  112. package/dist/integrations/SentryIntegration.d.ts +153 -0
  113. package/dist/integrations/SentryIntegration.js +200 -0
  114. package/dist/integrations/SentryIntegration.js.map +1 -0
  115. package/dist/integrations/index.d.ts +19 -0
  116. package/dist/integrations/index.js +16 -0
  117. package/dist/integrations/index.js.map +1 -0
  118. package/dist/marketplace/RuntimeAutoScaler.d.ts +148 -0
  119. package/dist/marketplace/RuntimeAutoScaler.js +366 -0
  120. package/dist/marketplace/RuntimeAutoScaler.js.map +1 -0
  121. package/dist/marketplace/RuntimeCatalog.d.ts +174 -0
  122. package/dist/marketplace/RuntimeCatalog.js +339 -0
  123. package/dist/marketplace/RuntimeCatalog.js.map +1 -0
  124. package/dist/marketplace/RuntimeDiscovery.d.ts +86 -0
  125. package/dist/marketplace/RuntimeDiscovery.js +219 -0
  126. package/dist/marketplace/RuntimeDiscovery.js.map +1 -0
  127. package/dist/marketplace/RuntimeHealthMonitor.d.ts +100 -0
  128. package/dist/marketplace/RuntimeHealthMonitor.js +241 -0
  129. package/dist/marketplace/RuntimeHealthMonitor.js.map +1 -0
  130. package/dist/marketplace/RuntimeMetricsDashboard.d.ts +113 -0
  131. package/dist/marketplace/RuntimeMetricsDashboard.js +293 -0
  132. package/dist/marketplace/RuntimeMetricsDashboard.js.map +1 -0
  133. package/dist/monitoring/CircuitBreaker.d.ts +107 -0
  134. package/dist/monitoring/CircuitBreaker.js +238 -0
  135. package/dist/monitoring/CircuitBreaker.js.map +1 -0
  136. package/dist/monitoring/DistributedTracer.d.ts +125 -0
  137. package/dist/monitoring/DistributedTracer.js +230 -0
  138. package/dist/monitoring/DistributedTracer.js.map +1 -0
  139. package/dist/monitoring/HealthCheck.d.ts +54 -0
  140. package/dist/monitoring/HealthCheck.js +102 -0
  141. package/dist/monitoring/HealthCheck.js.map +1 -0
  142. package/dist/monitoring/PerformanceProfiler.d.ts +63 -0
  143. package/dist/monitoring/PerformanceProfiler.js +229 -0
  144. package/dist/monitoring/PerformanceProfiler.js.map +1 -0
  145. package/dist/monitoring/PrometheusBootstrap.d.ts +30 -0
  146. package/dist/monitoring/PrometheusBootstrap.js +71 -0
  147. package/dist/monitoring/PrometheusBootstrap.js.map +1 -0
  148. package/dist/monitoring/PrometheusMetricsBridge.d.ts +60 -0
  149. package/dist/monitoring/PrometheusMetricsBridge.js +216 -0
  150. package/dist/monitoring/PrometheusMetricsBridge.js.map +1 -0
  151. package/dist/monitoring/RateLimiter.d.ts +58 -0
  152. package/dist/monitoring/RateLimiter.js +128 -0
  153. package/dist/monitoring/RateLimiter.js.map +1 -0
  154. package/dist/monitoring/StructuredLogger.d.ts +131 -0
  155. package/dist/monitoring/StructuredLogger.js +207 -0
  156. package/dist/monitoring/StructuredLogger.js.map +1 -0
  157. package/dist/monitoring/TracingBootstrap.d.ts +69 -0
  158. package/dist/monitoring/TracingBootstrap.js +129 -0
  159. package/dist/monitoring/TracingBootstrap.js.map +1 -0
  160. package/dist/monitoring/TriggerMetricsCollector.d.ts +94 -0
  161. package/dist/monitoring/TriggerMetricsCollector.js +174 -0
  162. package/dist/monitoring/TriggerMetricsCollector.js.map +1 -0
  163. package/dist/monitoring/index.d.ts +9 -0
  164. package/dist/monitoring/index.js +10 -0
  165. package/dist/monitoring/index.js.map +1 -0
  166. package/dist/openapi/OpenAPIGenerator.d.ts +192 -0
  167. package/dist/openapi/OpenAPIGenerator.js +373 -0
  168. package/dist/openapi/OpenAPIGenerator.js.map +1 -0
  169. package/dist/openapi/index.d.ts +20 -0
  170. package/dist/openapi/index.js +20 -0
  171. package/dist/openapi/index.js.map +1 -0
  172. package/dist/security/ABAC.d.ts +224 -0
  173. package/dist/security/ABAC.js +380 -0
  174. package/dist/security/ABAC.js.map +1 -0
  175. package/dist/security/AuditLogger.d.ts +242 -0
  176. package/dist/security/AuditLogger.js +317 -0
  177. package/dist/security/AuditLogger.js.map +1 -0
  178. package/dist/security/AuthMiddleware.d.ts +163 -0
  179. package/dist/security/AuthMiddleware.js +274 -0
  180. package/dist/security/AuthMiddleware.js.map +1 -0
  181. package/dist/security/EncryptionAtRest.d.ts +206 -0
  182. package/dist/security/EncryptionAtRest.js +236 -0
  183. package/dist/security/EncryptionAtRest.js.map +1 -0
  184. package/dist/security/OAuthProvider.d.ts +334 -0
  185. package/dist/security/OAuthProvider.js +719 -0
  186. package/dist/security/OAuthProvider.js.map +1 -0
  187. package/dist/security/PIIDetector.d.ts +233 -0
  188. package/dist/security/PIIDetector.js +354 -0
  189. package/dist/security/PIIDetector.js.map +1 -0
  190. package/dist/security/RBAC.d.ts +143 -0
  191. package/dist/security/RBAC.js +285 -0
  192. package/dist/security/RBAC.js.map +1 -0
  193. package/dist/security/SecretManager.d.ts +652 -0
  194. package/dist/security/SecretManager.js +1146 -0
  195. package/dist/security/SecretManager.js.map +1 -0
  196. package/dist/security/TLSConfig.d.ts +305 -0
  197. package/dist/security/TLSConfig.js +550 -0
  198. package/dist/security/TLSConfig.js.map +1 -0
  199. package/dist/security/index.d.ts +79 -0
  200. package/dist/security/index.js +80 -0
  201. package/dist/security/index.js.map +1 -0
  202. package/dist/testing/TestHarness.d.ts +189 -0
  203. package/dist/testing/TestHarness.js +272 -0
  204. package/dist/testing/TestHarness.js.map +1 -0
  205. package/dist/testing/TestLogger.d.ts +103 -0
  206. package/dist/testing/TestLogger.js +153 -0
  207. package/dist/testing/TestLogger.js.map +1 -0
  208. package/dist/testing/WorkflowTestRunner.d.ts +172 -0
  209. package/dist/testing/WorkflowTestRunner.js +355 -0
  210. package/dist/testing/WorkflowTestRunner.js.map +1 -0
  211. package/dist/testing/index.d.ts +21 -0
  212. package/dist/testing/index.js +22 -0
  213. package/dist/testing/index.js.map +1 -0
  214. package/dist/tracing/InMemoryRunStore.d.ts +44 -0
  215. package/dist/tracing/InMemoryRunStore.js +341 -0
  216. package/dist/tracing/InMemoryRunStore.js.map +1 -0
  217. package/dist/tracing/PostgresRunStore.d.ts +82 -0
  218. package/dist/tracing/PostgresRunStore.js +640 -0
  219. package/dist/tracing/PostgresRunStore.js.map +1 -0
  220. package/dist/tracing/RunStore.d.ts +38 -0
  221. package/dist/tracing/RunStore.js +2 -0
  222. package/dist/tracing/RunStore.js.map +1 -0
  223. package/dist/tracing/RunTracker.d.ts +75 -0
  224. package/dist/tracing/RunTracker.js +374 -0
  225. package/dist/tracing/RunTracker.js.map +1 -0
  226. package/dist/tracing/SqliteRunStore.d.ts +53 -0
  227. package/dist/tracing/SqliteRunStore.js +703 -0
  228. package/dist/tracing/SqliteRunStore.js.map +1 -0
  229. package/dist/tracing/TraceRouter.d.ts +47 -0
  230. package/dist/tracing/TraceRouter.js +904 -0
  231. package/dist/tracing/TraceRouter.js.map +1 -0
  232. package/dist/tracing/TracingLogger.d.ts +21 -0
  233. package/dist/tracing/TracingLogger.js +62 -0
  234. package/dist/tracing/TracingLogger.js.map +1 -0
  235. package/dist/tracing/createStore.d.ts +30 -0
  236. package/dist/tracing/createStore.js +75 -0
  237. package/dist/tracing/createStore.js.map +1 -0
  238. package/dist/tracing/index.d.ts +13 -0
  239. package/dist/tracing/index.js +9 -0
  240. package/dist/tracing/index.js.map +1 -0
  241. package/dist/tracing/sanitize.d.ts +7 -0
  242. package/dist/tracing/sanitize.js +95 -0
  243. package/dist/tracing/sanitize.js.map +1 -0
  244. package/dist/tracing/types.d.ts +178 -0
  245. package/dist/tracing/types.js +3 -0
  246. package/dist/tracing/types.js.map +1 -0
  247. package/dist/types/Average.d.ts +11 -0
  248. package/dist/types/Average.js +2 -0
  249. package/dist/types/Average.js.map +1 -0
  250. package/dist/types/Condition.d.ts +8 -0
  251. package/dist/types/Condition.js +2 -0
  252. package/dist/types/Condition.js.map +1 -0
  253. package/dist/types/Conditions.d.ts +5 -0
  254. package/dist/types/Conditions.js +2 -0
  255. package/dist/types/Conditions.js.map +1 -0
  256. package/dist/types/Config.d.ts +12 -0
  257. package/dist/types/Config.js +2 -0
  258. package/dist/types/Config.js.map +1 -0
  259. package/dist/types/Flow.d.ts +5 -0
  260. package/dist/types/Flow.js +2 -0
  261. package/dist/types/Flow.js.map +1 -0
  262. package/dist/types/GlobalOptions.d.ts +11 -0
  263. package/dist/types/GlobalOptions.js +2 -0
  264. package/dist/types/GlobalOptions.js.map +1 -0
  265. package/dist/types/Inputs.d.ts +5 -0
  266. package/dist/types/Inputs.js +2 -0
  267. package/dist/types/Inputs.js.map +1 -0
  268. package/dist/types/JsonLikeObject.d.ts +3 -0
  269. package/dist/types/JsonLikeObject.js +2 -0
  270. package/dist/types/JsonLikeObject.js.map +1 -0
  271. package/dist/types/Mapper.d.ts +5 -0
  272. package/dist/types/Mapper.js +2 -0
  273. package/dist/types/Mapper.js.map +1 -0
  274. package/dist/types/Node.d.ts +10 -0
  275. package/dist/types/Node.js +2 -0
  276. package/dist/types/Node.js.map +1 -0
  277. package/dist/types/ParamsDictionary.d.ts +3 -0
  278. package/dist/types/ParamsDictionary.js +2 -0
  279. package/dist/types/ParamsDictionary.js.map +1 -0
  280. package/dist/types/Properties.d.ts +5 -0
  281. package/dist/types/Properties.js +2 -0
  282. package/dist/types/Properties.js.map +1 -0
  283. package/dist/types/Targets.d.ts +5 -0
  284. package/dist/types/Targets.js +2 -0
  285. package/dist/types/Targets.js.map +1 -0
  286. package/dist/types/Trigger.d.ts +5 -0
  287. package/dist/types/Trigger.js +2 -0
  288. package/dist/types/Trigger.js.map +1 -0
  289. package/dist/types/TriggerHttp.d.ts +7 -0
  290. package/dist/types/TriggerHttp.js +2 -0
  291. package/dist/types/TriggerHttp.js.map +1 -0
  292. package/dist/types/TriggerResponse.d.ts +6 -0
  293. package/dist/types/TriggerResponse.js +2 -0
  294. package/dist/types/TriggerResponse.js.map +1 -0
  295. package/dist/types/Triggers.d.ts +5 -0
  296. package/dist/types/Triggers.js +2 -0
  297. package/dist/types/Triggers.js.map +1 -0
  298. package/dist/types/TryCatch.d.ts +6 -0
  299. package/dist/types/TryCatch.js +2 -0
  300. package/dist/types/TryCatch.js.map +1 -0
  301. package/dist/visualization/NodeDependencyGraph.d.ts +76 -0
  302. package/dist/visualization/NodeDependencyGraph.js +418 -0
  303. package/dist/visualization/NodeDependencyGraph.js.map +1 -0
  304. package/dist/visualization/WorkflowVisualizer.d.ts +144 -0
  305. package/dist/visualization/WorkflowVisualizer.js +446 -0
  306. package/dist/visualization/WorkflowVisualizer.js.map +1 -0
  307. package/package.json +95 -0
@@ -0,0 +1,719 @@
1
+ /**
2
+ * OAuth 2.0 / OpenID Connect Authentication Provider for Blok
3
+ *
4
+ * Provides standards-compliant OIDC authentication with:
5
+ * - OIDC Discovery (/.well-known/openid-configuration)
6
+ * - JWKS-based public key retrieval and caching
7
+ * - JWT verification using RS256 (RSA) and ES256 (ECDSA) via Node.js crypto
8
+ * - Token claims validation (exp, nbf, iss, aud)
9
+ * - Token introspection for opaque tokens (RFC 7662)
10
+ * - LRU token cache with TTL-based expiry
11
+ *
12
+ * Uses only Node.js built-in modules (node:crypto, node:buffer).
13
+ * No external JWT libraries required.
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * const oidcProvider = new OAuthOIDCProvider({
18
+ * issuerUrl: "https://auth.example.com",
19
+ * clientId: "my-app",
20
+ * audience: "https://api.example.com",
21
+ * });
22
+ *
23
+ * const auth = new AuthMiddleware({
24
+ * providers: [oidcProvider],
25
+ * });
26
+ * ```
27
+ */
28
+ import { Buffer } from "node:buffer";
29
+ import { createHash, createPublicKey, createVerify } from "node:crypto";
30
+ /**
31
+ * LRU-style token cache with TTL-based expiry.
32
+ *
33
+ * Caches validated token identities keyed by a SHA-256 hash of the raw token.
34
+ * When the cache exceeds its maximum size the least-recently-used entry is
35
+ * evicted.
36
+ *
37
+ * @example
38
+ * ```typescript
39
+ * const cache = new TokenCache(500);
40
+ * const hash = cache.hashToken(rawJwt);
41
+ *
42
+ * // Store after validation
43
+ * cache.set(hash, identity, 3600_000);
44
+ *
45
+ * // Retrieve on subsequent requests
46
+ * const cached = cache.get(hash);
47
+ * ```
48
+ */
49
+ export class TokenCache {
50
+ cache = new Map();
51
+ maxSize;
52
+ hits = 0;
53
+ misses = 0;
54
+ evictions = 0;
55
+ constructor(maxSize = 1000) {
56
+ this.maxSize = maxSize;
57
+ }
58
+ /**
59
+ * Retrieve a cached identity by token hash.
60
+ * Returns `undefined` if the entry is missing or has expired.
61
+ * Moves the entry to the end of the map (most-recently-used).
62
+ */
63
+ get(tokenHash) {
64
+ const entry = this.cache.get(tokenHash);
65
+ if (!entry) {
66
+ this.misses++;
67
+ return undefined;
68
+ }
69
+ // TTL check
70
+ if (Date.now() >= entry.expiresAt) {
71
+ this.cache.delete(tokenHash);
72
+ this.evictions++;
73
+ this.misses++;
74
+ return undefined;
75
+ }
76
+ // Move to end (most-recently-used)
77
+ this.cache.delete(tokenHash);
78
+ this.cache.set(tokenHash, entry);
79
+ this.hits++;
80
+ return entry.identity;
81
+ }
82
+ /**
83
+ * Store an identity in the cache.
84
+ *
85
+ * @param tokenHash - SHA-256 hex hash of the raw token
86
+ * @param identity - Validated identity to cache
87
+ * @param ttlMs - Time-to-live in milliseconds
88
+ */
89
+ set(tokenHash, identity, ttlMs) {
90
+ // If already present, delete first so re-insertion goes to end
91
+ if (this.cache.has(tokenHash)) {
92
+ this.cache.delete(tokenHash);
93
+ }
94
+ // Evict oldest entry if at capacity
95
+ if (this.cache.size >= this.maxSize) {
96
+ const oldest = this.cache.keys().next().value;
97
+ if (oldest !== undefined) {
98
+ this.cache.delete(oldest);
99
+ this.evictions++;
100
+ }
101
+ }
102
+ this.cache.set(tokenHash, {
103
+ identity,
104
+ expiresAt: Date.now() + ttlMs,
105
+ });
106
+ }
107
+ /**
108
+ * Invalidate (remove) a specific entry by token hash
109
+ */
110
+ invalidate(tokenHash) {
111
+ return this.cache.delete(tokenHash);
112
+ }
113
+ /**
114
+ * Remove all entries from the cache and reset statistics
115
+ */
116
+ clear() {
117
+ this.cache.clear();
118
+ this.hits = 0;
119
+ this.misses = 0;
120
+ this.evictions = 0;
121
+ }
122
+ /**
123
+ * Return cache performance statistics
124
+ */
125
+ getStats() {
126
+ return {
127
+ size: this.cache.size,
128
+ maxSize: this.maxSize,
129
+ hits: this.hits,
130
+ misses: this.misses,
131
+ evictions: this.evictions,
132
+ };
133
+ }
134
+ /**
135
+ * Compute a SHA-256 hex digest suitable for use as a cache key
136
+ */
137
+ static hashToken(token) {
138
+ return createHash("sha256").update(token).digest("hex");
139
+ }
140
+ }
141
+ // ---------------------------------------------------------------------------
142
+ // OAuthOIDCProvider
143
+ // ---------------------------------------------------------------------------
144
+ /**
145
+ * OAuth 2.0 / OpenID Connect Authentication Provider
146
+ *
147
+ * Validates JWT access tokens issued by an OIDC-compliant identity provider.
148
+ * Automatically discovers OIDC metadata and JWKS endpoints, verifies token
149
+ * signatures using RS256 or ES256, and validates standard claims.
150
+ *
151
+ * For opaque (non-JWT) tokens, falls back to RFC 7662 token introspection
152
+ * when an introspection endpoint is configured or discovered.
153
+ *
154
+ * @example
155
+ * ```typescript
156
+ * const provider = new OAuthOIDCProvider({
157
+ * issuerUrl: "https://accounts.google.com",
158
+ * clientId: "my-client-id",
159
+ * audience: "https://api.example.com",
160
+ * });
161
+ *
162
+ * const result = await provider.authenticate({
163
+ * headers: { authorization: "Bearer eyJhbGciOi..." },
164
+ * });
165
+ *
166
+ * if (result.authenticated) {
167
+ * console.log("Authenticated:", result.identity?.sub);
168
+ * }
169
+ * ```
170
+ */
171
+ export class OAuthOIDCProvider {
172
+ name = "oauth-oidc";
173
+ config;
174
+ /** Cached OIDC discovery document */
175
+ discoveryCache = null;
176
+ /** Cached JWKS keyset */
177
+ jwksCache = null;
178
+ /** Pre-imported Node.js KeyObjects keyed by kid */
179
+ keyObjectCache = new Map();
180
+ /** Token result cache */
181
+ tokenCache;
182
+ constructor(config) {
183
+ this.config = {
184
+ allowedAlgorithms: ["RS256", "ES256"],
185
+ rolesClaim: "roles",
186
+ scopesClaim: "scope",
187
+ clockToleranceSec: 30,
188
+ cacheJWKS: true,
189
+ cacheDiscovery: true,
190
+ ...config,
191
+ };
192
+ this.tokenCache = new TokenCache();
193
+ }
194
+ // -----------------------------------------------------------------------
195
+ // AuthProvider implementation
196
+ // -----------------------------------------------------------------------
197
+ /**
198
+ * Authenticate an incoming request by extracting and verifying the
199
+ * Bearer token from the Authorization header.
200
+ */
201
+ async authenticate(request) {
202
+ const headerValue = request.headers["authorization"] ?? request.headers["Authorization"];
203
+ if (!headerValue) {
204
+ return { authenticated: false, error: "No authorization header" };
205
+ }
206
+ const raw = String(headerValue).replace(/^Bearer\s+/i, "");
207
+ if (!raw || raw === String(headerValue)) {
208
+ return { authenticated: false, error: "Invalid Bearer token format" };
209
+ }
210
+ // Check token cache first
211
+ const tokenHash = TokenCache.hashToken(raw);
212
+ const cachedIdentity = this.tokenCache.get(tokenHash);
213
+ if (cachedIdentity) {
214
+ return { authenticated: true, identity: cachedIdentity };
215
+ }
216
+ try {
217
+ // Attempt JWT verification first
218
+ const identity = await this.verifyJWT(raw);
219
+ if (identity) {
220
+ // Cache with TTL derived from token expiry
221
+ const ttlMs = identity.expiresAt ? (identity.expiresAt - Math.floor(Date.now() / 1000)) * 1000 : 300_000; // 5 min fallback
222
+ if (ttlMs > 0) {
223
+ this.tokenCache.set(tokenHash, identity, ttlMs);
224
+ }
225
+ return { authenticated: true, identity };
226
+ }
227
+ // Fall back to introspection for opaque tokens
228
+ const introspectionEndpoint = this.config.introspectionEndpoint ?? (await this.resolveIntrospectionEndpoint());
229
+ if (introspectionEndpoint) {
230
+ const introspectionResult = await this.introspectToken(raw, introspectionEndpoint);
231
+ if (introspectionResult) {
232
+ const ttlMs = introspectionResult.expiresAt
233
+ ? (introspectionResult.expiresAt - Math.floor(Date.now() / 1000)) * 1000
234
+ : 300_000;
235
+ if (ttlMs > 0) {
236
+ this.tokenCache.set(tokenHash, introspectionResult, ttlMs);
237
+ }
238
+ return { authenticated: true, identity: introspectionResult };
239
+ }
240
+ }
241
+ return { authenticated: false, error: "Token verification failed", statusCode: 401 };
242
+ }
243
+ catch (err) {
244
+ return {
245
+ authenticated: false,
246
+ error: `OAuth authentication failed: ${err instanceof Error ? err.message : String(err)}`,
247
+ statusCode: 401,
248
+ };
249
+ }
250
+ }
251
+ // -----------------------------------------------------------------------
252
+ // OIDC Discovery
253
+ // -----------------------------------------------------------------------
254
+ /**
255
+ * Fetch and parse the OIDC discovery document from the issuer's
256
+ * `/.well-known/openid-configuration` endpoint.
257
+ *
258
+ * The result is cached when `cacheDiscovery` is enabled (default).
259
+ *
260
+ * @param issuerUrl - The OIDC issuer URL
261
+ * @returns Parsed OIDC discovery document
262
+ */
263
+ async discoverConfiguration(issuerUrl) {
264
+ if (this.config.cacheDiscovery && this.discoveryCache) {
265
+ return this.discoveryCache;
266
+ }
267
+ const url = issuerUrl.replace(/\/+$/, "") + "/.well-known/openid-configuration";
268
+ const response = await fetch(url, {
269
+ method: "GET",
270
+ headers: { Accept: "application/json" },
271
+ });
272
+ if (!response.ok) {
273
+ throw new Error(`OIDC discovery failed: ${response.status} ${response.statusText} from ${url}`);
274
+ }
275
+ const doc = (await response.json());
276
+ // Validate required fields
277
+ if (!doc.issuer || !doc.jwks_uri) {
278
+ throw new Error("OIDC discovery document missing required fields (issuer, jwks_uri)");
279
+ }
280
+ if (this.config.cacheDiscovery) {
281
+ this.discoveryCache = doc;
282
+ }
283
+ return doc;
284
+ }
285
+ // -----------------------------------------------------------------------
286
+ // JWKS Management
287
+ // -----------------------------------------------------------------------
288
+ /**
289
+ * Fetch the JSON Web Key Set from the specified URI.
290
+ *
291
+ * The result is cached when `cacheJWKS` is enabled (default).
292
+ *
293
+ * @param jwksUri - URL of the JWKS endpoint
294
+ * @returns The parsed JWKS
295
+ */
296
+ async fetchJWKS(jwksUri) {
297
+ if (this.config.cacheJWKS && this.jwksCache) {
298
+ return this.jwksCache;
299
+ }
300
+ const response = await fetch(jwksUri, {
301
+ method: "GET",
302
+ headers: { Accept: "application/json" },
303
+ });
304
+ if (!response.ok) {
305
+ throw new Error(`JWKS fetch failed: ${response.status} ${response.statusText} from ${jwksUri}`);
306
+ }
307
+ const jwks = (await response.json());
308
+ if (!jwks.keys || !Array.isArray(jwks.keys)) {
309
+ throw new Error("JWKS response missing 'keys' array");
310
+ }
311
+ if (this.config.cacheJWKS) {
312
+ this.jwksCache = jwks;
313
+ // Rebuild the key-object cache
314
+ this.keyObjectCache.clear();
315
+ }
316
+ return jwks;
317
+ }
318
+ // -----------------------------------------------------------------------
319
+ // Token introspection (RFC 7662)
320
+ // -----------------------------------------------------------------------
321
+ /**
322
+ * Introspect an opaque token via the RFC 7662 token introspection endpoint.
323
+ *
324
+ * Requires `clientId` and `clientSecret` for Basic authentication.
325
+ *
326
+ * @param token - The raw token string
327
+ * @param introspectionEndpoint - URL of the introspection endpoint
328
+ * @returns Resolved AuthIdentity when the token is active, otherwise `null`
329
+ */
330
+ async introspectToken(token, introspectionEndpoint) {
331
+ if (!this.config.clientSecret) {
332
+ return null;
333
+ }
334
+ const credentials = Buffer.from(`${this.config.clientId}:${this.config.clientSecret}`).toString("base64");
335
+ const response = await fetch(introspectionEndpoint, {
336
+ method: "POST",
337
+ headers: {
338
+ "Content-Type": "application/x-www-form-urlencoded",
339
+ Authorization: `Basic ${credentials}`,
340
+ Accept: "application/json",
341
+ },
342
+ body: `token=${encodeURIComponent(token)}&token_type_hint=access_token`,
343
+ });
344
+ if (!response.ok) {
345
+ return null;
346
+ }
347
+ const data = (await response.json());
348
+ if (!data.active) {
349
+ return null;
350
+ }
351
+ // Validate issuer if configured
352
+ if (data.iss && data.iss !== this.config.issuerUrl) {
353
+ return null;
354
+ }
355
+ // Extract roles from the introspection response
356
+ const roles = this.extractRoles(data);
357
+ return {
358
+ sub: data.sub || data.client_id || "unknown",
359
+ name: data.username,
360
+ roles,
361
+ claims: data,
362
+ provider: this.name,
363
+ issuedAt: data.iat,
364
+ expiresAt: data.exp,
365
+ };
366
+ }
367
+ // -----------------------------------------------------------------------
368
+ // JWT Verification (core)
369
+ // -----------------------------------------------------------------------
370
+ /**
371
+ * Verify a JWT access token using the issuer's public keys.
372
+ *
373
+ * 1. Decodes the JWT header to determine `alg` and `kid`.
374
+ * 2. Resolves the JWKS (via discovery or explicit config).
375
+ * 3. Selects the matching JWK by `kid`.
376
+ * 4. Verifies the signature using RS256 or ES256.
377
+ * 5. Validates standard claims (exp, nbf, iss, aud).
378
+ *
379
+ * @param token - Raw JWT string
380
+ * @returns Resolved AuthIdentity on success, `null` on failure
381
+ */
382
+ async verifyJWT(token) {
383
+ // Split and basic structure check
384
+ const parts = token.split(".");
385
+ if (parts.length !== 3) {
386
+ return null;
387
+ }
388
+ const [headerB64, payloadB64, signatureB64] = parts;
389
+ // Decode header
390
+ let header;
391
+ try {
392
+ header = JSON.parse(Buffer.from(headerB64, "base64url").toString("utf-8"));
393
+ }
394
+ catch {
395
+ return null;
396
+ }
397
+ // Verify algorithm is allowed
398
+ if (!this.config.allowedAlgorithms.includes(header.alg)) {
399
+ return null;
400
+ }
401
+ // Resolve the public key
402
+ const publicKey = await this.resolvePublicKey(header.alg, header.kid);
403
+ if (!publicKey) {
404
+ return null;
405
+ }
406
+ // Verify signature
407
+ const signingInput = `${headerB64}.${payloadB64}`;
408
+ const signature = Buffer.from(signatureB64, "base64url");
409
+ const valid = this.verifySignature(header.alg, publicKey, signingInput, signature);
410
+ if (!valid) {
411
+ return null;
412
+ }
413
+ // Decode payload
414
+ let payload;
415
+ try {
416
+ payload = JSON.parse(Buffer.from(payloadB64, "base64url").toString("utf-8"));
417
+ }
418
+ catch {
419
+ return null;
420
+ }
421
+ // Validate claims
422
+ const claimsError = this.validateClaims(payload);
423
+ if (claimsError) {
424
+ return null;
425
+ }
426
+ // Extract roles
427
+ const roles = this.extractRoles(payload);
428
+ return {
429
+ sub: payload.sub || "unknown",
430
+ name: payload.name,
431
+ email: payload.email,
432
+ roles,
433
+ claims: payload,
434
+ provider: this.name,
435
+ issuedAt: payload.iat,
436
+ expiresAt: payload.exp,
437
+ };
438
+ }
439
+ // -----------------------------------------------------------------------
440
+ // Signature verification
441
+ // -----------------------------------------------------------------------
442
+ /**
443
+ * Verify a JWT signature using the given algorithm and public key.
444
+ *
445
+ * @param alg - JWT algorithm (RS256 or ES256)
446
+ * @param key - Node.js KeyObject containing the public key
447
+ * @param signingInput - The `header.payload` string that was signed
448
+ * @param signature - Raw signature bytes
449
+ * @returns `true` if the signature is valid
450
+ */
451
+ verifySignature(alg, key, signingInput, signature) {
452
+ switch (alg) {
453
+ case "RS256": {
454
+ const verifier = createVerify("RSA-SHA256");
455
+ verifier.update(signingInput);
456
+ return verifier.verify(key, signature);
457
+ }
458
+ case "ES256": {
459
+ // ECDSA signatures in JWTs use the raw (r || s) format.
460
+ // Node.js crypto expects DER-encoded signatures, so we convert.
461
+ const derSignature = this.rawEcdsaToDer(signature);
462
+ const verifier = createVerify("SHA256");
463
+ verifier.update(signingInput);
464
+ return verifier.verify(key, derSignature);
465
+ }
466
+ default:
467
+ return false;
468
+ }
469
+ }
470
+ /**
471
+ * Convert a raw ECDSA signature (r || s, 64 bytes for P-256) to
472
+ * DER-encoded format expected by Node.js crypto.
473
+ *
474
+ * @param raw - Raw ECDSA signature bytes
475
+ * @returns DER-encoded signature buffer
476
+ */
477
+ rawEcdsaToDer(raw) {
478
+ const halfLen = raw.length / 2;
479
+ const r = raw.subarray(0, halfLen);
480
+ const s = raw.subarray(halfLen);
481
+ const encodeInteger = (int) => {
482
+ // Trim leading zeros but keep one if highest bit is set
483
+ let offset = 0;
484
+ while (offset < int.length - 1 && int[offset] === 0) {
485
+ offset++;
486
+ }
487
+ let trimmed = int.subarray(offset);
488
+ // If the high bit is set, prepend a 0x00 byte
489
+ if (trimmed[0] & 0x80) {
490
+ trimmed = Buffer.concat([Buffer.from([0x00]), trimmed]);
491
+ }
492
+ return Buffer.concat([Buffer.from([0x02, trimmed.length]), trimmed]);
493
+ };
494
+ const rDer = encodeInteger(r);
495
+ const sDer = encodeInteger(s);
496
+ return Buffer.concat([Buffer.from([0x30, rDer.length + sDer.length]), rDer, sDer]);
497
+ }
498
+ // -----------------------------------------------------------------------
499
+ // Key resolution
500
+ // -----------------------------------------------------------------------
501
+ /**
502
+ * Resolve a Node.js KeyObject for the given algorithm and key ID.
503
+ *
504
+ * Performs OIDC discovery and JWKS fetch as needed, then selects the
505
+ * appropriate JWK and imports it into a native KeyObject.
506
+ */
507
+ async resolvePublicKey(alg, kid) {
508
+ // Check KeyObject cache
509
+ const cacheKey = kid ?? alg;
510
+ const cached = this.keyObjectCache.get(cacheKey);
511
+ if (cached) {
512
+ return cached;
513
+ }
514
+ // Determine JWKS URI
515
+ const jwksUri = await this.resolveJwksUri();
516
+ if (!jwksUri) {
517
+ return null;
518
+ }
519
+ // Fetch JWKS
520
+ const jwks = await this.fetchJWKS(jwksUri);
521
+ // Find matching key
522
+ const jwk = this.selectKey(jwks, alg, kid);
523
+ if (!jwk) {
524
+ // If kid not found and we are using cache, try refreshing JWKS
525
+ // (key rotation scenario)
526
+ if (this.config.cacheJWKS && this.jwksCache) {
527
+ this.jwksCache = null;
528
+ this.keyObjectCache.clear();
529
+ const refreshed = await this.fetchJWKS(jwksUri);
530
+ const retryJwk = this.selectKey(refreshed, alg, kid);
531
+ if (!retryJwk) {
532
+ return null;
533
+ }
534
+ return this.importJWK(retryJwk);
535
+ }
536
+ return null;
537
+ }
538
+ const keyObject = this.importJWK(jwk);
539
+ if (keyObject) {
540
+ this.keyObjectCache.set(cacheKey, keyObject);
541
+ }
542
+ return keyObject;
543
+ }
544
+ /**
545
+ * Select a JWK from the key set that matches the algorithm and key ID.
546
+ */
547
+ selectKey(jwks, alg, kid) {
548
+ const candidates = jwks.keys.filter((k) => {
549
+ // Key must be for signature verification
550
+ if (k.use && k.use !== "sig")
551
+ return false;
552
+ // If alg is specified on the key, it must match
553
+ if (k.alg && k.alg !== alg)
554
+ return false;
555
+ // Key type must match algorithm
556
+ if (alg === "RS256" && k.kty !== "RSA")
557
+ return false;
558
+ if (alg === "ES256" && k.kty !== "EC")
559
+ return false;
560
+ return true;
561
+ });
562
+ if (candidates.length === 0) {
563
+ return null;
564
+ }
565
+ // If kid is provided, find an exact match
566
+ if (kid) {
567
+ return candidates.find((k) => k.kid === kid) ?? null;
568
+ }
569
+ // Without kid, return the first matching candidate
570
+ return candidates[0];
571
+ }
572
+ /**
573
+ * Import a JWK into a Node.js KeyObject using `crypto.createPublicKey`.
574
+ */
575
+ importJWK(jwk) {
576
+ try {
577
+ if (jwk.kty === "RSA") {
578
+ return createPublicKey({
579
+ key: {
580
+ kty: jwk.kty,
581
+ n: jwk.n,
582
+ e: jwk.e,
583
+ },
584
+ format: "jwk",
585
+ });
586
+ }
587
+ if (jwk.kty === "EC") {
588
+ return createPublicKey({
589
+ key: {
590
+ kty: jwk.kty,
591
+ crv: jwk.crv,
592
+ x: jwk.x,
593
+ y: jwk.y,
594
+ },
595
+ format: "jwk",
596
+ });
597
+ }
598
+ return null;
599
+ }
600
+ catch {
601
+ return null;
602
+ }
603
+ }
604
+ // -----------------------------------------------------------------------
605
+ // Claims validation
606
+ // -----------------------------------------------------------------------
607
+ /**
608
+ * Validate standard JWT claims: exp, nbf, iss, aud.
609
+ *
610
+ * @returns An error message string if validation fails, `null` otherwise
611
+ */
612
+ validateClaims(payload) {
613
+ const now = Math.floor(Date.now() / 1000);
614
+ const tolerance = this.config.clockToleranceSec;
615
+ // Check expiration
616
+ if (payload.exp !== undefined && payload.exp + tolerance < now) {
617
+ return "Token expired";
618
+ }
619
+ // Check not-before
620
+ if (payload.nbf !== undefined && payload.nbf - tolerance > now) {
621
+ return "Token not yet valid";
622
+ }
623
+ // Check issuer
624
+ if (payload.iss && payload.iss !== this.config.issuerUrl) {
625
+ return `Invalid issuer: expected ${this.config.issuerUrl}, got ${payload.iss}`;
626
+ }
627
+ // Check audience
628
+ if (this.config.audience) {
629
+ const aud = Array.isArray(payload.aud) ? payload.aud : payload.aud ? [payload.aud] : [];
630
+ if (!aud.includes(this.config.audience)) {
631
+ return `Invalid audience: expected ${this.config.audience}`;
632
+ }
633
+ }
634
+ return null;
635
+ }
636
+ // -----------------------------------------------------------------------
637
+ // Role extraction
638
+ // -----------------------------------------------------------------------
639
+ /**
640
+ * Extract roles from token claims using the configured claim names.
641
+ *
642
+ * Checks the `rolesClaim` first (e.g. "roles"), then falls back
643
+ * to parsing space-delimited scopes from `scopesClaim` (e.g. "scope").
644
+ */
645
+ extractRoles(claims) {
646
+ const roles = [];
647
+ // Extract from roles claim
648
+ const rolesClaim = this.config.rolesClaim;
649
+ const rolesValue = claims[rolesClaim];
650
+ if (Array.isArray(rolesValue)) {
651
+ roles.push(...rolesValue);
652
+ }
653
+ else if (typeof rolesValue === "string") {
654
+ roles.push(rolesValue);
655
+ }
656
+ // Extract from scopes claim (space-delimited string)
657
+ const scopesClaim = this.config.scopesClaim;
658
+ const scopesValue = claims[scopesClaim];
659
+ if (typeof scopesValue === "string" && scopesValue.length > 0) {
660
+ const scopeRoles = scopesValue.split(/\s+/).filter(Boolean);
661
+ for (const s of scopeRoles) {
662
+ if (!roles.includes(s)) {
663
+ roles.push(s);
664
+ }
665
+ }
666
+ }
667
+ return roles;
668
+ }
669
+ // -----------------------------------------------------------------------
670
+ // URI resolution helpers
671
+ // -----------------------------------------------------------------------
672
+ /**
673
+ * Resolve the JWKS URI, either from explicit config or via OIDC discovery
674
+ */
675
+ async resolveJwksUri() {
676
+ if (this.config.jwksUri) {
677
+ return this.config.jwksUri;
678
+ }
679
+ try {
680
+ const doc = await this.discoverConfiguration(this.config.issuerUrl);
681
+ return doc.jwks_uri;
682
+ }
683
+ catch {
684
+ return null;
685
+ }
686
+ }
687
+ /**
688
+ * Resolve the token introspection endpoint from OIDC discovery
689
+ */
690
+ async resolveIntrospectionEndpoint() {
691
+ try {
692
+ const doc = await this.discoverConfiguration(this.config.issuerUrl);
693
+ return doc.introspection_endpoint ?? null;
694
+ }
695
+ catch {
696
+ return null;
697
+ }
698
+ }
699
+ // -----------------------------------------------------------------------
700
+ // Cache management (public API for operational use)
701
+ // -----------------------------------------------------------------------
702
+ /**
703
+ * Clear all internal caches (discovery, JWKS, token cache).
704
+ * Useful during key rotation or configuration changes.
705
+ */
706
+ clearCaches() {
707
+ this.discoveryCache = null;
708
+ this.jwksCache = null;
709
+ this.keyObjectCache.clear();
710
+ this.tokenCache.clear();
711
+ }
712
+ /**
713
+ * Return statistics from the token cache
714
+ */
715
+ getTokenCacheStats() {
716
+ return this.tokenCache.getStats();
717
+ }
718
+ }
719
+ //# sourceMappingURL=OAuthProvider.js.map