@naylence/runtime 0.3.16 → 0.3.18

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.
@@ -515,12 +515,12 @@ async function ensureRuntimeFactoriesRegistered(registry = factory.Registry) {
515
515
  }
516
516
 
517
517
  // This file is auto-generated during build - do not edit manually
518
- // Generated from package.json version: 0.3.16
518
+ // Generated from package.json version: 0.3.18
519
519
  /**
520
520
  * The package version, injected at build time.
521
521
  * @internal
522
522
  */
523
- const VERSION = '0.3.16';
523
+ const VERSION = '0.3.18';
524
524
 
525
525
  let initialized = false;
526
526
  const runtimePlugin = {
@@ -28228,6 +28228,7 @@ const ENV_VAR_DEFAULT_ENCRYPTION_LEVEL = 'FAME_DEFAULT_ENCRYPTION_LEVEL';
28228
28228
  const ENV_VAR_HMAC_SECRET = 'FAME_HMAC_SECRET';
28229
28229
  const ENV_VAR_JWT_REVERSE_AUTH_TRUSTED_ISSUER = 'FAME_JWT_REVERSE_AUTH_TRUSTED_ISSUER';
28230
28230
  const ENV_VAR_JWT_REVERSE_AUTH_AUDIENCE = 'FAME_JWT_REVERSE_AUTH_AUDIENCE';
28231
+ const ENV_VAR_ENFORCE_TOKEN_SUBJECT_NODE_IDENTITY = 'FAME_ENFORCE_TOKEN_SUBJECT_NODE_IDENTITY';
28231
28232
  const PROFILE_NAME_STRICT_OVERLAY = 'strict-overlay';
28232
28233
  const PROFILE_NAME_OVERLAY = 'overlay';
28233
28234
  const PROFILE_NAME_OVERLAY_CALLBACK = 'overlay-callback';
@@ -28463,6 +28464,7 @@ const GATED_PROFILE = {
28463
28464
  max_ttl_sec: 86400,
28464
28465
  algorithm: factory.Expressions.env(ENV_VAR_JWT_ALGORITHM, 'RS256'),
28465
28466
  audience: factory.Expressions.env(ENV_VAR_JWT_AUDIENCE$1),
28467
+ enforce_token_subject_node_identity: factory.Expressions.env(ENV_VAR_ENFORCE_TOKEN_SUBJECT_NODE_IDENTITY, 'false'),
28466
28468
  },
28467
28469
  };
28468
28470
  const GATED_CALLBACK_PROFILE = {
@@ -28669,6 +28671,7 @@ function deepClone$3(value) {
28669
28671
  var nodeSecurityProfileFactory = /*#__PURE__*/Object.freeze({
28670
28672
  __proto__: null,
28671
28673
  ENV_VAR_DEFAULT_ENCRYPTION_LEVEL: ENV_VAR_DEFAULT_ENCRYPTION_LEVEL,
28674
+ ENV_VAR_ENFORCE_TOKEN_SUBJECT_NODE_IDENTITY: ENV_VAR_ENFORCE_TOKEN_SUBJECT_NODE_IDENTITY,
28672
28675
  ENV_VAR_HMAC_SECRET: ENV_VAR_HMAC_SECRET,
28673
28676
  ENV_VAR_JWKS_URL: ENV_VAR_JWKS_URL,
28674
28677
  ENV_VAR_JWT_ALGORITHM: ENV_VAR_JWT_ALGORITHM,
@@ -34710,6 +34713,7 @@ class OAuth2AuthorizerFactory extends AuthorizerFactory {
34710
34713
  defaultTtlSec: normalized.defaultTtlSec,
34711
34714
  maxTtlSec: normalized.maxTtlSec,
34712
34715
  reverseAuthTtlSec: normalized.reverseAuthTtlSec,
34716
+ enforceTokenSubjectNodeIdentity: normalized.enforceTokenSubjectNodeIdentity,
34713
34717
  };
34714
34718
  if (tokenIssuer) {
34715
34719
  authorizerOptions.tokenIssuer = tokenIssuer;
@@ -34777,6 +34781,8 @@ function normalizeConfig$c(config) {
34777
34781
  : typeof source.reverse_auth_ttl_sec === 'number'
34778
34782
  ? source.reverse_auth_ttl_sec
34779
34783
  : DEFAULT_REVERSE_AUTH_TTL_SEC;
34784
+ const enforceTokenSubjectNodeIdentity = normalizeBooleanOption(source.enforceTokenSubjectNodeIdentity ??
34785
+ source.enforce_token_subject_node_identity, false);
34780
34786
  const tokenVerifierConfigInput = source.tokenVerifierConfig ?? source.token_verifier_config ?? null;
34781
34787
  const tokenVerifierConfig = normalizeTokenVerifierConfig({
34782
34788
  config: tokenVerifierConfigInput,
@@ -34795,6 +34801,7 @@ function normalizeConfig$c(config) {
34795
34801
  maxTtlSec,
34796
34802
  tokenVerifierConfig,
34797
34803
  reverseAuthTtlSec: reverseAuthCandidate,
34804
+ enforceTokenSubjectNodeIdentity,
34798
34805
  ...(audience ? { audience } : {}),
34799
34806
  };
34800
34807
  if (tokenIssuerConfig) {
@@ -34815,6 +34822,21 @@ function normalizeTokenVerifierConfig({ config, issuer, jwksUrl, algorithm, }) {
34815
34822
  };
34816
34823
  return defaultConfig;
34817
34824
  }
34825
+ function normalizeBooleanOption(value, defaultValue) {
34826
+ if (typeof value === 'boolean') {
34827
+ return value;
34828
+ }
34829
+ if (typeof value === 'string') {
34830
+ const lower = value.toLowerCase().trim();
34831
+ if (lower === 'true' || lower === '1' || lower === 'yes') {
34832
+ return true;
34833
+ }
34834
+ if (lower === 'false' || lower === '0' || lower === 'no') {
34835
+ return false;
34836
+ }
34837
+ }
34838
+ return defaultValue;
34839
+ }
34818
34840
 
34819
34841
  var oauth2AuthorizerFactory = /*#__PURE__*/Object.freeze({
34820
34842
  __proto__: null,
@@ -39832,6 +39854,10 @@ function normalizeOptions$4(raw) {
39832
39854
  : typeof snake.aud === 'string'
39833
39855
  ? snake.aud
39834
39856
  : undefined);
39857
+ const enforceTokenSubjectNodeIdentity = camel.enforceTokenSubjectNodeIdentity ??
39858
+ (typeof snake.enforce_token_subject_node_identity === 'boolean'
39859
+ ? snake.enforce_token_subject_node_identity
39860
+ : undefined);
39835
39861
  return {
39836
39862
  tokenVerifier,
39837
39863
  tokenIssuer,
@@ -39841,6 +39867,7 @@ function normalizeOptions$4(raw) {
39841
39867
  defaultTtlSec,
39842
39868
  maxTtlSec,
39843
39869
  reverseAuthTtlSec,
39870
+ enforceTokenSubjectNodeIdentity,
39844
39871
  };
39845
39872
  }
39846
39873
  class OAuth2Authorizer {
@@ -39854,6 +39881,8 @@ class OAuth2Authorizer {
39854
39881
  this.requireScope = options.requireScope ?? true;
39855
39882
  this.reverseAuthTtlSec =
39856
39883
  options.reverseAuthTtlSec ?? DEFAULT_REVERSE_AUTH_TTL_SEC;
39884
+ this.enforceTokenSubjectNodeIdentity =
39885
+ options.enforceTokenSubjectNodeIdentity ?? false;
39857
39886
  }
39858
39887
  get tokenVerifier() {
39859
39888
  return this.tokenVerifierImpl;
@@ -39983,6 +40012,13 @@ class OAuth2Authorizer {
39983
40012
  });
39984
40013
  return undefined;
39985
40014
  }
40015
+ // Enforce token subject node identity if enabled
40016
+ if (this.enforceTokenSubjectNodeIdentity) {
40017
+ const validationResult = await this.validateTokenSubjectNodeIdentity(frame.systemId, claims);
40018
+ if (!validationResult) {
40019
+ return undefined;
40020
+ }
40021
+ }
39986
40022
  claims.instance_id = claims.instance_id ?? frame.instanceId;
39987
40023
  claims.assigned_path = claims.assigned_path ?? frame.assignedPath;
39988
40024
  claims.accepted_capabilities =
@@ -40060,6 +40096,33 @@ class OAuth2Authorizer {
40060
40096
  }
40061
40097
  return false;
40062
40098
  }
40099
+ async validateTokenSubjectNodeIdentity(systemId, claims) {
40100
+ const sub = claims.sub;
40101
+ if (typeof sub !== 'string' || sub.trim().length === 0) {
40102
+ logger$3.warning('oauth2_attach_missing_subject_claim', {
40103
+ system_id: systemId,
40104
+ });
40105
+ return false;
40106
+ }
40107
+ const expectedPrefix = await core.generateIdAsync({
40108
+ mode: 'fingerprint',
40109
+ material: sub,
40110
+ length: 8,
40111
+ });
40112
+ if (!systemId.startsWith(`${expectedPrefix}-`)) {
40113
+ logger$3.warning('oauth2_attach_node_identity_mismatch', {
40114
+ system_id: systemId,
40115
+ expected_prefix: expectedPrefix,
40116
+ subject: sub,
40117
+ });
40118
+ return false;
40119
+ }
40120
+ logger$3.debug('oauth2_attach_node_identity_verified', {
40121
+ system_id: systemId,
40122
+ expected_prefix: expectedPrefix,
40123
+ });
40124
+ return true;
40125
+ }
40063
40126
  }
40064
40127
 
40065
40128
  var oauth2Authorizer = /*#__PURE__*/Object.freeze({
@@ -513,12 +513,12 @@ async function ensureRuntimeFactoriesRegistered(registry = Registry) {
513
513
  }
514
514
 
515
515
  // This file is auto-generated during build - do not edit manually
516
- // Generated from package.json version: 0.3.16
516
+ // Generated from package.json version: 0.3.18
517
517
  /**
518
518
  * The package version, injected at build time.
519
519
  * @internal
520
520
  */
521
- const VERSION = '0.3.16';
521
+ const VERSION = '0.3.18';
522
522
 
523
523
  let initialized = false;
524
524
  const runtimePlugin = {
@@ -28226,6 +28226,7 @@ const ENV_VAR_DEFAULT_ENCRYPTION_LEVEL = 'FAME_DEFAULT_ENCRYPTION_LEVEL';
28226
28226
  const ENV_VAR_HMAC_SECRET = 'FAME_HMAC_SECRET';
28227
28227
  const ENV_VAR_JWT_REVERSE_AUTH_TRUSTED_ISSUER = 'FAME_JWT_REVERSE_AUTH_TRUSTED_ISSUER';
28228
28228
  const ENV_VAR_JWT_REVERSE_AUTH_AUDIENCE = 'FAME_JWT_REVERSE_AUTH_AUDIENCE';
28229
+ const ENV_VAR_ENFORCE_TOKEN_SUBJECT_NODE_IDENTITY = 'FAME_ENFORCE_TOKEN_SUBJECT_NODE_IDENTITY';
28229
28230
  const PROFILE_NAME_STRICT_OVERLAY = 'strict-overlay';
28230
28231
  const PROFILE_NAME_OVERLAY = 'overlay';
28231
28232
  const PROFILE_NAME_OVERLAY_CALLBACK = 'overlay-callback';
@@ -28461,6 +28462,7 @@ const GATED_PROFILE = {
28461
28462
  max_ttl_sec: 86400,
28462
28463
  algorithm: Expressions.env(ENV_VAR_JWT_ALGORITHM, 'RS256'),
28463
28464
  audience: Expressions.env(ENV_VAR_JWT_AUDIENCE$1),
28465
+ enforce_token_subject_node_identity: Expressions.env(ENV_VAR_ENFORCE_TOKEN_SUBJECT_NODE_IDENTITY, 'false'),
28464
28466
  },
28465
28467
  };
28466
28468
  const GATED_CALLBACK_PROFILE = {
@@ -28667,6 +28669,7 @@ function deepClone$3(value) {
28667
28669
  var nodeSecurityProfileFactory = /*#__PURE__*/Object.freeze({
28668
28670
  __proto__: null,
28669
28671
  ENV_VAR_DEFAULT_ENCRYPTION_LEVEL: ENV_VAR_DEFAULT_ENCRYPTION_LEVEL,
28672
+ ENV_VAR_ENFORCE_TOKEN_SUBJECT_NODE_IDENTITY: ENV_VAR_ENFORCE_TOKEN_SUBJECT_NODE_IDENTITY,
28670
28673
  ENV_VAR_HMAC_SECRET: ENV_VAR_HMAC_SECRET,
28671
28674
  ENV_VAR_JWKS_URL: ENV_VAR_JWKS_URL,
28672
28675
  ENV_VAR_JWT_ALGORITHM: ENV_VAR_JWT_ALGORITHM,
@@ -34708,6 +34711,7 @@ class OAuth2AuthorizerFactory extends AuthorizerFactory {
34708
34711
  defaultTtlSec: normalized.defaultTtlSec,
34709
34712
  maxTtlSec: normalized.maxTtlSec,
34710
34713
  reverseAuthTtlSec: normalized.reverseAuthTtlSec,
34714
+ enforceTokenSubjectNodeIdentity: normalized.enforceTokenSubjectNodeIdentity,
34711
34715
  };
34712
34716
  if (tokenIssuer) {
34713
34717
  authorizerOptions.tokenIssuer = tokenIssuer;
@@ -34775,6 +34779,8 @@ function normalizeConfig$c(config) {
34775
34779
  : typeof source.reverse_auth_ttl_sec === 'number'
34776
34780
  ? source.reverse_auth_ttl_sec
34777
34781
  : DEFAULT_REVERSE_AUTH_TTL_SEC;
34782
+ const enforceTokenSubjectNodeIdentity = normalizeBooleanOption(source.enforceTokenSubjectNodeIdentity ??
34783
+ source.enforce_token_subject_node_identity, false);
34778
34784
  const tokenVerifierConfigInput = source.tokenVerifierConfig ?? source.token_verifier_config ?? null;
34779
34785
  const tokenVerifierConfig = normalizeTokenVerifierConfig({
34780
34786
  config: tokenVerifierConfigInput,
@@ -34793,6 +34799,7 @@ function normalizeConfig$c(config) {
34793
34799
  maxTtlSec,
34794
34800
  tokenVerifierConfig,
34795
34801
  reverseAuthTtlSec: reverseAuthCandidate,
34802
+ enforceTokenSubjectNodeIdentity,
34796
34803
  ...(audience ? { audience } : {}),
34797
34804
  };
34798
34805
  if (tokenIssuerConfig) {
@@ -34813,6 +34820,21 @@ function normalizeTokenVerifierConfig({ config, issuer, jwksUrl, algorithm, }) {
34813
34820
  };
34814
34821
  return defaultConfig;
34815
34822
  }
34823
+ function normalizeBooleanOption(value, defaultValue) {
34824
+ if (typeof value === 'boolean') {
34825
+ return value;
34826
+ }
34827
+ if (typeof value === 'string') {
34828
+ const lower = value.toLowerCase().trim();
34829
+ if (lower === 'true' || lower === '1' || lower === 'yes') {
34830
+ return true;
34831
+ }
34832
+ if (lower === 'false' || lower === '0' || lower === 'no') {
34833
+ return false;
34834
+ }
34835
+ }
34836
+ return defaultValue;
34837
+ }
34816
34838
 
34817
34839
  var oauth2AuthorizerFactory = /*#__PURE__*/Object.freeze({
34818
34840
  __proto__: null,
@@ -39830,6 +39852,10 @@ function normalizeOptions$4(raw) {
39830
39852
  : typeof snake.aud === 'string'
39831
39853
  ? snake.aud
39832
39854
  : undefined);
39855
+ const enforceTokenSubjectNodeIdentity = camel.enforceTokenSubjectNodeIdentity ??
39856
+ (typeof snake.enforce_token_subject_node_identity === 'boolean'
39857
+ ? snake.enforce_token_subject_node_identity
39858
+ : undefined);
39833
39859
  return {
39834
39860
  tokenVerifier,
39835
39861
  tokenIssuer,
@@ -39839,6 +39865,7 @@ function normalizeOptions$4(raw) {
39839
39865
  defaultTtlSec,
39840
39866
  maxTtlSec,
39841
39867
  reverseAuthTtlSec,
39868
+ enforceTokenSubjectNodeIdentity,
39842
39869
  };
39843
39870
  }
39844
39871
  class OAuth2Authorizer {
@@ -39852,6 +39879,8 @@ class OAuth2Authorizer {
39852
39879
  this.requireScope = options.requireScope ?? true;
39853
39880
  this.reverseAuthTtlSec =
39854
39881
  options.reverseAuthTtlSec ?? DEFAULT_REVERSE_AUTH_TTL_SEC;
39882
+ this.enforceTokenSubjectNodeIdentity =
39883
+ options.enforceTokenSubjectNodeIdentity ?? false;
39855
39884
  }
39856
39885
  get tokenVerifier() {
39857
39886
  return this.tokenVerifierImpl;
@@ -39981,6 +40010,13 @@ class OAuth2Authorizer {
39981
40010
  });
39982
40011
  return undefined;
39983
40012
  }
40013
+ // Enforce token subject node identity if enabled
40014
+ if (this.enforceTokenSubjectNodeIdentity) {
40015
+ const validationResult = await this.validateTokenSubjectNodeIdentity(frame.systemId, claims);
40016
+ if (!validationResult) {
40017
+ return undefined;
40018
+ }
40019
+ }
39984
40020
  claims.instance_id = claims.instance_id ?? frame.instanceId;
39985
40021
  claims.assigned_path = claims.assigned_path ?? frame.assignedPath;
39986
40022
  claims.accepted_capabilities =
@@ -40058,6 +40094,33 @@ class OAuth2Authorizer {
40058
40094
  }
40059
40095
  return false;
40060
40096
  }
40097
+ async validateTokenSubjectNodeIdentity(systemId, claims) {
40098
+ const sub = claims.sub;
40099
+ if (typeof sub !== 'string' || sub.trim().length === 0) {
40100
+ logger$3.warning('oauth2_attach_missing_subject_claim', {
40101
+ system_id: systemId,
40102
+ });
40103
+ return false;
40104
+ }
40105
+ const expectedPrefix = await generateIdAsync({
40106
+ mode: 'fingerprint',
40107
+ material: sub,
40108
+ length: 8,
40109
+ });
40110
+ if (!systemId.startsWith(`${expectedPrefix}-`)) {
40111
+ logger$3.warning('oauth2_attach_node_identity_mismatch', {
40112
+ system_id: systemId,
40113
+ expected_prefix: expectedPrefix,
40114
+ subject: sub,
40115
+ });
40116
+ return false;
40117
+ }
40118
+ logger$3.debug('oauth2_attach_node_identity_verified', {
40119
+ system_id: systemId,
40120
+ expected_prefix: expectedPrefix,
40121
+ });
40122
+ return true;
40123
+ }
40061
40124
  }
40062
40125
 
40063
40126
  var oauth2Authorizer = /*#__PURE__*/Object.freeze({
@@ -86,6 +86,7 @@ class OAuth2AuthorizerFactory extends authorizer_factory_js_1.AuthorizerFactory
86
86
  defaultTtlSec: normalized.defaultTtlSec,
87
87
  maxTtlSec: normalized.maxTtlSec,
88
88
  reverseAuthTtlSec: normalized.reverseAuthTtlSec,
89
+ enforceTokenSubjectNodeIdentity: normalized.enforceTokenSubjectNodeIdentity,
89
90
  };
90
91
  if (tokenIssuer) {
91
92
  authorizerOptions.tokenIssuer = tokenIssuer;
@@ -154,6 +155,8 @@ function normalizeConfig(config) {
154
155
  : typeof source.reverse_auth_ttl_sec === 'number'
155
156
  ? source.reverse_auth_ttl_sec
156
157
  : ttl_constants_js_1.DEFAULT_REVERSE_AUTH_TTL_SEC;
158
+ const enforceTokenSubjectNodeIdentity = normalizeBooleanOption(source.enforceTokenSubjectNodeIdentity ??
159
+ source.enforce_token_subject_node_identity, false);
157
160
  const tokenVerifierConfigInput = source.tokenVerifierConfig ?? source.token_verifier_config ?? null;
158
161
  const tokenVerifierConfig = normalizeTokenVerifierConfig({
159
162
  config: tokenVerifierConfigInput,
@@ -172,6 +175,7 @@ function normalizeConfig(config) {
172
175
  maxTtlSec,
173
176
  tokenVerifierConfig,
174
177
  reverseAuthTtlSec: reverseAuthCandidate,
178
+ enforceTokenSubjectNodeIdentity,
175
179
  ...(audience ? { audience } : {}),
176
180
  };
177
181
  if (tokenIssuerConfig) {
@@ -192,4 +196,19 @@ function normalizeTokenVerifierConfig({ config, issuer, jwksUrl, algorithm, }) {
192
196
  };
193
197
  return defaultConfig;
194
198
  }
199
+ function normalizeBooleanOption(value, defaultValue) {
200
+ if (typeof value === 'boolean') {
201
+ return value;
202
+ }
203
+ if (typeof value === 'string') {
204
+ const lower = value.toLowerCase().trim();
205
+ if (lower === 'true' || lower === '1' || lower === 'yes') {
206
+ return true;
207
+ }
208
+ if (lower === 'false' || lower === '0' || lower === 'no') {
209
+ return false;
210
+ }
211
+ }
212
+ return defaultValue;
213
+ }
195
214
  exports.default = OAuth2AuthorizerFactory;
@@ -37,6 +37,10 @@ function normalizeOptions(raw) {
37
37
  : typeof snake.aud === 'string'
38
38
  ? snake.aud
39
39
  : undefined);
40
+ const enforceTokenSubjectNodeIdentity = camel.enforceTokenSubjectNodeIdentity ??
41
+ (typeof snake.enforce_token_subject_node_identity === 'boolean'
42
+ ? snake.enforce_token_subject_node_identity
43
+ : undefined);
40
44
  return {
41
45
  tokenVerifier,
42
46
  tokenIssuer,
@@ -46,6 +50,7 @@ function normalizeOptions(raw) {
46
50
  defaultTtlSec,
47
51
  maxTtlSec,
48
52
  reverseAuthTtlSec,
53
+ enforceTokenSubjectNodeIdentity,
49
54
  };
50
55
  }
51
56
  class OAuth2Authorizer {
@@ -59,6 +64,8 @@ class OAuth2Authorizer {
59
64
  this.requireScope = options.requireScope ?? true;
60
65
  this.reverseAuthTtlSec =
61
66
  options.reverseAuthTtlSec ?? ttl_constants_js_1.DEFAULT_REVERSE_AUTH_TTL_SEC;
67
+ this.enforceTokenSubjectNodeIdentity =
68
+ options.enforceTokenSubjectNodeIdentity ?? false;
62
69
  }
63
70
  get tokenVerifier() {
64
71
  return this.tokenVerifierImpl;
@@ -188,6 +195,13 @@ class OAuth2Authorizer {
188
195
  });
189
196
  return undefined;
190
197
  }
198
+ // Enforce token subject node identity if enabled
199
+ if (this.enforceTokenSubjectNodeIdentity) {
200
+ const validationResult = await this.validateTokenSubjectNodeIdentity(frame.systemId, claims);
201
+ if (!validationResult) {
202
+ return undefined;
203
+ }
204
+ }
191
205
  claims.instance_id = claims.instance_id ?? frame.instanceId;
192
206
  claims.assigned_path = claims.assigned_path ?? frame.assignedPath;
193
207
  claims.accepted_capabilities =
@@ -265,5 +279,32 @@ class OAuth2Authorizer {
265
279
  }
266
280
  return false;
267
281
  }
282
+ async validateTokenSubjectNodeIdentity(systemId, claims) {
283
+ const sub = claims.sub;
284
+ if (typeof sub !== 'string' || sub.trim().length === 0) {
285
+ logger.warning('oauth2_attach_missing_subject_claim', {
286
+ system_id: systemId,
287
+ });
288
+ return false;
289
+ }
290
+ const expectedPrefix = await (0, core_1.generateIdAsync)({
291
+ mode: 'fingerprint',
292
+ material: sub,
293
+ length: 8,
294
+ });
295
+ if (!systemId.startsWith(`${expectedPrefix}-`)) {
296
+ logger.warning('oauth2_attach_node_identity_mismatch', {
297
+ system_id: systemId,
298
+ expected_prefix: expectedPrefix,
299
+ subject: sub,
300
+ });
301
+ return false;
302
+ }
303
+ logger.debug('oauth2_attach_node_identity_verified', {
304
+ system_id: systemId,
305
+ expected_prefix: expectedPrefix,
306
+ });
307
+ return true;
308
+ }
268
309
  }
269
310
  exports.OAuth2Authorizer = OAuth2Authorizer;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.NodeSecurityProfileFactory = exports.FACTORY_META = exports.PROFILE_NAME_OPEN = exports.PROFILE_NAME_GATED_CALLBACK = exports.PROFILE_NAME_GATED = exports.PROFILE_NAME_OVERLAY_CALLBACK = exports.PROFILE_NAME_OVERLAY = exports.PROFILE_NAME_STRICT_OVERLAY = exports.ENV_VAR_JWT_REVERSE_AUTH_AUDIENCE = exports.ENV_VAR_JWT_REVERSE_AUTH_TRUSTED_ISSUER = exports.ENV_VAR_HMAC_SECRET = exports.ENV_VAR_DEFAULT_ENCRYPTION_LEVEL = exports.ENV_VAR_JWKS_URL = exports.ENV_VAR_JWT_AUDIENCE = exports.ENV_VAR_JWT_ALGORITHM = exports.ENV_VAR_JWT_TRUSTED_ISSUER = void 0;
3
+ exports.NodeSecurityProfileFactory = exports.FACTORY_META = exports.PROFILE_NAME_OPEN = exports.PROFILE_NAME_GATED_CALLBACK = exports.PROFILE_NAME_GATED = exports.PROFILE_NAME_OVERLAY_CALLBACK = exports.PROFILE_NAME_OVERLAY = exports.PROFILE_NAME_STRICT_OVERLAY = exports.ENV_VAR_ENFORCE_TOKEN_SUBJECT_NODE_IDENTITY = exports.ENV_VAR_JWT_REVERSE_AUTH_AUDIENCE = exports.ENV_VAR_JWT_REVERSE_AUTH_TRUSTED_ISSUER = exports.ENV_VAR_HMAC_SECRET = exports.ENV_VAR_DEFAULT_ENCRYPTION_LEVEL = exports.ENV_VAR_JWKS_URL = exports.ENV_VAR_JWT_AUDIENCE = exports.ENV_VAR_JWT_ALGORITHM = exports.ENV_VAR_JWT_TRUSTED_ISSUER = void 0;
4
4
  const factory_1 = require("@naylence/factory");
5
5
  const security_manager_factory_js_1 = require("./security-manager-factory.js");
6
6
  const logging_js_1 = require("../util/logging.js");
@@ -13,6 +13,7 @@ exports.ENV_VAR_DEFAULT_ENCRYPTION_LEVEL = 'FAME_DEFAULT_ENCRYPTION_LEVEL';
13
13
  exports.ENV_VAR_HMAC_SECRET = 'FAME_HMAC_SECRET';
14
14
  exports.ENV_VAR_JWT_REVERSE_AUTH_TRUSTED_ISSUER = 'FAME_JWT_REVERSE_AUTH_TRUSTED_ISSUER';
15
15
  exports.ENV_VAR_JWT_REVERSE_AUTH_AUDIENCE = 'FAME_JWT_REVERSE_AUTH_AUDIENCE';
16
+ exports.ENV_VAR_ENFORCE_TOKEN_SUBJECT_NODE_IDENTITY = 'FAME_ENFORCE_TOKEN_SUBJECT_NODE_IDENTITY';
16
17
  exports.PROFILE_NAME_STRICT_OVERLAY = 'strict-overlay';
17
18
  exports.PROFILE_NAME_OVERLAY = 'overlay';
18
19
  exports.PROFILE_NAME_OVERLAY_CALLBACK = 'overlay-callback';
@@ -248,6 +249,7 @@ const GATED_PROFILE = {
248
249
  max_ttl_sec: 86400,
249
250
  algorithm: factory_1.Expressions.env(exports.ENV_VAR_JWT_ALGORITHM, 'RS256'),
250
251
  audience: factory_1.Expressions.env(exports.ENV_VAR_JWT_AUDIENCE),
252
+ enforce_token_subject_node_identity: factory_1.Expressions.env(exports.ENV_VAR_ENFORCE_TOKEN_SUBJECT_NODE_IDENTITY, 'false'),
251
253
  },
252
254
  };
253
255
  const GATED_CALLBACK_PROFILE = {
@@ -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.16
3
+ // Generated from package.json version: 0.3.18
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.16';
10
+ exports.VERSION = '0.3.18';
@@ -50,6 +50,7 @@ export class OAuth2AuthorizerFactory extends AuthorizerFactory {
50
50
  defaultTtlSec: normalized.defaultTtlSec,
51
51
  maxTtlSec: normalized.maxTtlSec,
52
52
  reverseAuthTtlSec: normalized.reverseAuthTtlSec,
53
+ enforceTokenSubjectNodeIdentity: normalized.enforceTokenSubjectNodeIdentity,
53
54
  };
54
55
  if (tokenIssuer) {
55
56
  authorizerOptions.tokenIssuer = tokenIssuer;
@@ -117,6 +118,8 @@ function normalizeConfig(config) {
117
118
  : typeof source.reverse_auth_ttl_sec === 'number'
118
119
  ? source.reverse_auth_ttl_sec
119
120
  : DEFAULT_REVERSE_AUTH_TTL_SEC;
121
+ const enforceTokenSubjectNodeIdentity = normalizeBooleanOption(source.enforceTokenSubjectNodeIdentity ??
122
+ source.enforce_token_subject_node_identity, false);
120
123
  const tokenVerifierConfigInput = source.tokenVerifierConfig ?? source.token_verifier_config ?? null;
121
124
  const tokenVerifierConfig = normalizeTokenVerifierConfig({
122
125
  config: tokenVerifierConfigInput,
@@ -135,6 +138,7 @@ function normalizeConfig(config) {
135
138
  maxTtlSec,
136
139
  tokenVerifierConfig,
137
140
  reverseAuthTtlSec: reverseAuthCandidate,
141
+ enforceTokenSubjectNodeIdentity,
138
142
  ...(audience ? { audience } : {}),
139
143
  };
140
144
  if (tokenIssuerConfig) {
@@ -155,4 +159,19 @@ function normalizeTokenVerifierConfig({ config, issuer, jwksUrl, algorithm, }) {
155
159
  };
156
160
  return defaultConfig;
157
161
  }
162
+ function normalizeBooleanOption(value, defaultValue) {
163
+ if (typeof value === 'boolean') {
164
+ return value;
165
+ }
166
+ if (typeof value === 'string') {
167
+ const lower = value.toLowerCase().trim();
168
+ if (lower === 'true' || lower === '1' || lower === 'yes') {
169
+ return true;
170
+ }
171
+ if (lower === 'false' || lower === '0' || lower === 'no') {
172
+ return false;
173
+ }
174
+ }
175
+ return defaultValue;
176
+ }
158
177
  export default OAuth2AuthorizerFactory;
@@ -1,4 +1,4 @@
1
- import { createAuthorizationContext } from '@naylence/core';
1
+ import { createAuthorizationContext, generateIdAsync } from '@naylence/core';
2
2
  import { DEFAULT_REVERSE_AUTH_TTL_SEC } from '../../constants/ttl-constants.js';
3
3
  import { getLogger } from '../../util/logging.js';
4
4
  const logger = getLogger('naylence.fame.security.auth.oauth2_authorizer');
@@ -34,6 +34,10 @@ function normalizeOptions(raw) {
34
34
  : typeof snake.aud === 'string'
35
35
  ? snake.aud
36
36
  : undefined);
37
+ const enforceTokenSubjectNodeIdentity = camel.enforceTokenSubjectNodeIdentity ??
38
+ (typeof snake.enforce_token_subject_node_identity === 'boolean'
39
+ ? snake.enforce_token_subject_node_identity
40
+ : undefined);
37
41
  return {
38
42
  tokenVerifier,
39
43
  tokenIssuer,
@@ -43,6 +47,7 @@ function normalizeOptions(raw) {
43
47
  defaultTtlSec,
44
48
  maxTtlSec,
45
49
  reverseAuthTtlSec,
50
+ enforceTokenSubjectNodeIdentity,
46
51
  };
47
52
  }
48
53
  export class OAuth2Authorizer {
@@ -56,6 +61,8 @@ export class OAuth2Authorizer {
56
61
  this.requireScope = options.requireScope ?? true;
57
62
  this.reverseAuthTtlSec =
58
63
  options.reverseAuthTtlSec ?? DEFAULT_REVERSE_AUTH_TTL_SEC;
64
+ this.enforceTokenSubjectNodeIdentity =
65
+ options.enforceTokenSubjectNodeIdentity ?? false;
59
66
  }
60
67
  get tokenVerifier() {
61
68
  return this.tokenVerifierImpl;
@@ -185,6 +192,13 @@ export class OAuth2Authorizer {
185
192
  });
186
193
  return undefined;
187
194
  }
195
+ // Enforce token subject node identity if enabled
196
+ if (this.enforceTokenSubjectNodeIdentity) {
197
+ const validationResult = await this.validateTokenSubjectNodeIdentity(frame.systemId, claims);
198
+ if (!validationResult) {
199
+ return undefined;
200
+ }
201
+ }
188
202
  claims.instance_id = claims.instance_id ?? frame.instanceId;
189
203
  claims.assigned_path = claims.assigned_path ?? frame.assignedPath;
190
204
  claims.accepted_capabilities =
@@ -262,4 +276,31 @@ export class OAuth2Authorizer {
262
276
  }
263
277
  return false;
264
278
  }
279
+ async validateTokenSubjectNodeIdentity(systemId, claims) {
280
+ const sub = claims.sub;
281
+ if (typeof sub !== 'string' || sub.trim().length === 0) {
282
+ logger.warning('oauth2_attach_missing_subject_claim', {
283
+ system_id: systemId,
284
+ });
285
+ return false;
286
+ }
287
+ const expectedPrefix = await generateIdAsync({
288
+ mode: 'fingerprint',
289
+ material: sub,
290
+ length: 8,
291
+ });
292
+ if (!systemId.startsWith(`${expectedPrefix}-`)) {
293
+ logger.warning('oauth2_attach_node_identity_mismatch', {
294
+ system_id: systemId,
295
+ expected_prefix: expectedPrefix,
296
+ subject: sub,
297
+ });
298
+ return false;
299
+ }
300
+ logger.debug('oauth2_attach_node_identity_verified', {
301
+ system_id: systemId,
302
+ expected_prefix: expectedPrefix,
303
+ });
304
+ return true;
305
+ }
265
306
  }
@@ -10,6 +10,7 @@ export const ENV_VAR_DEFAULT_ENCRYPTION_LEVEL = 'FAME_DEFAULT_ENCRYPTION_LEVEL';
10
10
  export const ENV_VAR_HMAC_SECRET = 'FAME_HMAC_SECRET';
11
11
  export const ENV_VAR_JWT_REVERSE_AUTH_TRUSTED_ISSUER = 'FAME_JWT_REVERSE_AUTH_TRUSTED_ISSUER';
12
12
  export const ENV_VAR_JWT_REVERSE_AUTH_AUDIENCE = 'FAME_JWT_REVERSE_AUTH_AUDIENCE';
13
+ export const ENV_VAR_ENFORCE_TOKEN_SUBJECT_NODE_IDENTITY = 'FAME_ENFORCE_TOKEN_SUBJECT_NODE_IDENTITY';
13
14
  export const PROFILE_NAME_STRICT_OVERLAY = 'strict-overlay';
14
15
  export const PROFILE_NAME_OVERLAY = 'overlay';
15
16
  export const PROFILE_NAME_OVERLAY_CALLBACK = 'overlay-callback';
@@ -245,6 +246,7 @@ const GATED_PROFILE = {
245
246
  max_ttl_sec: 86400,
246
247
  algorithm: Expressions.env(ENV_VAR_JWT_ALGORITHM, 'RS256'),
247
248
  audience: Expressions.env(ENV_VAR_JWT_AUDIENCE),
249
+ enforce_token_subject_node_identity: Expressions.env(ENV_VAR_ENFORCE_TOKEN_SUBJECT_NODE_IDENTITY, 'false'),
248
250
  },
249
251
  };
250
252
  const GATED_CALLBACK_PROFILE = {
@@ -1,7 +1,7 @@
1
1
  // This file is auto-generated during build - do not edit manually
2
- // Generated from package.json version: 0.3.16
2
+ // Generated from package.json version: 0.3.18
3
3
  /**
4
4
  * The package version, injected at build time.
5
5
  * @internal
6
6
  */
7
- export const VERSION = '0.3.16';
7
+ export const VERSION = '0.3.18';
@@ -14,12 +14,12 @@ var fastify = require('fastify');
14
14
  var websocketPlugin = require('@fastify/websocket');
15
15
 
16
16
  // This file is auto-generated during build - do not edit manually
17
- // Generated from package.json version: 0.3.16
17
+ // Generated from package.json version: 0.3.18
18
18
  /**
19
19
  * The package version, injected at build time.
20
20
  * @internal
21
21
  */
22
- const VERSION = '0.3.16';
22
+ const VERSION = '0.3.18';
23
23
 
24
24
  /**
25
25
  * Fame protocol specific error classes with WebSocket close codes and proper inheritance.
@@ -28115,6 +28115,7 @@ const ENV_VAR_DEFAULT_ENCRYPTION_LEVEL = 'FAME_DEFAULT_ENCRYPTION_LEVEL';
28115
28115
  const ENV_VAR_HMAC_SECRET = 'FAME_HMAC_SECRET';
28116
28116
  const ENV_VAR_JWT_REVERSE_AUTH_TRUSTED_ISSUER = 'FAME_JWT_REVERSE_AUTH_TRUSTED_ISSUER';
28117
28117
  const ENV_VAR_JWT_REVERSE_AUTH_AUDIENCE = 'FAME_JWT_REVERSE_AUTH_AUDIENCE';
28118
+ const ENV_VAR_ENFORCE_TOKEN_SUBJECT_NODE_IDENTITY = 'FAME_ENFORCE_TOKEN_SUBJECT_NODE_IDENTITY';
28118
28119
  const PROFILE_NAME_STRICT_OVERLAY = 'strict-overlay';
28119
28120
  const PROFILE_NAME_OVERLAY = 'overlay';
28120
28121
  const PROFILE_NAME_OVERLAY_CALLBACK = 'overlay-callback';
@@ -28350,6 +28351,7 @@ const GATED_PROFILE = {
28350
28351
  max_ttl_sec: 86400,
28351
28352
  algorithm: factory.Expressions.env(ENV_VAR_JWT_ALGORITHM, 'RS256'),
28352
28353
  audience: factory.Expressions.env(ENV_VAR_JWT_AUDIENCE$1),
28354
+ enforce_token_subject_node_identity: factory.Expressions.env(ENV_VAR_ENFORCE_TOKEN_SUBJECT_NODE_IDENTITY, 'false'),
28353
28355
  },
28354
28356
  };
28355
28357
  const GATED_CALLBACK_PROFILE = {
@@ -28556,6 +28558,7 @@ function deepClone$3(value) {
28556
28558
  var nodeSecurityProfileFactory = /*#__PURE__*/Object.freeze({
28557
28559
  __proto__: null,
28558
28560
  ENV_VAR_DEFAULT_ENCRYPTION_LEVEL: ENV_VAR_DEFAULT_ENCRYPTION_LEVEL,
28561
+ ENV_VAR_ENFORCE_TOKEN_SUBJECT_NODE_IDENTITY: ENV_VAR_ENFORCE_TOKEN_SUBJECT_NODE_IDENTITY,
28559
28562
  ENV_VAR_HMAC_SECRET: ENV_VAR_HMAC_SECRET,
28560
28563
  ENV_VAR_JWKS_URL: ENV_VAR_JWKS_URL,
28561
28564
  ENV_VAR_JWT_ALGORITHM: ENV_VAR_JWT_ALGORITHM,
@@ -33260,6 +33263,7 @@ class OAuth2AuthorizerFactory extends AuthorizerFactory {
33260
33263
  defaultTtlSec: normalized.defaultTtlSec,
33261
33264
  maxTtlSec: normalized.maxTtlSec,
33262
33265
  reverseAuthTtlSec: normalized.reverseAuthTtlSec,
33266
+ enforceTokenSubjectNodeIdentity: normalized.enforceTokenSubjectNodeIdentity,
33263
33267
  };
33264
33268
  if (tokenIssuer) {
33265
33269
  authorizerOptions.tokenIssuer = tokenIssuer;
@@ -33327,6 +33331,8 @@ function normalizeConfig$c(config) {
33327
33331
  : typeof source.reverse_auth_ttl_sec === 'number'
33328
33332
  ? source.reverse_auth_ttl_sec
33329
33333
  : DEFAULT_REVERSE_AUTH_TTL_SEC;
33334
+ const enforceTokenSubjectNodeIdentity = normalizeBooleanOption(source.enforceTokenSubjectNodeIdentity ??
33335
+ source.enforce_token_subject_node_identity, false);
33330
33336
  const tokenVerifierConfigInput = source.tokenVerifierConfig ?? source.token_verifier_config ?? null;
33331
33337
  const tokenVerifierConfig = normalizeTokenVerifierConfig({
33332
33338
  config: tokenVerifierConfigInput,
@@ -33345,6 +33351,7 @@ function normalizeConfig$c(config) {
33345
33351
  maxTtlSec,
33346
33352
  tokenVerifierConfig,
33347
33353
  reverseAuthTtlSec: reverseAuthCandidate,
33354
+ enforceTokenSubjectNodeIdentity,
33348
33355
  ...(audience ? { audience } : {}),
33349
33356
  };
33350
33357
  if (tokenIssuerConfig) {
@@ -33365,6 +33372,21 @@ function normalizeTokenVerifierConfig({ config, issuer, jwksUrl, algorithm, }) {
33365
33372
  };
33366
33373
  return defaultConfig;
33367
33374
  }
33375
+ function normalizeBooleanOption(value, defaultValue) {
33376
+ if (typeof value === 'boolean') {
33377
+ return value;
33378
+ }
33379
+ if (typeof value === 'string') {
33380
+ const lower = value.toLowerCase().trim();
33381
+ if (lower === 'true' || lower === '1' || lower === 'yes') {
33382
+ return true;
33383
+ }
33384
+ if (lower === 'false' || lower === '0' || lower === 'no') {
33385
+ return false;
33386
+ }
33387
+ }
33388
+ return defaultValue;
33389
+ }
33368
33390
 
33369
33391
  var oauth2AuthorizerFactory = /*#__PURE__*/Object.freeze({
33370
33392
  __proto__: null,
@@ -39634,6 +39656,10 @@ function normalizeOptions$4(raw) {
39634
39656
  : typeof snake.aud === 'string'
39635
39657
  ? snake.aud
39636
39658
  : undefined);
39659
+ const enforceTokenSubjectNodeIdentity = camel.enforceTokenSubjectNodeIdentity ??
39660
+ (typeof snake.enforce_token_subject_node_identity === 'boolean'
39661
+ ? snake.enforce_token_subject_node_identity
39662
+ : undefined);
39637
39663
  return {
39638
39664
  tokenVerifier,
39639
39665
  tokenIssuer,
@@ -39643,6 +39669,7 @@ function normalizeOptions$4(raw) {
39643
39669
  defaultTtlSec,
39644
39670
  maxTtlSec,
39645
39671
  reverseAuthTtlSec,
39672
+ enforceTokenSubjectNodeIdentity,
39646
39673
  };
39647
39674
  }
39648
39675
  class OAuth2Authorizer {
@@ -39656,6 +39683,8 @@ class OAuth2Authorizer {
39656
39683
  this.requireScope = options.requireScope ?? true;
39657
39684
  this.reverseAuthTtlSec =
39658
39685
  options.reverseAuthTtlSec ?? DEFAULT_REVERSE_AUTH_TTL_SEC;
39686
+ this.enforceTokenSubjectNodeIdentity =
39687
+ options.enforceTokenSubjectNodeIdentity ?? false;
39659
39688
  }
39660
39689
  get tokenVerifier() {
39661
39690
  return this.tokenVerifierImpl;
@@ -39785,6 +39814,13 @@ class OAuth2Authorizer {
39785
39814
  });
39786
39815
  return undefined;
39787
39816
  }
39817
+ // Enforce token subject node identity if enabled
39818
+ if (this.enforceTokenSubjectNodeIdentity) {
39819
+ const validationResult = await this.validateTokenSubjectNodeIdentity(frame.systemId, claims);
39820
+ if (!validationResult) {
39821
+ return undefined;
39822
+ }
39823
+ }
39788
39824
  claims.instance_id = claims.instance_id ?? frame.instanceId;
39789
39825
  claims.assigned_path = claims.assigned_path ?? frame.assignedPath;
39790
39826
  claims.accepted_capabilities =
@@ -39862,6 +39898,33 @@ class OAuth2Authorizer {
39862
39898
  }
39863
39899
  return false;
39864
39900
  }
39901
+ async validateTokenSubjectNodeIdentity(systemId, claims) {
39902
+ const sub = claims.sub;
39903
+ if (typeof sub !== 'string' || sub.trim().length === 0) {
39904
+ logger$3.warning('oauth2_attach_missing_subject_claim', {
39905
+ system_id: systemId,
39906
+ });
39907
+ return false;
39908
+ }
39909
+ const expectedPrefix = await core.generateIdAsync({
39910
+ mode: 'fingerprint',
39911
+ material: sub,
39912
+ length: 8,
39913
+ });
39914
+ if (!systemId.startsWith(`${expectedPrefix}-`)) {
39915
+ logger$3.warning('oauth2_attach_node_identity_mismatch', {
39916
+ system_id: systemId,
39917
+ expected_prefix: expectedPrefix,
39918
+ subject: sub,
39919
+ });
39920
+ return false;
39921
+ }
39922
+ logger$3.debug('oauth2_attach_node_identity_verified', {
39923
+ system_id: systemId,
39924
+ expected_prefix: expectedPrefix,
39925
+ });
39926
+ return true;
39927
+ }
39865
39928
  }
39866
39929
 
39867
39930
  var oauth2Authorizer = /*#__PURE__*/Object.freeze({
@@ -13,12 +13,12 @@ import fastify from 'fastify';
13
13
  import websocketPlugin from '@fastify/websocket';
14
14
 
15
15
  // This file is auto-generated during build - do not edit manually
16
- // Generated from package.json version: 0.3.16
16
+ // Generated from package.json version: 0.3.18
17
17
  /**
18
18
  * The package version, injected at build time.
19
19
  * @internal
20
20
  */
21
- const VERSION = '0.3.16';
21
+ const VERSION = '0.3.18';
22
22
 
23
23
  /**
24
24
  * Fame protocol specific error classes with WebSocket close codes and proper inheritance.
@@ -28114,6 +28114,7 @@ const ENV_VAR_DEFAULT_ENCRYPTION_LEVEL = 'FAME_DEFAULT_ENCRYPTION_LEVEL';
28114
28114
  const ENV_VAR_HMAC_SECRET = 'FAME_HMAC_SECRET';
28115
28115
  const ENV_VAR_JWT_REVERSE_AUTH_TRUSTED_ISSUER = 'FAME_JWT_REVERSE_AUTH_TRUSTED_ISSUER';
28116
28116
  const ENV_VAR_JWT_REVERSE_AUTH_AUDIENCE = 'FAME_JWT_REVERSE_AUTH_AUDIENCE';
28117
+ const ENV_VAR_ENFORCE_TOKEN_SUBJECT_NODE_IDENTITY = 'FAME_ENFORCE_TOKEN_SUBJECT_NODE_IDENTITY';
28117
28118
  const PROFILE_NAME_STRICT_OVERLAY = 'strict-overlay';
28118
28119
  const PROFILE_NAME_OVERLAY = 'overlay';
28119
28120
  const PROFILE_NAME_OVERLAY_CALLBACK = 'overlay-callback';
@@ -28349,6 +28350,7 @@ const GATED_PROFILE = {
28349
28350
  max_ttl_sec: 86400,
28350
28351
  algorithm: Expressions.env(ENV_VAR_JWT_ALGORITHM, 'RS256'),
28351
28352
  audience: Expressions.env(ENV_VAR_JWT_AUDIENCE$1),
28353
+ enforce_token_subject_node_identity: Expressions.env(ENV_VAR_ENFORCE_TOKEN_SUBJECT_NODE_IDENTITY, 'false'),
28352
28354
  },
28353
28355
  };
28354
28356
  const GATED_CALLBACK_PROFILE = {
@@ -28555,6 +28557,7 @@ function deepClone$3(value) {
28555
28557
  var nodeSecurityProfileFactory = /*#__PURE__*/Object.freeze({
28556
28558
  __proto__: null,
28557
28559
  ENV_VAR_DEFAULT_ENCRYPTION_LEVEL: ENV_VAR_DEFAULT_ENCRYPTION_LEVEL,
28560
+ ENV_VAR_ENFORCE_TOKEN_SUBJECT_NODE_IDENTITY: ENV_VAR_ENFORCE_TOKEN_SUBJECT_NODE_IDENTITY,
28558
28561
  ENV_VAR_HMAC_SECRET: ENV_VAR_HMAC_SECRET,
28559
28562
  ENV_VAR_JWKS_URL: ENV_VAR_JWKS_URL,
28560
28563
  ENV_VAR_JWT_ALGORITHM: ENV_VAR_JWT_ALGORITHM,
@@ -33259,6 +33262,7 @@ class OAuth2AuthorizerFactory extends AuthorizerFactory {
33259
33262
  defaultTtlSec: normalized.defaultTtlSec,
33260
33263
  maxTtlSec: normalized.maxTtlSec,
33261
33264
  reverseAuthTtlSec: normalized.reverseAuthTtlSec,
33265
+ enforceTokenSubjectNodeIdentity: normalized.enforceTokenSubjectNodeIdentity,
33262
33266
  };
33263
33267
  if (tokenIssuer) {
33264
33268
  authorizerOptions.tokenIssuer = tokenIssuer;
@@ -33326,6 +33330,8 @@ function normalizeConfig$c(config) {
33326
33330
  : typeof source.reverse_auth_ttl_sec === 'number'
33327
33331
  ? source.reverse_auth_ttl_sec
33328
33332
  : DEFAULT_REVERSE_AUTH_TTL_SEC;
33333
+ const enforceTokenSubjectNodeIdentity = normalizeBooleanOption(source.enforceTokenSubjectNodeIdentity ??
33334
+ source.enforce_token_subject_node_identity, false);
33329
33335
  const tokenVerifierConfigInput = source.tokenVerifierConfig ?? source.token_verifier_config ?? null;
33330
33336
  const tokenVerifierConfig = normalizeTokenVerifierConfig({
33331
33337
  config: tokenVerifierConfigInput,
@@ -33344,6 +33350,7 @@ function normalizeConfig$c(config) {
33344
33350
  maxTtlSec,
33345
33351
  tokenVerifierConfig,
33346
33352
  reverseAuthTtlSec: reverseAuthCandidate,
33353
+ enforceTokenSubjectNodeIdentity,
33347
33354
  ...(audience ? { audience } : {}),
33348
33355
  };
33349
33356
  if (tokenIssuerConfig) {
@@ -33364,6 +33371,21 @@ function normalizeTokenVerifierConfig({ config, issuer, jwksUrl, algorithm, }) {
33364
33371
  };
33365
33372
  return defaultConfig;
33366
33373
  }
33374
+ function normalizeBooleanOption(value, defaultValue) {
33375
+ if (typeof value === 'boolean') {
33376
+ return value;
33377
+ }
33378
+ if (typeof value === 'string') {
33379
+ const lower = value.toLowerCase().trim();
33380
+ if (lower === 'true' || lower === '1' || lower === 'yes') {
33381
+ return true;
33382
+ }
33383
+ if (lower === 'false' || lower === '0' || lower === 'no') {
33384
+ return false;
33385
+ }
33386
+ }
33387
+ return defaultValue;
33388
+ }
33367
33389
 
33368
33390
  var oauth2AuthorizerFactory = /*#__PURE__*/Object.freeze({
33369
33391
  __proto__: null,
@@ -39633,6 +39655,10 @@ function normalizeOptions$4(raw) {
39633
39655
  : typeof snake.aud === 'string'
39634
39656
  ? snake.aud
39635
39657
  : undefined);
39658
+ const enforceTokenSubjectNodeIdentity = camel.enforceTokenSubjectNodeIdentity ??
39659
+ (typeof snake.enforce_token_subject_node_identity === 'boolean'
39660
+ ? snake.enforce_token_subject_node_identity
39661
+ : undefined);
39636
39662
  return {
39637
39663
  tokenVerifier,
39638
39664
  tokenIssuer,
@@ -39642,6 +39668,7 @@ function normalizeOptions$4(raw) {
39642
39668
  defaultTtlSec,
39643
39669
  maxTtlSec,
39644
39670
  reverseAuthTtlSec,
39671
+ enforceTokenSubjectNodeIdentity,
39645
39672
  };
39646
39673
  }
39647
39674
  class OAuth2Authorizer {
@@ -39655,6 +39682,8 @@ class OAuth2Authorizer {
39655
39682
  this.requireScope = options.requireScope ?? true;
39656
39683
  this.reverseAuthTtlSec =
39657
39684
  options.reverseAuthTtlSec ?? DEFAULT_REVERSE_AUTH_TTL_SEC;
39685
+ this.enforceTokenSubjectNodeIdentity =
39686
+ options.enforceTokenSubjectNodeIdentity ?? false;
39658
39687
  }
39659
39688
  get tokenVerifier() {
39660
39689
  return this.tokenVerifierImpl;
@@ -39784,6 +39813,13 @@ class OAuth2Authorizer {
39784
39813
  });
39785
39814
  return undefined;
39786
39815
  }
39816
+ // Enforce token subject node identity if enabled
39817
+ if (this.enforceTokenSubjectNodeIdentity) {
39818
+ const validationResult = await this.validateTokenSubjectNodeIdentity(frame.systemId, claims);
39819
+ if (!validationResult) {
39820
+ return undefined;
39821
+ }
39822
+ }
39787
39823
  claims.instance_id = claims.instance_id ?? frame.instanceId;
39788
39824
  claims.assigned_path = claims.assigned_path ?? frame.assignedPath;
39789
39825
  claims.accepted_capabilities =
@@ -39861,6 +39897,33 @@ class OAuth2Authorizer {
39861
39897
  }
39862
39898
  return false;
39863
39899
  }
39900
+ async validateTokenSubjectNodeIdentity(systemId, claims) {
39901
+ const sub = claims.sub;
39902
+ if (typeof sub !== 'string' || sub.trim().length === 0) {
39903
+ logger$3.warning('oauth2_attach_missing_subject_claim', {
39904
+ system_id: systemId,
39905
+ });
39906
+ return false;
39907
+ }
39908
+ const expectedPrefix = await generateIdAsync({
39909
+ mode: 'fingerprint',
39910
+ material: sub,
39911
+ length: 8,
39912
+ });
39913
+ if (!systemId.startsWith(`${expectedPrefix}-`)) {
39914
+ logger$3.warning('oauth2_attach_node_identity_mismatch', {
39915
+ system_id: systemId,
39916
+ expected_prefix: expectedPrefix,
39917
+ subject: sub,
39918
+ });
39919
+ return false;
39920
+ }
39921
+ logger$3.debug('oauth2_attach_node_identity_verified', {
39922
+ system_id: systemId,
39923
+ expected_prefix: expectedPrefix,
39924
+ });
39925
+ return true;
39926
+ }
39864
39927
  }
39865
39928
 
39866
39929
  var oauth2Authorizer = /*#__PURE__*/Object.freeze({
@@ -4426,12 +4426,12 @@ async function ensureRuntimeFactoriesRegistered(registry = factory.Registry) {
4426
4426
  }
4427
4427
 
4428
4428
  // This file is auto-generated during build - do not edit manually
4429
- // Generated from package.json version: 0.3.16
4429
+ // Generated from package.json version: 0.3.18
4430
4430
  /**
4431
4431
  * The package version, injected at build time.
4432
4432
  * @internal
4433
4433
  */
4434
- const VERSION = '0.3.16';
4434
+ const VERSION = '0.3.18';
4435
4435
 
4436
4436
  let initialized = false;
4437
4437
  const runtimePlugin = {
@@ -29319,6 +29319,7 @@ const ENV_VAR_DEFAULT_ENCRYPTION_LEVEL = 'FAME_DEFAULT_ENCRYPTION_LEVEL';
29319
29319
  const ENV_VAR_HMAC_SECRET = 'FAME_HMAC_SECRET';
29320
29320
  const ENV_VAR_JWT_REVERSE_AUTH_TRUSTED_ISSUER = 'FAME_JWT_REVERSE_AUTH_TRUSTED_ISSUER';
29321
29321
  const ENV_VAR_JWT_REVERSE_AUTH_AUDIENCE = 'FAME_JWT_REVERSE_AUTH_AUDIENCE';
29322
+ const ENV_VAR_ENFORCE_TOKEN_SUBJECT_NODE_IDENTITY = 'FAME_ENFORCE_TOKEN_SUBJECT_NODE_IDENTITY';
29322
29323
  const PROFILE_NAME_STRICT_OVERLAY = 'strict-overlay';
29323
29324
  const PROFILE_NAME_OVERLAY = 'overlay';
29324
29325
  const PROFILE_NAME_OVERLAY_CALLBACK = 'overlay-callback';
@@ -29554,6 +29555,7 @@ const GATED_PROFILE = {
29554
29555
  max_ttl_sec: 86400,
29555
29556
  algorithm: factory.Expressions.env(ENV_VAR_JWT_ALGORITHM$2, 'RS256'),
29556
29557
  audience: factory.Expressions.env(ENV_VAR_JWT_AUDIENCE$2),
29558
+ enforce_token_subject_node_identity: factory.Expressions.env(ENV_VAR_ENFORCE_TOKEN_SUBJECT_NODE_IDENTITY, 'false'),
29557
29559
  },
29558
29560
  };
29559
29561
  const GATED_CALLBACK_PROFILE = {
@@ -29760,6 +29762,7 @@ function deepClone$3(value) {
29760
29762
  var nodeSecurityProfileFactory = /*#__PURE__*/Object.freeze({
29761
29763
  __proto__: null,
29762
29764
  ENV_VAR_DEFAULT_ENCRYPTION_LEVEL: ENV_VAR_DEFAULT_ENCRYPTION_LEVEL,
29765
+ ENV_VAR_ENFORCE_TOKEN_SUBJECT_NODE_IDENTITY: ENV_VAR_ENFORCE_TOKEN_SUBJECT_NODE_IDENTITY,
29763
29766
  ENV_VAR_HMAC_SECRET: ENV_VAR_HMAC_SECRET,
29764
29767
  ENV_VAR_JWKS_URL: ENV_VAR_JWKS_URL,
29765
29768
  ENV_VAR_JWT_ALGORITHM: ENV_VAR_JWT_ALGORITHM$2,
@@ -38001,6 +38004,7 @@ class OAuth2AuthorizerFactory extends AuthorizerFactory {
38001
38004
  defaultTtlSec: normalized.defaultTtlSec,
38002
38005
  maxTtlSec: normalized.maxTtlSec,
38003
38006
  reverseAuthTtlSec: normalized.reverseAuthTtlSec,
38007
+ enforceTokenSubjectNodeIdentity: normalized.enforceTokenSubjectNodeIdentity,
38004
38008
  };
38005
38009
  if (tokenIssuer) {
38006
38010
  authorizerOptions.tokenIssuer = tokenIssuer;
@@ -38068,6 +38072,8 @@ function normalizeConfig$c(config) {
38068
38072
  : typeof source.reverse_auth_ttl_sec === 'number'
38069
38073
  ? source.reverse_auth_ttl_sec
38070
38074
  : DEFAULT_REVERSE_AUTH_TTL_SEC;
38075
+ const enforceTokenSubjectNodeIdentity = normalizeBooleanOption(source.enforceTokenSubjectNodeIdentity ??
38076
+ source.enforce_token_subject_node_identity, false);
38071
38077
  const tokenVerifierConfigInput = source.tokenVerifierConfig ?? source.token_verifier_config ?? null;
38072
38078
  const tokenVerifierConfig = normalizeTokenVerifierConfig({
38073
38079
  config: tokenVerifierConfigInput,
@@ -38086,6 +38092,7 @@ function normalizeConfig$c(config) {
38086
38092
  maxTtlSec,
38087
38093
  tokenVerifierConfig,
38088
38094
  reverseAuthTtlSec: reverseAuthCandidate,
38095
+ enforceTokenSubjectNodeIdentity,
38089
38096
  ...(audience ? { audience } : {}),
38090
38097
  };
38091
38098
  if (tokenIssuerConfig) {
@@ -38106,6 +38113,21 @@ function normalizeTokenVerifierConfig({ config, issuer, jwksUrl, algorithm, }) {
38106
38113
  };
38107
38114
  return defaultConfig;
38108
38115
  }
38116
+ function normalizeBooleanOption(value, defaultValue) {
38117
+ if (typeof value === 'boolean') {
38118
+ return value;
38119
+ }
38120
+ if (typeof value === 'string') {
38121
+ const lower = value.toLowerCase().trim();
38122
+ if (lower === 'true' || lower === '1' || lower === 'yes') {
38123
+ return true;
38124
+ }
38125
+ if (lower === 'false' || lower === '0' || lower === 'no') {
38126
+ return false;
38127
+ }
38128
+ }
38129
+ return defaultValue;
38130
+ }
38109
38131
 
38110
38132
  var oauth2AuthorizerFactory = /*#__PURE__*/Object.freeze({
38111
38133
  __proto__: null,
@@ -41968,6 +41990,10 @@ function normalizeOptions$4(raw) {
41968
41990
  : typeof snake.aud === 'string'
41969
41991
  ? snake.aud
41970
41992
  : undefined);
41993
+ const enforceTokenSubjectNodeIdentity = camel.enforceTokenSubjectNodeIdentity ??
41994
+ (typeof snake.enforce_token_subject_node_identity === 'boolean'
41995
+ ? snake.enforce_token_subject_node_identity
41996
+ : undefined);
41971
41997
  return {
41972
41998
  tokenVerifier,
41973
41999
  tokenIssuer,
@@ -41977,6 +42003,7 @@ function normalizeOptions$4(raw) {
41977
42003
  defaultTtlSec,
41978
42004
  maxTtlSec,
41979
42005
  reverseAuthTtlSec,
42006
+ enforceTokenSubjectNodeIdentity,
41980
42007
  };
41981
42008
  }
41982
42009
  class OAuth2Authorizer {
@@ -41990,6 +42017,8 @@ class OAuth2Authorizer {
41990
42017
  this.requireScope = options.requireScope ?? true;
41991
42018
  this.reverseAuthTtlSec =
41992
42019
  options.reverseAuthTtlSec ?? DEFAULT_REVERSE_AUTH_TTL_SEC;
42020
+ this.enforceTokenSubjectNodeIdentity =
42021
+ options.enforceTokenSubjectNodeIdentity ?? false;
41993
42022
  }
41994
42023
  get tokenVerifier() {
41995
42024
  return this.tokenVerifierImpl;
@@ -42119,6 +42148,13 @@ class OAuth2Authorizer {
42119
42148
  });
42120
42149
  return undefined;
42121
42150
  }
42151
+ // Enforce token subject node identity if enabled
42152
+ if (this.enforceTokenSubjectNodeIdentity) {
42153
+ const validationResult = await this.validateTokenSubjectNodeIdentity(frame.systemId, claims);
42154
+ if (!validationResult) {
42155
+ return undefined;
42156
+ }
42157
+ }
42122
42158
  claims.instance_id = claims.instance_id ?? frame.instanceId;
42123
42159
  claims.assigned_path = claims.assigned_path ?? frame.assignedPath;
42124
42160
  claims.accepted_capabilities =
@@ -42196,6 +42232,33 @@ class OAuth2Authorizer {
42196
42232
  }
42197
42233
  return false;
42198
42234
  }
42235
+ async validateTokenSubjectNodeIdentity(systemId, claims) {
42236
+ const sub = claims.sub;
42237
+ if (typeof sub !== 'string' || sub.trim().length === 0) {
42238
+ logger$3.warning('oauth2_attach_missing_subject_claim', {
42239
+ system_id: systemId,
42240
+ });
42241
+ return false;
42242
+ }
42243
+ const expectedPrefix = await core.generateIdAsync({
42244
+ mode: 'fingerprint',
42245
+ material: sub,
42246
+ length: 8,
42247
+ });
42248
+ if (!systemId.startsWith(`${expectedPrefix}-`)) {
42249
+ logger$3.warning('oauth2_attach_node_identity_mismatch', {
42250
+ system_id: systemId,
42251
+ expected_prefix: expectedPrefix,
42252
+ subject: sub,
42253
+ });
42254
+ return false;
42255
+ }
42256
+ logger$3.debug('oauth2_attach_node_identity_verified', {
42257
+ system_id: systemId,
42258
+ expected_prefix: expectedPrefix,
42259
+ });
42260
+ return true;
42261
+ }
42199
42262
  }
42200
42263
 
42201
42264
  var oauth2Authorizer = /*#__PURE__*/Object.freeze({
@@ -4425,12 +4425,12 @@ async function ensureRuntimeFactoriesRegistered(registry = Registry) {
4425
4425
  }
4426
4426
 
4427
4427
  // This file is auto-generated during build - do not edit manually
4428
- // Generated from package.json version: 0.3.16
4428
+ // Generated from package.json version: 0.3.18
4429
4429
  /**
4430
4430
  * The package version, injected at build time.
4431
4431
  * @internal
4432
4432
  */
4433
- const VERSION = '0.3.16';
4433
+ const VERSION = '0.3.18';
4434
4434
 
4435
4435
  let initialized = false;
4436
4436
  const runtimePlugin = {
@@ -29318,6 +29318,7 @@ const ENV_VAR_DEFAULT_ENCRYPTION_LEVEL = 'FAME_DEFAULT_ENCRYPTION_LEVEL';
29318
29318
  const ENV_VAR_HMAC_SECRET = 'FAME_HMAC_SECRET';
29319
29319
  const ENV_VAR_JWT_REVERSE_AUTH_TRUSTED_ISSUER = 'FAME_JWT_REVERSE_AUTH_TRUSTED_ISSUER';
29320
29320
  const ENV_VAR_JWT_REVERSE_AUTH_AUDIENCE = 'FAME_JWT_REVERSE_AUTH_AUDIENCE';
29321
+ const ENV_VAR_ENFORCE_TOKEN_SUBJECT_NODE_IDENTITY = 'FAME_ENFORCE_TOKEN_SUBJECT_NODE_IDENTITY';
29321
29322
  const PROFILE_NAME_STRICT_OVERLAY = 'strict-overlay';
29322
29323
  const PROFILE_NAME_OVERLAY = 'overlay';
29323
29324
  const PROFILE_NAME_OVERLAY_CALLBACK = 'overlay-callback';
@@ -29553,6 +29554,7 @@ const GATED_PROFILE = {
29553
29554
  max_ttl_sec: 86400,
29554
29555
  algorithm: Expressions.env(ENV_VAR_JWT_ALGORITHM$2, 'RS256'),
29555
29556
  audience: Expressions.env(ENV_VAR_JWT_AUDIENCE$2),
29557
+ enforce_token_subject_node_identity: Expressions.env(ENV_VAR_ENFORCE_TOKEN_SUBJECT_NODE_IDENTITY, 'false'),
29556
29558
  },
29557
29559
  };
29558
29560
  const GATED_CALLBACK_PROFILE = {
@@ -29759,6 +29761,7 @@ function deepClone$3(value) {
29759
29761
  var nodeSecurityProfileFactory = /*#__PURE__*/Object.freeze({
29760
29762
  __proto__: null,
29761
29763
  ENV_VAR_DEFAULT_ENCRYPTION_LEVEL: ENV_VAR_DEFAULT_ENCRYPTION_LEVEL,
29764
+ ENV_VAR_ENFORCE_TOKEN_SUBJECT_NODE_IDENTITY: ENV_VAR_ENFORCE_TOKEN_SUBJECT_NODE_IDENTITY,
29762
29765
  ENV_VAR_HMAC_SECRET: ENV_VAR_HMAC_SECRET,
29763
29766
  ENV_VAR_JWKS_URL: ENV_VAR_JWKS_URL,
29764
29767
  ENV_VAR_JWT_ALGORITHM: ENV_VAR_JWT_ALGORITHM$2,
@@ -38000,6 +38003,7 @@ class OAuth2AuthorizerFactory extends AuthorizerFactory {
38000
38003
  defaultTtlSec: normalized.defaultTtlSec,
38001
38004
  maxTtlSec: normalized.maxTtlSec,
38002
38005
  reverseAuthTtlSec: normalized.reverseAuthTtlSec,
38006
+ enforceTokenSubjectNodeIdentity: normalized.enforceTokenSubjectNodeIdentity,
38003
38007
  };
38004
38008
  if (tokenIssuer) {
38005
38009
  authorizerOptions.tokenIssuer = tokenIssuer;
@@ -38067,6 +38071,8 @@ function normalizeConfig$c(config) {
38067
38071
  : typeof source.reverse_auth_ttl_sec === 'number'
38068
38072
  ? source.reverse_auth_ttl_sec
38069
38073
  : DEFAULT_REVERSE_AUTH_TTL_SEC;
38074
+ const enforceTokenSubjectNodeIdentity = normalizeBooleanOption(source.enforceTokenSubjectNodeIdentity ??
38075
+ source.enforce_token_subject_node_identity, false);
38070
38076
  const tokenVerifierConfigInput = source.tokenVerifierConfig ?? source.token_verifier_config ?? null;
38071
38077
  const tokenVerifierConfig = normalizeTokenVerifierConfig({
38072
38078
  config: tokenVerifierConfigInput,
@@ -38085,6 +38091,7 @@ function normalizeConfig$c(config) {
38085
38091
  maxTtlSec,
38086
38092
  tokenVerifierConfig,
38087
38093
  reverseAuthTtlSec: reverseAuthCandidate,
38094
+ enforceTokenSubjectNodeIdentity,
38088
38095
  ...(audience ? { audience } : {}),
38089
38096
  };
38090
38097
  if (tokenIssuerConfig) {
@@ -38105,6 +38112,21 @@ function normalizeTokenVerifierConfig({ config, issuer, jwksUrl, algorithm, }) {
38105
38112
  };
38106
38113
  return defaultConfig;
38107
38114
  }
38115
+ function normalizeBooleanOption(value, defaultValue) {
38116
+ if (typeof value === 'boolean') {
38117
+ return value;
38118
+ }
38119
+ if (typeof value === 'string') {
38120
+ const lower = value.toLowerCase().trim();
38121
+ if (lower === 'true' || lower === '1' || lower === 'yes') {
38122
+ return true;
38123
+ }
38124
+ if (lower === 'false' || lower === '0' || lower === 'no') {
38125
+ return false;
38126
+ }
38127
+ }
38128
+ return defaultValue;
38129
+ }
38108
38130
 
38109
38131
  var oauth2AuthorizerFactory = /*#__PURE__*/Object.freeze({
38110
38132
  __proto__: null,
@@ -41967,6 +41989,10 @@ function normalizeOptions$4(raw) {
41967
41989
  : typeof snake.aud === 'string'
41968
41990
  ? snake.aud
41969
41991
  : undefined);
41992
+ const enforceTokenSubjectNodeIdentity = camel.enforceTokenSubjectNodeIdentity ??
41993
+ (typeof snake.enforce_token_subject_node_identity === 'boolean'
41994
+ ? snake.enforce_token_subject_node_identity
41995
+ : undefined);
41970
41996
  return {
41971
41997
  tokenVerifier,
41972
41998
  tokenIssuer,
@@ -41976,6 +42002,7 @@ function normalizeOptions$4(raw) {
41976
42002
  defaultTtlSec,
41977
42003
  maxTtlSec,
41978
42004
  reverseAuthTtlSec,
42005
+ enforceTokenSubjectNodeIdentity,
41979
42006
  };
41980
42007
  }
41981
42008
  class OAuth2Authorizer {
@@ -41989,6 +42016,8 @@ class OAuth2Authorizer {
41989
42016
  this.requireScope = options.requireScope ?? true;
41990
42017
  this.reverseAuthTtlSec =
41991
42018
  options.reverseAuthTtlSec ?? DEFAULT_REVERSE_AUTH_TTL_SEC;
42019
+ this.enforceTokenSubjectNodeIdentity =
42020
+ options.enforceTokenSubjectNodeIdentity ?? false;
41992
42021
  }
41993
42022
  get tokenVerifier() {
41994
42023
  return this.tokenVerifierImpl;
@@ -42118,6 +42147,13 @@ class OAuth2Authorizer {
42118
42147
  });
42119
42148
  return undefined;
42120
42149
  }
42150
+ // Enforce token subject node identity if enabled
42151
+ if (this.enforceTokenSubjectNodeIdentity) {
42152
+ const validationResult = await this.validateTokenSubjectNodeIdentity(frame.systemId, claims);
42153
+ if (!validationResult) {
42154
+ return undefined;
42155
+ }
42156
+ }
42121
42157
  claims.instance_id = claims.instance_id ?? frame.instanceId;
42122
42158
  claims.assigned_path = claims.assigned_path ?? frame.assignedPath;
42123
42159
  claims.accepted_capabilities =
@@ -42195,6 +42231,33 @@ class OAuth2Authorizer {
42195
42231
  }
42196
42232
  return false;
42197
42233
  }
42234
+ async validateTokenSubjectNodeIdentity(systemId, claims) {
42235
+ const sub = claims.sub;
42236
+ if (typeof sub !== 'string' || sub.trim().length === 0) {
42237
+ logger$3.warning('oauth2_attach_missing_subject_claim', {
42238
+ system_id: systemId,
42239
+ });
42240
+ return false;
42241
+ }
42242
+ const expectedPrefix = await generateIdAsync({
42243
+ mode: 'fingerprint',
42244
+ material: sub,
42245
+ length: 8,
42246
+ });
42247
+ if (!systemId.startsWith(`${expectedPrefix}-`)) {
42248
+ logger$3.warning('oauth2_attach_node_identity_mismatch', {
42249
+ system_id: systemId,
42250
+ expected_prefix: expectedPrefix,
42251
+ subject: sub,
42252
+ });
42253
+ return false;
42254
+ }
42255
+ logger$3.debug('oauth2_attach_node_identity_verified', {
42256
+ system_id: systemId,
42257
+ expected_prefix: expectedPrefix,
42258
+ });
42259
+ return true;
42260
+ }
42198
42261
  }
42199
42262
 
42200
42263
  var oauth2Authorizer = /*#__PURE__*/Object.freeze({
@@ -23,6 +23,8 @@ export interface OAuth2AuthorizerConfig extends AuthorizerConfig {
23
23
  token_issuer_config?: TokenIssuerConfig;
24
24
  reverseAuthTtlSec?: number;
25
25
  reverse_auth_ttl_sec?: number;
26
+ enforceTokenSubjectNodeIdentity?: boolean;
27
+ enforce_token_subject_node_identity?: boolean;
26
28
  }
27
29
  export declare const FACTORY_META: {
28
30
  readonly base: "AuthorizerFactory";
@@ -14,6 +14,7 @@ export interface OAuth2AuthorizerOptions {
14
14
  defaultTtlSec?: number;
15
15
  maxTtlSec?: number;
16
16
  reverseAuthTtlSec?: number;
17
+ enforceTokenSubjectNodeIdentity?: boolean;
17
18
  }
18
19
  export declare class OAuth2Authorizer implements Authorizer, TokenVerifierProvider, NodeEventListener {
19
20
  readonly priority = 1000;
@@ -23,6 +24,7 @@ export declare class OAuth2Authorizer implements Authorizer, TokenVerifierProvid
23
24
  private readonly requiredScopes;
24
25
  private readonly requireScope;
25
26
  private readonly reverseAuthTtlSec;
27
+ private readonly enforceTokenSubjectNodeIdentity;
26
28
  private node?;
27
29
  constructor(rawOptions: OAuth2AuthorizerOptions | Record<string, unknown>);
28
30
  get tokenVerifier(): TokenVerifier;
@@ -35,4 +37,5 @@ export declare class OAuth2Authorizer implements Authorizer, TokenVerifierProvid
35
37
  private extractScopes;
36
38
  private mergeScopes;
37
39
  private hasRequiredScope;
40
+ private validateTokenSubjectNodeIdentity;
38
41
  }
@@ -10,6 +10,7 @@ export declare const ENV_VAR_DEFAULT_ENCRYPTION_LEVEL = "FAME_DEFAULT_ENCRYPTION
10
10
  export declare const ENV_VAR_HMAC_SECRET = "FAME_HMAC_SECRET";
11
11
  export declare const ENV_VAR_JWT_REVERSE_AUTH_TRUSTED_ISSUER = "FAME_JWT_REVERSE_AUTH_TRUSTED_ISSUER";
12
12
  export declare const ENV_VAR_JWT_REVERSE_AUTH_AUDIENCE = "FAME_JWT_REVERSE_AUTH_AUDIENCE";
13
+ export declare const ENV_VAR_ENFORCE_TOKEN_SUBJECT_NODE_IDENTITY = "FAME_ENFORCE_TOKEN_SUBJECT_NODE_IDENTITY";
13
14
  export declare const PROFILE_NAME_STRICT_OVERLAY = "strict-overlay";
14
15
  export declare const PROFILE_NAME_OVERLAY = "overlay";
15
16
  export declare const PROFILE_NAME_OVERLAY_CALLBACK = "overlay-callback";
@@ -2,4 +2,4 @@
2
2
  * The package version, injected at build time.
3
3
  * @internal
4
4
  */
5
- export declare const VERSION = "0.3.16";
5
+ export declare const VERSION = "0.3.18";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@naylence/runtime",
3
- "version": "0.3.16",
3
+ "version": "0.3.18",
4
4
  "type": "module",
5
5
  "description": "Naylence Runtime - Complete TypeScript runtime",
6
6
  "author": "Naylence Dev <naylencedev@gmail.com>",