@naylence/runtime 0.3.5-test.910 → 0.3.5-test.913

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 (47) hide show
  1. package/dist/browser/index.cjs +1915 -1214
  2. package/dist/browser/index.mjs +1910 -1209
  3. package/dist/cjs/naylence/fame/config/extended-fame-config.js +52 -0
  4. package/dist/cjs/naylence/fame/factory-manifest.js +2 -0
  5. package/dist/cjs/naylence/fame/http/jwks-api-router.js +16 -18
  6. package/dist/cjs/naylence/fame/http/oauth2-server.js +28 -31
  7. package/dist/cjs/naylence/fame/http/oauth2-token-router.js +901 -93
  8. package/dist/cjs/naylence/fame/http/openid-configuration-router.js +30 -32
  9. package/dist/cjs/naylence/fame/node/admission/admission-profile-factory.js +79 -0
  10. package/dist/cjs/naylence/fame/security/auth/oauth2-pkce-token-provider-factory.js +171 -0
  11. package/dist/cjs/naylence/fame/security/auth/oauth2-pkce-token-provider.js +560 -0
  12. package/dist/cjs/naylence/fame/security/crypto/providers/default-crypto-provider.js +0 -162
  13. package/dist/cjs/naylence/fame/telemetry/open-telemetry-trace-emitter-factory.js +19 -2
  14. package/dist/cjs/naylence/fame/telemetry/open-telemetry-trace-emitter.js +19 -9
  15. package/dist/cjs/naylence/fame/util/register-runtime-factories.js +6 -0
  16. package/dist/cjs/version.js +2 -2
  17. package/dist/esm/naylence/fame/config/extended-fame-config.js +52 -0
  18. package/dist/esm/naylence/fame/factory-manifest.js +2 -0
  19. package/dist/esm/naylence/fame/http/jwks-api-router.js +16 -17
  20. package/dist/esm/naylence/fame/http/oauth2-server.js +28 -31
  21. package/dist/esm/naylence/fame/http/oauth2-token-router.js +901 -93
  22. package/dist/esm/naylence/fame/http/openid-configuration-router.js +30 -31
  23. package/dist/esm/naylence/fame/node/admission/admission-profile-factory.js +79 -0
  24. package/dist/esm/naylence/fame/security/auth/oauth2-pkce-token-provider-factory.js +134 -0
  25. package/dist/esm/naylence/fame/security/auth/oauth2-pkce-token-provider.js +555 -0
  26. package/dist/esm/naylence/fame/security/crypto/providers/default-crypto-provider.js +0 -162
  27. package/dist/esm/naylence/fame/telemetry/open-telemetry-trace-emitter-factory.js +19 -2
  28. package/dist/esm/naylence/fame/telemetry/open-telemetry-trace-emitter.js +19 -9
  29. package/dist/esm/naylence/fame/util/register-runtime-factories.js +6 -0
  30. package/dist/esm/version.js +2 -2
  31. package/dist/node/index.cjs +1911 -1210
  32. package/dist/node/index.mjs +1910 -1209
  33. package/dist/node/node.cjs +2945 -1439
  34. package/dist/node/node.mjs +2944 -1438
  35. package/dist/types/naylence/fame/factory-manifest.d.ts +1 -1
  36. package/dist/types/naylence/fame/http/jwks-api-router.d.ts +8 -8
  37. package/dist/types/naylence/fame/http/oauth2-server.d.ts +3 -3
  38. package/dist/types/naylence/fame/http/oauth2-token-router.d.ts +75 -19
  39. package/dist/types/naylence/fame/http/openid-configuration-router.d.ts +8 -8
  40. package/dist/types/naylence/fame/security/auth/oauth2-pkce-token-provider-factory.d.ts +27 -0
  41. package/dist/types/naylence/fame/security/auth/oauth2-pkce-token-provider.d.ts +42 -0
  42. package/dist/types/naylence/fame/security/crypto/providers/default-crypto-provider.d.ts +0 -1
  43. package/dist/types/naylence/fame/telemetry/open-telemetry-trace-emitter.d.ts +4 -0
  44. package/dist/types/version.d.ts +1 -1
  45. package/package.json +4 -4
  46. package/dist/esm/naylence/fame/fastapi/oauth2-server.js +0 -205
  47. package/dist/types/naylence/fame/fastapi/oauth2-server.d.ts +0 -22
@@ -34,9 +34,6 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.DefaultCryptoProvider = void 0;
37
- const asn1_schema_1 = require("@peculiar/asn1-schema");
38
- const asn1_x509_1 = require("@peculiar/asn1-x509");
39
- const asn1_csr_1 = require("@peculiar/asn1-csr");
40
37
  const core_1 = require("@naylence/core");
41
38
  const logging_js_1 = require("../../../util/logging.js");
42
39
  const util_js_1 = require("../../../util/util.js");
@@ -52,11 +49,6 @@ const DEFAULT_AUDIENCE = 'router-dev';
52
49
  const DEFAULT_TTL_SEC = 3600;
53
50
  const DEFAULT_HMAC_SECRET_BYTES = 32;
54
51
  const ENCRYPTION_ALG = 'ECDH-ES';
55
- const EXTENSION_REQUEST_OID = '1.2.840.113549.1.9.14';
56
- const COMMON_NAME_OID = '2.5.4.3';
57
- const ED25519_OID = '1.3.101.112';
58
- const CSR_PEM_TAG = 'CERTIFICATE REQUEST';
59
- const LOGICAL_URI_PREFIX = 'naylence://';
60
52
  function normalizeDefaultCryptoProviderOptions(options) {
61
53
  if (!options) {
62
54
  return {};
@@ -322,76 +314,6 @@ class DefaultCryptoProvider {
322
314
  has_chain: Boolean(certificateChainPem),
323
315
  });
324
316
  }
325
- async createCsr(nodeId, physicalPath, logicals, subjectName) {
326
- const trimmedNodeId = assertNonEmptyString(nodeId, 'nodeId');
327
- const trimmedPhysicalPath = assertNonEmptyString(physicalPath, 'physicalPath');
328
- try {
329
- if (this.artifacts.signing.algorithm !== 'EdDSA') {
330
- throw new Error('CSR creation only supported for Ed25519 signing keys in the default crypto provider');
331
- }
332
- const cryptoImpl = await ensureWebCrypto();
333
- const privateKey = await cryptoImpl.subtle.importKey('pkcs8', pemToArrayBuffer(this.signingPrivatePem), {
334
- name: 'Ed25519',
335
- }, false, ['sign']);
336
- const publicKeyDer = pemToArrayBuffer(this.signingPublicPem);
337
- const subjectPkInfo = asn1_schema_1.AsnConvert.parse(publicKeyDer, asn1_x509_1.SubjectPublicKeyInfo);
338
- const sanitizedLogicals = Array.isArray(logicals)
339
- ? logicals.filter((value) => typeof value === 'string' && value.trim().length > 0)
340
- : [];
341
- const commonName = typeof subjectName === 'string' && subjectName.trim().length > 0
342
- ? subjectName.trim()
343
- : trimmedNodeId;
344
- const subject = buildSubjectName(commonName);
345
- const attributes = new asn1_csr_1.Attributes();
346
- if (sanitizedLogicals.length > 0) {
347
- const san = new asn1_x509_1.SubjectAlternativeName(sanitizedLogicals.map((logical) => new asn1_x509_1.GeneralName({
348
- uniformResourceIdentifier: `${LOGICAL_URI_PREFIX}${logical}`,
349
- })));
350
- const extensions = new asn1_x509_1.Extensions([
351
- new asn1_x509_1.Extension({
352
- extnID: asn1_x509_1.id_ce_subjectAltName,
353
- critical: false,
354
- extnValue: new asn1_schema_1.OctetString(asn1_schema_1.AsnConvert.serialize(san)),
355
- }),
356
- ]);
357
- attributes.push(new asn1_x509_1.Attribute({
358
- type: EXTENSION_REQUEST_OID,
359
- values: [asn1_schema_1.AsnConvert.serialize(extensions)],
360
- }));
361
- }
362
- const requestInfo = new asn1_csr_1.CertificationRequestInfo({
363
- subject,
364
- subjectPKInfo: subjectPkInfo,
365
- attributes,
366
- });
367
- const requestInfoDer = asn1_schema_1.AsnConvert.serialize(requestInfo);
368
- const signature = await cryptoImpl.subtle.sign('Ed25519', privateKey, requestInfoDer);
369
- const certificationRequest = new asn1_csr_1.CertificationRequest({
370
- certificationRequestInfo: requestInfo,
371
- signatureAlgorithm: new asn1_x509_1.AlgorithmIdentifier({
372
- algorithm: ED25519_OID,
373
- }),
374
- signature: encodeBitString(signature),
375
- });
376
- certificationRequest.certificationRequestInfoRaw = requestInfoDer;
377
- const csrDer = asn1_schema_1.AsnConvert.serialize(certificationRequest);
378
- const csrPem = arrayBufferToPem(csrDer, CSR_PEM_TAG);
379
- logger.debug('csr_created', {
380
- node_id: trimmedNodeId,
381
- physical_path: trimmedPhysicalPath,
382
- logical_count: sanitizedLogicals.length,
383
- });
384
- return csrPem;
385
- }
386
- catch (error) {
387
- logger.error('csr_creation_failed', {
388
- node_id: trimmedNodeId,
389
- physical_path: trimmedPhysicalPath,
390
- error: error instanceof Error ? error.message : String(error),
391
- });
392
- throw error;
393
- }
394
- }
395
317
  }
396
318
  exports.DefaultCryptoProvider = DefaultCryptoProvider;
397
319
  async function buildProviderArtifacts(options) {
@@ -628,90 +550,6 @@ function pemToDerBase64(pem) {
628
550
  // Ensure the output is valid base64 without whitespace
629
551
  return base64.replace(/\s+/g, '');
630
552
  }
631
- let cryptoPromise = null;
632
- async function ensureWebCrypto() {
633
- if (typeof globalThis.crypto !== 'undefined' && globalThis.crypto?.subtle) {
634
- return globalThis.crypto;
635
- }
636
- if (!cryptoPromise) {
637
- if (typeof process !== 'undefined' &&
638
- typeof process.versions?.node === 'string') {
639
- cryptoPromise = Promise.resolve().then(() => __importStar(require('node:crypto'))).then((module) => {
640
- const webcrypto = module.webcrypto;
641
- if (!webcrypto || !webcrypto.subtle) {
642
- throw new Error('WebCrypto API is not available in this Node.js runtime');
643
- }
644
- globalThis.crypto = webcrypto;
645
- return webcrypto;
646
- });
647
- }
648
- else {
649
- cryptoPromise = Promise.reject(new Error('WebCrypto API is not available in this environment'));
650
- }
651
- }
652
- return cryptoPromise;
653
- }
654
- function pemToArrayBuffer(pem) {
655
- const normalized = pem
656
- .replace(/-----BEGIN[^-]+-----/g, '')
657
- .replace(/-----END[^-]+-----/g, '')
658
- .replace(/\s+/g, '');
659
- const bytes = base64ToBytes(normalized);
660
- return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
661
- }
662
- function base64ToBytes(base64) {
663
- if (typeof Buffer !== 'undefined') {
664
- const buffer = Buffer.from(base64, 'base64');
665
- const bytes = new Uint8Array(buffer.length);
666
- for (let i = 0; i < buffer.length; i += 1) {
667
- bytes[i] = buffer[i];
668
- }
669
- return bytes;
670
- }
671
- if (typeof atob === 'function') {
672
- const binary = atob(base64);
673
- const bytes = new Uint8Array(binary.length);
674
- for (let i = 0; i < binary.length; i += 1) {
675
- bytes[i] = binary.charCodeAt(i);
676
- }
677
- return bytes;
678
- }
679
- throw new Error('No base64 decoder available in this environment');
680
- }
681
- function arrayBufferToPem(buffer, tag) {
682
- const base64 = bytesToBase64(new Uint8Array(buffer));
683
- return `-----BEGIN ${tag}-----\n${formatPem(base64)}\n-----END ${tag}-----\n`;
684
- }
685
- function formatPem(base64) {
686
- const lines = [];
687
- for (let i = 0; i < base64.length; i += 64) {
688
- lines.push(base64.slice(i, i + 64));
689
- }
690
- return lines.join('\n');
691
- }
692
- function encodeBitString(signature) {
693
- const bytes = new Uint8Array(signature);
694
- const bitString = new Uint8Array(bytes.length + 1);
695
- bitString.set(bytes, 1);
696
- return bitString.buffer;
697
- }
698
- function buildSubjectName(commonName) {
699
- const attribute = new asn1_x509_1.AttributeTypeAndValue({
700
- type: COMMON_NAME_OID,
701
- value: new asn1_x509_1.AttributeValue({ utf8String: commonName }),
702
- });
703
- return new asn1_x509_1.Name([new asn1_x509_1.RelativeDistinguishedName([attribute])]);
704
- }
705
- function assertNonEmptyString(value, name) {
706
- if (typeof value !== 'string') {
707
- throw new TypeError(`${name} must be a string`);
708
- }
709
- const trimmed = value.trim();
710
- if (trimmed.length === 0) {
711
- throw new TypeError(`${name} must be a non-empty string`);
712
- }
713
- return trimmed;
714
- }
715
553
  function cloneJson(value) {
716
554
  return JSON.parse(JSON.stringify(value));
717
555
  }
@@ -40,15 +40,25 @@ const lazy_import_js_1 = require("../util/lazy-import.js");
40
40
  const trace_emitter_factory_js_1 = require("./trace-emitter-factory.js");
41
41
  const logging_js_1 = require("../util/logging.js");
42
42
  let openTelemetryTraceEmitterModulePromise = null;
43
+ let otelApiModulePromise = null;
43
44
  const logger = (0, logging_js_1.getLogger)('naylence.fame.telemetry.open_telemetry_trace_emitter_factory');
45
+ const MISSING_OTEL_HELP_MESSAGE = 'Missing optional OpenTelemetry dependency. Install @opentelemetry/api (and related packages) to enable trace emission.';
44
46
  function getOpenTelemetryTraceEmitterModule() {
45
47
  if (!openTelemetryTraceEmitterModulePromise) {
46
48
  openTelemetryTraceEmitterModulePromise = (0, lazy_import_js_1.safeImport)(() => Promise.resolve().then(() => __importStar(require('./open-telemetry-trace-emitter.js'))), '@opentelemetry/api', {
47
- helpMessage: 'Missing optional OpenTelemetry dependency. Install @opentelemetry/api (and related packages) to enable trace emission.',
49
+ helpMessage: MISSING_OTEL_HELP_MESSAGE,
48
50
  });
49
51
  }
50
52
  return openTelemetryTraceEmitterModulePromise;
51
53
  }
54
+ function getOtelApiModule() {
55
+ if (!otelApiModulePromise) {
56
+ otelApiModulePromise = (0, lazy_import_js_1.safeImport)(() => Promise.resolve().then(() => __importStar(require('@opentelemetry/api'))), '@opentelemetry/api', {
57
+ helpMessage: MISSING_OTEL_HELP_MESSAGE,
58
+ });
59
+ }
60
+ return otelApiModulePromise;
61
+ }
52
62
  exports.FACTORY_META = {
53
63
  base: trace_emitter_factory_js_1.TRACE_EMITTER_FACTORY_BASE_TYPE,
54
64
  key: 'OpenTelemetryTraceEmitter',
@@ -111,9 +121,16 @@ class OpenTelemetryTraceEmitterFactory extends trace_emitter_factory_js_1.TraceE
111
121
  }
112
122
  throw error;
113
123
  }
114
- const { OpenTelemetryTraceEmitter } = await getOpenTelemetryTraceEmitterModule();
124
+ const [{ OpenTelemetryTraceEmitter }, otelModule] = await Promise.all([
125
+ getOpenTelemetryTraceEmitterModule(),
126
+ getOtelApiModule(),
127
+ ]);
115
128
  const emitterOptions = {
116
129
  serviceName: normalized.serviceName,
130
+ otelApi: {
131
+ trace: otelModule.trace,
132
+ SpanStatusCode: otelModule.SpanStatusCode,
133
+ },
117
134
  };
118
135
  if (options.tracer) {
119
136
  emitterOptions.tracer = options.tracer;
@@ -1,12 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.OpenTelemetryTraceEmitter = void 0;
4
- const api_1 = require("@opentelemetry/api");
5
4
  const base_trace_emitter_js_1 = require("./base-trace-emitter.js");
6
5
  const otel_context_js_1 = require("./otel-context.js");
7
6
  class OpenTelemetryTraceSpan {
8
- constructor(span) {
7
+ constructor(span, api) {
9
8
  this.span = span;
9
+ this.api = api;
10
10
  }
11
11
  setAttribute(key, value) {
12
12
  try {
@@ -32,7 +32,7 @@ class OpenTelemetryTraceSpan {
32
32
  setStatusError(description) {
33
33
  try {
34
34
  const status = {
35
- code: api_1.SpanStatusCode.ERROR,
35
+ code: this.api.SpanStatusCode.ERROR,
36
36
  };
37
37
  if (description !== undefined) {
38
38
  status.message = description;
@@ -45,10 +45,10 @@ class OpenTelemetryTraceSpan {
45
45
  }
46
46
  }
47
47
  class OpenTelemetrySpanScope {
48
- constructor(span) {
48
+ constructor(span, api) {
49
49
  this.span = span;
50
50
  this.entered = false;
51
- this.wrapper = new OpenTelemetryTraceSpan(span);
51
+ this.wrapper = new OpenTelemetryTraceSpan(span, api);
52
52
  }
53
53
  enter() {
54
54
  if (!this.entered) {
@@ -83,7 +83,9 @@ class OpenTelemetryTraceEmitter extends base_trace_emitter_js_1.BaseTraceEmitter
83
83
  super();
84
84
  this.shutdownInvoked = false;
85
85
  const normalized = normalizeOpenTelemetryTraceEmitterOptions(options);
86
- this.tracer = normalized.tracer ?? api_1.trace.getTracer(normalized.serviceName);
86
+ this.otelApi = normalized.otelApi;
87
+ this.tracer =
88
+ normalized.tracer ?? this.otelApi.trace.getTracer(normalized.serviceName);
87
89
  this.lifecycle = normalized.lifecycle ?? null;
88
90
  this.authStrategy = normalized.authStrategy ?? null;
89
91
  }
@@ -101,7 +103,7 @@ class OpenTelemetryTraceEmitter extends base_trace_emitter_js_1.BaseTraceEmitter
101
103
  if (typeof envelopeTraceId === 'string') {
102
104
  this.applyEnvelopeTraceId(span, envelopeTraceId);
103
105
  }
104
- return new OpenTelemetrySpanScope(span);
106
+ return new OpenTelemetrySpanScope(span, this.otelApi);
105
107
  }
106
108
  async flush() {
107
109
  if (this.lifecycle?.forceFlush) {
@@ -114,7 +116,7 @@ class OpenTelemetryTraceEmitter extends base_trace_emitter_js_1.BaseTraceEmitter
114
116
  }
115
117
  }
116
118
  try {
117
- const provider = api_1.trace.getTracerProvider();
119
+ const provider = this.otelApi.trace.getTracerProvider();
118
120
  if (provider && typeof provider.forceFlush === 'function') {
119
121
  await provider.forceFlush();
120
122
  }
@@ -149,7 +151,7 @@ class OpenTelemetryTraceEmitter extends base_trace_emitter_js_1.BaseTraceEmitter
149
151
  }
150
152
  }
151
153
  try {
152
- const provider = api_1.trace.getTracerProvider();
154
+ const provider = this.otelApi.trace.getTracerProvider();
153
155
  if (provider && typeof provider.shutdown === 'function') {
154
156
  await provider.shutdown();
155
157
  }
@@ -191,6 +193,13 @@ function normalizeOpenTelemetryTraceEmitterOptions(input) {
191
193
  const source = (input ?? {});
192
194
  const serviceName = extractNonEmptyString(pickFirst(source, ['serviceName', 'service_name'])) ?? 'naylence-service';
193
195
  const tracer = pickFirst(source, ['tracer']);
196
+ const otelApi = pickFirst(source, [
197
+ 'otelApi',
198
+ 'otel_api',
199
+ ]);
200
+ if (!otelApi) {
201
+ throw new Error('OpenTelemetryTraceEmitter requires OpenTelemetry API bindings. Provide otelApi via options.');
202
+ }
194
203
  const lifecycle = pickFirst(source, [
195
204
  'lifecycle',
196
205
  'lifeCycle',
@@ -203,6 +212,7 @@ function normalizeOpenTelemetryTraceEmitterOptions(input) {
203
212
  return {
204
213
  serviceName,
205
214
  tracer,
215
+ otelApi,
206
216
  lifecycle,
207
217
  authStrategy,
208
218
  };
@@ -46,6 +46,9 @@ const NODE_ONLY_FACTORY_MODULES = new Set([
46
46
  './telemetry/open-telemetry-trace-emitter-factory.js',
47
47
  './security/credential/prompt-credential-provider-factory.js',
48
48
  ]);
49
+ const BROWSER_ONLY_FACTORY_MODULES = new Set([
50
+ './security/auth/oauth2-pkce-token-provider-factory.js',
51
+ ]);
49
52
  const isNodeEnvironment = typeof process !== 'undefined' && Boolean(process?.versions?.node);
50
53
  function detectModuleUrl() {
51
54
  // Prefer Node-friendly __filename when available.
@@ -188,6 +191,9 @@ async function performRegistration(registry) {
188
191
  if (!isNodeEnvironment && NODE_ONLY_FACTORY_MODULES.has(spec)) {
189
192
  return;
190
193
  }
194
+ if (isNodeEnvironment && BROWSER_ONLY_FACTORY_MODULES.has(spec)) {
195
+ return;
196
+ }
191
197
  try {
192
198
  let mod;
193
199
  let lastError;
@@ -1,10 +1,10 @@
1
1
  "use strict";
2
2
  // This file is auto-generated during build - do not edit manually
3
- // Generated from package.json version: 0.3.5-test.910
3
+ // Generated from package.json version: 0.3.5-test.913
4
4
  Object.defineProperty(exports, "__esModule", { value: true });
5
5
  exports.VERSION = void 0;
6
6
  /**
7
7
  * The package version, injected at build time.
8
8
  * @internal
9
9
  */
10
- exports.VERSION = '0.3.5-test.910';
10
+ exports.VERSION = '0.3.5-test.913';
@@ -14,6 +14,57 @@ const CONFIG_SEARCH_PATHS = [
14
14
  ];
15
15
  const fsModuleSpecifier = String.fromCharCode(102) + String.fromCharCode(115);
16
16
  let cachedFsModule = null;
17
+ // Capture this module's URL without triggering TypeScript's import.meta restriction on CJS builds
18
+ const currentModuleUrl = (() => {
19
+ try {
20
+ return (0, eval)('import.meta.url');
21
+ }
22
+ catch {
23
+ return undefined;
24
+ }
25
+ })();
26
+ // Shared flag that allows synchronous waiting for the Node-specific require shim
27
+ const requireReadyFlag = isNode && typeof SharedArrayBuffer !== 'undefined'
28
+ ? new Int32Array(new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT))
29
+ : null;
30
+ if (requireReadyFlag) {
31
+ // 0 means initializing, 1 means ready (success or failure)
32
+ Atomics.store(requireReadyFlag, 0, 0);
33
+ // Prepare a CommonJS-style require when running in pure ESM contexts
34
+ void (async () => {
35
+ try {
36
+ if (typeof require !== 'function') {
37
+ const moduleNamespace = (await import('node:module'));
38
+ const createRequire = moduleNamespace.createRequire;
39
+ if (typeof createRequire === 'function') {
40
+ const fallbackPath = `${process.cwd()}/.__naylence_require_shim__.mjs`;
41
+ const nodeRequire = createRequire(currentModuleUrl ?? fallbackPath);
42
+ globalThis.require = nodeRequire;
43
+ }
44
+ }
45
+ }
46
+ catch {
47
+ // Ignore failures – getFsModule will surface a helpful error when needed
48
+ }
49
+ })()
50
+ .catch(() => {
51
+ // Ignore async errors – the ready flag will still unblock consumers
52
+ })
53
+ .finally(() => {
54
+ Atomics.store(requireReadyFlag, 0, 1);
55
+ Atomics.notify(requireReadyFlag, 0);
56
+ });
57
+ }
58
+ function ensureRequireReady() {
59
+ if (!requireReadyFlag) {
60
+ return;
61
+ }
62
+ if (Atomics.load(requireReadyFlag, 0) === 1) {
63
+ return;
64
+ }
65
+ // Block until the asynchronous loader finishes initialising
66
+ Atomics.wait(requireReadyFlag, 0, 0);
67
+ }
17
68
  function getFsModule() {
18
69
  if (cachedFsModule) {
19
70
  return cachedFsModule;
@@ -21,6 +72,7 @@ function getFsModule() {
21
72
  if (!isNode) {
22
73
  throw new Error('File system access is not available in this environment');
23
74
  }
75
+ ensureRequireReady();
24
76
  if (typeof require === 'function') {
25
77
  try {
26
78
  cachedFsModule = require(fsModuleSpecifier);
@@ -35,6 +35,7 @@ export const MODULES = [
35
35
  "./security/auth/noop-token-verifier-factory.js",
36
36
  "./security/auth/oauth2-authorizer-factory.js",
37
37
  "./security/auth/oauth2-client-credentials-token-provider-factory.js",
38
+ "./security/auth/oauth2-pkce-token-provider-factory.js",
38
39
  "./security/auth/query-param-auth-injection-strategy-factory.js",
39
40
  "./security/auth/shared-secret-authorizer-factory.js",
40
41
  "./security/auth/shared-secret-token-provider-factory.js",
@@ -108,6 +109,7 @@ export const MODULE_LOADERS = {
108
109
  "./security/auth/noop-token-verifier-factory.js": () => import("./security/auth/noop-token-verifier-factory.js"),
109
110
  "./security/auth/oauth2-authorizer-factory.js": () => import("./security/auth/oauth2-authorizer-factory.js"),
110
111
  "./security/auth/oauth2-client-credentials-token-provider-factory.js": () => import("./security/auth/oauth2-client-credentials-token-provider-factory.js"),
112
+ "./security/auth/oauth2-pkce-token-provider-factory.js": () => import("./security/auth/oauth2-pkce-token-provider-factory.js"),
111
113
  "./security/auth/query-param-auth-injection-strategy-factory.js": () => import("./security/auth/query-param-auth-injection-strategy-factory.js"),
112
114
  "./security/auth/shared-secret-authorizer-factory.js": () => import("./security/auth/shared-secret-authorizer-factory.js"),
113
115
  "./security/auth/shared-secret-token-provider-factory.js": () => import("./security/auth/shared-secret-token-provider-factory.js"),
@@ -1,10 +1,9 @@
1
1
  /**
2
- * JWKS (JSON Web Key Set) API router for Express
2
+ * JWKS (JSON Web Key Set) API plugin for Fastify
3
3
  *
4
4
  * Provides /.well-known/jwks.json endpoint for public key discovery
5
5
  * Used by OAuth2/JWT token verification
6
6
  */
7
- import express from 'express';
8
7
  import { getLogger } from '../util/logging.js';
9
8
  const logger = getLogger('naylence.fame.http.jwks_api_router');
10
9
  const DEFAULT_PREFIX = '';
@@ -84,23 +83,22 @@ function filterKeysByType(jwksData, allowedTypes) {
84
83
  return { ...jwksData, keys: filteredKeys };
85
84
  }
86
85
  /**
87
- * Create an Express router that exposes JWKS at /.well-known/jwks.json
86
+ * Create a Fastify plugin that exposes JWKS at /.well-known/jwks.json
88
87
  *
89
88
  * @param options - Router configuration options
90
- * @returns Express router with JWKS endpoint
89
+ * @returns Fastify plugin with JWKS endpoint
91
90
  *
92
91
  * @example
93
92
  * ```typescript
94
- * import express from 'express';
93
+ * import Fastify from 'fastify';
95
94
  * import { createJwksRouter } from '@naylence/runtime';
96
95
  *
97
- * const app = express();
96
+ * const app = Fastify();
98
97
  * const cryptoProvider = new MyCryptoProvider();
99
- * app.use(createJwksRouter({ cryptoProvider }));
98
+ * app.register(createJwksRouter({ cryptoProvider }));
100
99
  * ```
101
100
  */
102
101
  export function createJwksRouter(options = {}) {
103
- const router = express.Router();
104
102
  const { getJwksJson, cryptoProvider, prefix = DEFAULT_PREFIX, keyTypes, } = normalizeCreateJwksRouterOptions(options);
105
103
  // Get JWKS data
106
104
  let jwks;
@@ -123,14 +121,15 @@ export function createJwksRouter(options = {}) {
123
121
  key_types: allowedKeyTypes,
124
122
  total_keys: jwks.keys.length,
125
123
  });
126
- // JWKS endpoint
127
- router.get(`${prefix}/.well-known/jwks.json`, (_req, res) => {
128
- const filteredJwks = filterKeysByType(jwks, allowedKeyTypes);
129
- logger.debug('jwks_served', {
130
- total_keys: jwks.keys.length,
131
- filtered_keys: filteredJwks.keys.length,
124
+ const plugin = async (instance) => {
125
+ instance.get(`${prefix}/.well-known/jwks.json`, async (_request, reply) => {
126
+ const filteredJwks = filterKeysByType(jwks, allowedKeyTypes);
127
+ logger.debug('jwks_served', {
128
+ total_keys: jwks.keys.length,
129
+ filtered_keys: filteredJwks.keys.length,
130
+ });
131
+ reply.send(filteredJwks);
132
132
  });
133
- res.json(filteredJwks);
134
- });
135
- return router;
133
+ };
134
+ return plugin;
136
135
  }
@@ -21,7 +21,7 @@
21
21
  * FAME_JWT_ISSUER: JWT issuer (default: https://auth.fame.fabric)
22
22
  * FAME_JWT_ALGORITHM: JWT algorithm (default: EdDSA)
23
23
  */
24
- import express from 'express';
24
+ import Fastify from 'fastify';
25
25
  import { createOAuth2TokenRouter } from './oauth2-token-router.js';
26
26
  import { createJwksRouter } from './jwks-api-router.js';
27
27
  import { createOpenIDConfigurationRouter } from './openid-configuration-router.js';
@@ -53,23 +53,18 @@ async function getCryptoProvider() {
53
53
  return DefaultCryptoProvider.create();
54
54
  }
55
55
  /**
56
- * Create and configure the OAuth2 Express application
56
+ * Create and configure the OAuth2 Fastify application
57
57
  */
58
58
  export async function createApp() {
59
- const app = express();
60
- // Middleware
61
- app.use(express.json());
62
- app.use(express.urlencoded({ extended: true }));
59
+ const app = Fastify({ logger: false });
63
60
  // Get crypto provider
64
61
  const cryptoProvider = await getCryptoProvider();
65
62
  // Add routers
66
- app.use(createOAuth2TokenRouter({ cryptoProvider }));
67
- app.use(createJwksRouter({ cryptoProvider }));
68
- app.use(createOpenIDConfigurationRouter());
63
+ app.register(createOAuth2TokenRouter({ cryptoProvider }));
64
+ app.register(createJwksRouter({ cryptoProvider }));
65
+ app.register(createOpenIDConfigurationRouter());
69
66
  // Health check endpoint
70
- app.get('/health', (_req, res) => {
71
- res.json({ status: 'ok' });
72
- });
67
+ app.get('/health', async () => ({ status: 'ok' }));
73
68
  return app;
74
69
  }
75
70
  /**
@@ -97,27 +92,29 @@ async function main() {
97
92
  });
98
93
  const app = await createApp();
99
94
  // Start server
100
- app.listen(port, host, () => {
101
- logger.info('oauth2_server_started', {
102
- host,
103
- port,
104
- endpoints: {
105
- token: '/oauth/token',
106
- jwks: '/.well-known/jwks.json',
107
- openid_config: '/.well-known/openid-configuration',
108
- health: '/health',
109
- },
110
- });
95
+ await app.listen({ port, host });
96
+ logger.info('oauth2_server_started', {
97
+ host,
98
+ port,
99
+ endpoints: {
100
+ token: '/oauth/token',
101
+ jwks: '/.well-known/jwks.json',
102
+ openid_config: '/.well-known/openid-configuration',
103
+ health: '/health',
104
+ },
111
105
  });
106
+ const shutdown = (signal) => {
107
+ logger.info('oauth2_server_shutting_down', { signal });
108
+ app
109
+ .close()
110
+ .catch((error) => logger.error('oauth2_server_shutdown_error', {
111
+ error: error instanceof Error ? error.message : String(error),
112
+ }))
113
+ .finally(() => process.exit(0));
114
+ };
112
115
  // Graceful shutdown
113
- process.on('SIGINT', () => {
114
- logger.info('oauth2_server_shutting_down', { signal: 'SIGINT' });
115
- process.exit(0);
116
- });
117
- process.on('SIGTERM', () => {
118
- logger.info('oauth2_server_shutting_down', { signal: 'SIGTERM' });
119
- process.exit(0);
120
- });
116
+ process.on('SIGINT', () => shutdown('SIGINT'));
117
+ process.on('SIGTERM', () => shutdown('SIGTERM'));
121
118
  }
122
119
  // Export main for CLI usage
123
120
  export { main };