@naylence/runtime 0.3.12 → 0.3.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/index.cjs +1479 -926
- package/dist/browser/index.mjs +1472 -927
- package/dist/cjs/naylence/fame/connector/broadcast-channel-connector.browser.js +1 -1
- package/dist/cjs/naylence/fame/factory-manifest.js +6 -0
- package/dist/cjs/naylence/fame/grants/grant-materializer.js +59 -0
- package/dist/cjs/naylence/fame/node/admission/admission-profile-factory.js +4 -2
- package/dist/cjs/naylence/fame/node/admission/direct-admission-client-factory.js +3 -1
- package/dist/cjs/naylence/fame/node/admission/direct-admission-client.js +12 -9
- package/dist/cjs/naylence/fame/node/default-node-identity-policy-factory.js +21 -0
- package/dist/cjs/naylence/fame/node/default-node-identity-policy.js +60 -0
- package/dist/cjs/naylence/fame/node/factory-commons.js +31 -7
- package/dist/cjs/naylence/fame/node/index.js +11 -1
- package/dist/cjs/naylence/fame/node/node-config.js +4 -0
- package/dist/cjs/naylence/fame/node/node-identity-policy-factory.js +22 -0
- package/dist/cjs/naylence/fame/node/node-identity-policy-profile-factory.js +67 -0
- package/dist/cjs/naylence/fame/node/node-identity-policy.js +2 -0
- package/dist/cjs/naylence/fame/node/node.js +45 -9
- package/dist/cjs/naylence/fame/node/root-session-manager.js +1 -11
- package/dist/cjs/naylence/fame/node/rpc-client-manager.js +10 -3
- package/dist/cjs/naylence/fame/node/token-subject-node-identity-policy-factory.js +55 -0
- package/dist/cjs/naylence/fame/node/token-subject-node-identity-policy.js +84 -0
- package/dist/cjs/naylence/fame/node/upstream-session-manager.js +87 -9
- package/dist/cjs/naylence/fame/security/auth/auth-identity.js +2 -0
- package/dist/cjs/naylence/fame/security/auth/materializable-token-provider.js +9 -0
- package/dist/cjs/naylence/fame/security/auth/oauth2-pkce-token-provider.js +9 -0
- package/dist/cjs/naylence/fame/security/auth/static-token-provider.js +44 -0
- package/dist/cjs/naylence/fame/security/auth/token-provider.js +6 -0
- package/dist/cjs/naylence/fame/security/default-security-manager.js +4 -2
- package/dist/cjs/naylence/fame/security/index.js +1 -0
- package/dist/cjs/naylence/fame/security/keys/default-key-manager.js +1 -1
- package/dist/cjs/naylence/fame/util/task-spawner.js +8 -0
- package/dist/cjs/version.js +2 -2
- package/dist/esm/naylence/fame/connector/broadcast-channel-connector.browser.js +1 -1
- package/dist/esm/naylence/fame/factory-manifest.js +6 -0
- package/dist/esm/naylence/fame/grants/grant-materializer.js +55 -0
- package/dist/esm/naylence/fame/node/admission/admission-profile-factory.js +4 -2
- package/dist/esm/naylence/fame/node/admission/direct-admission-client-factory.js +3 -1
- package/dist/esm/naylence/fame/node/admission/direct-admission-client.js +13 -10
- package/dist/esm/naylence/fame/node/default-node-identity-policy-factory.js +17 -0
- package/dist/esm/naylence/fame/node/default-node-identity-policy.js +56 -0
- package/dist/esm/naylence/fame/node/factory-commons.js +31 -7
- package/dist/esm/naylence/fame/node/index.js +7 -0
- package/dist/esm/naylence/fame/node/node-config.js +4 -0
- package/dist/esm/naylence/fame/node/node-identity-policy-factory.js +18 -0
- package/dist/esm/naylence/fame/node/node-identity-policy-profile-factory.js +63 -0
- package/dist/esm/naylence/fame/node/node-identity-policy.js +1 -0
- package/dist/esm/naylence/fame/node/node.js +45 -9
- package/dist/esm/naylence/fame/node/root-session-manager.js +1 -11
- package/dist/esm/naylence/fame/node/rpc-client-manager.js +10 -3
- package/dist/esm/naylence/fame/node/token-subject-node-identity-policy-factory.js +18 -0
- package/dist/esm/naylence/fame/node/token-subject-node-identity-policy.js +80 -0
- package/dist/esm/naylence/fame/node/upstream-session-manager.js +87 -9
- package/dist/esm/naylence/fame/security/auth/auth-identity.js +1 -0
- package/dist/esm/naylence/fame/security/auth/materializable-token-provider.js +6 -0
- package/dist/esm/naylence/fame/security/auth/oauth2-pkce-token-provider.js +9 -0
- package/dist/esm/naylence/fame/security/auth/static-token-provider.js +44 -0
- package/dist/esm/naylence/fame/security/auth/token-provider.js +5 -0
- package/dist/esm/naylence/fame/security/default-security-manager.js +4 -2
- package/dist/esm/naylence/fame/security/index.js +1 -0
- package/dist/esm/naylence/fame/security/keys/default-key-manager.js +1 -1
- package/dist/esm/naylence/fame/util/task-spawner.js +8 -0
- package/dist/esm/version.js +2 -2
- package/dist/node/index.cjs +1432 -879
- package/dist/node/index.mjs +1425 -880
- package/dist/node/node.cjs +1560 -1007
- package/dist/node/node.mjs +1553 -1008
- package/dist/types/naylence/fame/factory-manifest.d.ts +1 -1
- package/dist/types/naylence/fame/grants/grant-materializer.d.ts +4 -0
- package/dist/types/naylence/fame/node/admission/admission-profile-factory.d.ts +1 -1
- package/dist/types/naylence/fame/node/admission/direct-admission-client-factory.d.ts +1 -1
- package/dist/types/naylence/fame/node/admission/direct-admission-client.d.ts +3 -0
- package/dist/types/naylence/fame/node/default-node-identity-policy-factory.d.ts +15 -0
- package/dist/types/naylence/fame/node/default-node-identity-policy.d.ts +5 -0
- package/dist/types/naylence/fame/node/factory-commons.d.ts +2 -0
- package/dist/types/naylence/fame/node/index.d.ts +7 -0
- package/dist/types/naylence/fame/node/node-config.d.ts +2 -0
- package/dist/types/naylence/fame/node/node-identity-policy-factory.d.ts +12 -0
- package/dist/types/naylence/fame/node/node-identity-policy-profile-factory.d.ts +15 -0
- package/dist/types/naylence/fame/node/node-identity-policy.d.ts +26 -0
- package/dist/types/naylence/fame/node/node-like.d.ts +3 -1
- package/dist/types/naylence/fame/node/node.d.ts +4 -1
- package/dist/types/naylence/fame/node/root-session-manager.d.ts +0 -1
- package/dist/types/naylence/fame/node/rpc-client-manager.d.ts +2 -0
- package/dist/types/naylence/fame/node/token-subject-node-identity-policy-factory.d.ts +14 -0
- package/dist/types/naylence/fame/node/token-subject-node-identity-policy.d.ts +5 -0
- package/dist/types/naylence/fame/node/upstream-session-manager.d.ts +4 -0
- package/dist/types/naylence/fame/security/auth/auth-identity.d.ts +6 -0
- package/dist/types/naylence/fame/security/auth/materializable-token-provider.d.ts +12 -0
- package/dist/types/naylence/fame/security/auth/oauth2-pkce-token-provider.d.ts +4 -2
- package/dist/types/naylence/fame/security/auth/static-token-provider.d.ts +4 -2
- package/dist/types/naylence/fame/security/auth/token-provider.d.ts +5 -0
- package/dist/types/naylence/fame/security/index.d.ts +1 -0
- package/dist/types/version.d.ts +1 -1
- package/package.json +1 -1
|
@@ -102,6 +102,8 @@ class RPCClientManager {
|
|
|
102
102
|
this.rpcBound = false;
|
|
103
103
|
this.trackerEventHandler = null;
|
|
104
104
|
this.trackerWithEvents = null;
|
|
105
|
+
this.boundPhysicalPath = null;
|
|
106
|
+
this.rpcRecipient = null;
|
|
105
107
|
this.setupTrackerEventHandler();
|
|
106
108
|
}
|
|
107
109
|
setupTrackerEventHandler() {
|
|
@@ -253,6 +255,8 @@ class RPCClientManager {
|
|
|
253
255
|
this.rpcBound = false;
|
|
254
256
|
this.rpcReplyAddress = null;
|
|
255
257
|
this.rpcListenerAddress = null;
|
|
258
|
+
this.boundPhysicalPath = null;
|
|
259
|
+
this.rpcRecipient = null;
|
|
256
260
|
for (const [requestId, pending] of Array.from(this.pending.entries())) {
|
|
257
261
|
if (pending.timer) {
|
|
258
262
|
clearTimeout(pending.timer);
|
|
@@ -270,17 +274,20 @@ class RPCClientManager {
|
|
|
270
274
|
this.pendingByEnvelopeId.clear();
|
|
271
275
|
}
|
|
272
276
|
async ensureReplyListener() {
|
|
273
|
-
|
|
277
|
+
const currentPhysicalPath = this.getPhysicalPath();
|
|
278
|
+
if (this.rpcBound && this.boundPhysicalPath === currentPhysicalPath) {
|
|
274
279
|
return;
|
|
275
280
|
}
|
|
276
|
-
const recipient = `__rpc__${(0, core_1.generateId)()}`;
|
|
277
|
-
this.
|
|
281
|
+
const recipient = this.rpcRecipient || `__rpc__${(0, core_1.generateId)()}`;
|
|
282
|
+
this.rpcRecipient = recipient;
|
|
283
|
+
this.rpcReplyAddress = (0, core_1.formatAddress)(recipient, currentPhysicalPath);
|
|
278
284
|
const handler = async (envelope, _context) => {
|
|
279
285
|
await this.handleReplyEnvelope(envelope);
|
|
280
286
|
return null;
|
|
281
287
|
};
|
|
282
288
|
this.rpcListenerAddress = await this.listenCallback(recipient, handler);
|
|
283
289
|
this.rpcBound = true;
|
|
290
|
+
this.boundPhysicalPath = currentPhysicalPath;
|
|
284
291
|
logger.debug('rpc_reply_listener_bound', {
|
|
285
292
|
reply_recipient: recipient,
|
|
286
293
|
reply_address: this.rpcReplyAddress?.toString(),
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.TokenSubjectNodeIdentityPolicyFactory = exports.FACTORY_META = void 0;
|
|
37
|
+
const node_identity_policy_factory_js_1 = require("./node-identity-policy-factory.js");
|
|
38
|
+
exports.FACTORY_META = {
|
|
39
|
+
base: node_identity_policy_factory_js_1.NODE_IDENTITY_POLICY_FACTORY_BASE_TYPE,
|
|
40
|
+
key: 'TokenSubjectNodeIdentityPolicy',
|
|
41
|
+
};
|
|
42
|
+
class TokenSubjectNodeIdentityPolicyFactory extends node_identity_policy_factory_js_1.NodeIdentityPolicyFactory {
|
|
43
|
+
constructor() {
|
|
44
|
+
super(...arguments);
|
|
45
|
+
this.type = 'TokenSubjectNodeIdentityPolicy';
|
|
46
|
+
}
|
|
47
|
+
async create(
|
|
48
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
49
|
+
_config) {
|
|
50
|
+
const { TokenSubjectNodeIdentityPolicy } = await Promise.resolve().then(() => __importStar(require('./token-subject-node-identity-policy.js')));
|
|
51
|
+
return new TokenSubjectNodeIdentityPolicy();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
exports.TokenSubjectNodeIdentityPolicyFactory = TokenSubjectNodeIdentityPolicyFactory;
|
|
55
|
+
exports.default = TokenSubjectNodeIdentityPolicyFactory;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TokenSubjectNodeIdentityPolicy = void 0;
|
|
4
|
+
const core_1 = require("@naylence/core");
|
|
5
|
+
const token_provider_factory_js_1 = require("../security/auth/token-provider-factory.js");
|
|
6
|
+
const token_provider_js_1 = require("../security/auth/token-provider.js");
|
|
7
|
+
const logging_js_1 = require("../util/logging.js");
|
|
8
|
+
const logger = (0, logging_js_1.getLogger)('naylence.fame.node.token_subject_node_identity_policy');
|
|
9
|
+
class TokenSubjectNodeIdentityPolicy {
|
|
10
|
+
async resolveInitialNodeId(context) {
|
|
11
|
+
if (context.configuredId) {
|
|
12
|
+
return context.configuredId;
|
|
13
|
+
}
|
|
14
|
+
if (context.persistedId) {
|
|
15
|
+
return context.persistedId;
|
|
16
|
+
}
|
|
17
|
+
return (0, core_1.generateIdAsync)();
|
|
18
|
+
}
|
|
19
|
+
async resolveAdmissionNodeId(context) {
|
|
20
|
+
logger.debug('resolve_admission_node_id_start', {
|
|
21
|
+
grantsCount: context.grants?.length ?? 0,
|
|
22
|
+
currentNodeId: context.currentNodeId,
|
|
23
|
+
});
|
|
24
|
+
if (context.grants && context.grants.length > 0) {
|
|
25
|
+
for (const grant of context.grants) {
|
|
26
|
+
try {
|
|
27
|
+
const auth = grant.auth;
|
|
28
|
+
if (!auth) {
|
|
29
|
+
logger.debug('skipping_grant_no_auth', { grantType: grant.type });
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
const tokenProviderConfig = (auth.tokenProvider ??
|
|
33
|
+
auth.token_provider);
|
|
34
|
+
if (!tokenProviderConfig ||
|
|
35
|
+
typeof tokenProviderConfig.type !== 'string') {
|
|
36
|
+
logger.debug('skipping_grant_invalid_token_provider_config', {
|
|
37
|
+
grantType: grant.type,
|
|
38
|
+
config: tokenProviderConfig,
|
|
39
|
+
});
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
logger.debug('creating_token_provider', {
|
|
43
|
+
type: tokenProviderConfig.type,
|
|
44
|
+
});
|
|
45
|
+
const provider = await token_provider_factory_js_1.TokenProviderFactory.createTokenProvider(tokenProviderConfig);
|
|
46
|
+
const isExposing = (0, token_provider_js_1.isIdentityExposingTokenProvider)(provider);
|
|
47
|
+
logger.debug('token_provider_created', {
|
|
48
|
+
type: tokenProviderConfig.type,
|
|
49
|
+
isIdentityExposing: isExposing,
|
|
50
|
+
});
|
|
51
|
+
if (isExposing) {
|
|
52
|
+
const identity = await provider.getIdentity();
|
|
53
|
+
logger.debug('retrieved_identity', { identity });
|
|
54
|
+
if (identity && identity.subject) {
|
|
55
|
+
const hashedSubject = await (0, core_1.generateIdAsync)({
|
|
56
|
+
mode: 'fingerprint',
|
|
57
|
+
material: identity.subject,
|
|
58
|
+
length: 8,
|
|
59
|
+
});
|
|
60
|
+
const newNodeId = `${hashedSubject}-${context.currentNodeId}`;
|
|
61
|
+
logger.info('resolved_identity_from_token', {
|
|
62
|
+
subject: identity.subject,
|
|
63
|
+
hashedSubject,
|
|
64
|
+
newNodeId,
|
|
65
|
+
});
|
|
66
|
+
return newNodeId;
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
logger.debug('identity_missing_subject', { identity });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
logger.warning('failed_to_extract_identity_from_grant', { error: err });
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
logger.debug('no_grants_available');
|
|
80
|
+
}
|
|
81
|
+
return context.currentNodeId;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
exports.TokenSubjectNodeIdentityPolicy = TokenSubjectNodeIdentityPolicy;
|
|
@@ -103,6 +103,7 @@ class UpstreamSessionManager extends task_spawner_js_1.TaskSpawner {
|
|
|
103
103
|
super();
|
|
104
104
|
this.readyEvent = new async_event_js_1.AsyncEvent();
|
|
105
105
|
this.stopEvent = new async_event_js_1.AsyncEvent();
|
|
106
|
+
this.wakeEvent = new async_event_js_1.AsyncEvent();
|
|
106
107
|
this.queueEvent = new async_event_js_1.AsyncEvent();
|
|
107
108
|
this.currentStopSubtasks = null;
|
|
108
109
|
this.messageQueue = [];
|
|
@@ -114,6 +115,7 @@ class UpstreamSessionManager extends task_spawner_js_1.TaskSpawner {
|
|
|
114
115
|
this.hadSuccessfulAttach = false;
|
|
115
116
|
this.lastConnectorState = null;
|
|
116
117
|
this.connectEpoch = 0;
|
|
118
|
+
this._visibilityHandler = null;
|
|
117
119
|
const options = normalizeOptions(optionsInput);
|
|
118
120
|
this.node = options.node;
|
|
119
121
|
this.attachClient = options.attachClient;
|
|
@@ -133,6 +135,34 @@ class UpstreamSessionManager extends task_spawner_js_1.TaskSpawner {
|
|
|
133
135
|
get systemId() {
|
|
134
136
|
return this.targetSystemId;
|
|
135
137
|
}
|
|
138
|
+
setupVisibilityListener() {
|
|
139
|
+
logger.debug('setup_visibility_listener_called', {
|
|
140
|
+
has_document: typeof document !== 'undefined',
|
|
141
|
+
});
|
|
142
|
+
if (typeof document !== 'undefined' && document.addEventListener) {
|
|
143
|
+
this._visibilityHandler = () => {
|
|
144
|
+
logger.debug('visibility_change_event_fired', {
|
|
145
|
+
state: document.visibilityState,
|
|
146
|
+
});
|
|
147
|
+
if (document.visibilityState === 'visible') {
|
|
148
|
+
logger.debug('visibility_change_detected_waking_up');
|
|
149
|
+
this.wakeEvent.set();
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
document.addEventListener('visibilitychange', this._visibilityHandler);
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
logger.debug('setup_visibility_listener_skipped_no_document');
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
teardownVisibilityListener() {
|
|
159
|
+
if (this._visibilityHandler &&
|
|
160
|
+
typeof document !== 'undefined' &&
|
|
161
|
+
document.removeEventListener) {
|
|
162
|
+
document.removeEventListener('visibilitychange', this._visibilityHandler);
|
|
163
|
+
this._visibilityHandler = null;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
136
166
|
async start(options = {}) {
|
|
137
167
|
const { waitUntilReady = true } = options;
|
|
138
168
|
if (this.fsmTask) {
|
|
@@ -140,6 +170,8 @@ class UpstreamSessionManager extends task_spawner_js_1.TaskSpawner {
|
|
|
140
170
|
}
|
|
141
171
|
this.stopEvent.clear();
|
|
142
172
|
this.readyEvent.clear();
|
|
173
|
+
this.wakeEvent.clear();
|
|
174
|
+
this.setupVisibilityListener();
|
|
143
175
|
const taskName = `upstream-fsm-${this.connectEpoch}`;
|
|
144
176
|
this.fsmTask = this.spawn(() => this.fsmLoop(), { name: taskName });
|
|
145
177
|
if (!waitUntilReady) {
|
|
@@ -160,6 +192,7 @@ class UpstreamSessionManager extends task_spawner_js_1.TaskSpawner {
|
|
|
160
192
|
}
|
|
161
193
|
async stop() {
|
|
162
194
|
logger.debug('upstream_session_manager_stopping');
|
|
195
|
+
this.teardownVisibilityListener();
|
|
163
196
|
this.stopEvent.set();
|
|
164
197
|
this.currentStopSubtasks?.set();
|
|
165
198
|
if (this.fsmTask) {
|
|
@@ -213,11 +246,16 @@ class UpstreamSessionManager extends task_spawner_js_1.TaskSpawner {
|
|
|
213
246
|
async fsmLoop() {
|
|
214
247
|
let delay = UpstreamSessionManager.BACKOFF_INITIAL;
|
|
215
248
|
while (!this.stopEvent.isSet()) {
|
|
249
|
+
const startTime = Date.now();
|
|
216
250
|
try {
|
|
217
251
|
await this.connectCycle();
|
|
218
252
|
delay = UpstreamSessionManager.BACKOFF_INITIAL;
|
|
219
253
|
}
|
|
220
254
|
catch (error) {
|
|
255
|
+
// Reset backoff if the connection was alive for more than 10 seconds
|
|
256
|
+
if (Date.now() - startTime > 10000) {
|
|
257
|
+
delay = UpstreamSessionManager.BACKOFF_INITIAL;
|
|
258
|
+
}
|
|
221
259
|
if (error instanceof task_types_js_1.TaskCancelledError) {
|
|
222
260
|
throw error;
|
|
223
261
|
}
|
|
@@ -232,11 +270,20 @@ class UpstreamSessionManager extends task_spawner_js_1.TaskSpawner {
|
|
|
232
270
|
}
|
|
233
271
|
}
|
|
234
272
|
else {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
273
|
+
const err = error;
|
|
274
|
+
if (err.name === 'OAuth2PkceRedirectInitiatedError') {
|
|
275
|
+
logger.info('upstream_link_redirecting', {
|
|
276
|
+
error: err.message,
|
|
277
|
+
will_retry: true,
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
else {
|
|
281
|
+
logger.warning('upstream_link_closed', {
|
|
282
|
+
error: err.message,
|
|
283
|
+
will_retry: true,
|
|
284
|
+
exc_info: true,
|
|
285
|
+
});
|
|
286
|
+
}
|
|
240
287
|
if (!this.hadSuccessfulAttach) {
|
|
241
288
|
throw error;
|
|
242
289
|
}
|
|
@@ -254,14 +301,40 @@ class UpstreamSessionManager extends task_spawner_js_1.TaskSpawner {
|
|
|
254
301
|
if (delaySeconds <= 0) {
|
|
255
302
|
return;
|
|
256
303
|
}
|
|
304
|
+
// If the document is visible, cap the backoff delay to improve UX
|
|
305
|
+
// This ensures that if the user is watching, we retry quickly (e.g. 1s)
|
|
306
|
+
// instead of waiting for the full exponential backoff (up to 30s).
|
|
307
|
+
let effectiveDelay = delaySeconds;
|
|
308
|
+
if (typeof document !== 'undefined' &&
|
|
309
|
+
document.visibilityState === 'visible') {
|
|
310
|
+
effectiveDelay = Math.min(delaySeconds, 1.0);
|
|
311
|
+
if (effectiveDelay < delaySeconds) {
|
|
312
|
+
logger.debug('sleep_reduced_document_visible', {
|
|
313
|
+
original: delaySeconds,
|
|
314
|
+
new: effectiveDelay,
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
if (this.wakeEvent.isSet()) {
|
|
319
|
+
this.wakeEvent.clear();
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
257
322
|
let timeout;
|
|
258
323
|
const sleepPromise = new Promise((resolve) => {
|
|
259
324
|
timeout = setTimeout(() => {
|
|
260
325
|
timeout = undefined;
|
|
261
326
|
resolve();
|
|
262
|
-
},
|
|
327
|
+
}, effectiveDelay * 1000);
|
|
263
328
|
});
|
|
264
|
-
await Promise.race([
|
|
329
|
+
await Promise.race([
|
|
330
|
+
sleepPromise,
|
|
331
|
+
this.stopEvent.wait(),
|
|
332
|
+
this.wakeEvent.wait(),
|
|
333
|
+
]);
|
|
334
|
+
if (this.wakeEvent.isSet()) {
|
|
335
|
+
logger.debug('sleep_interrupted_by_wake_event');
|
|
336
|
+
this.wakeEvent.clear();
|
|
337
|
+
}
|
|
265
338
|
if (timeout !== undefined) {
|
|
266
339
|
clearTimeout(timeout);
|
|
267
340
|
}
|
|
@@ -281,7 +354,7 @@ class UpstreamSessionManager extends task_spawner_js_1.TaskSpawner {
|
|
|
281
354
|
return signal ? event.wait({ signal }) : event.wait();
|
|
282
355
|
}
|
|
283
356
|
_getLocalNodeId() {
|
|
284
|
-
const normalized = this._normalizeNodeId(this.node.
|
|
357
|
+
const normalized = this._normalizeNodeId(this.node.provisionalId);
|
|
285
358
|
if (!normalized) {
|
|
286
359
|
throw new Error('UpstreamSessionManager requires node with a stable identifier');
|
|
287
360
|
}
|
|
@@ -299,7 +372,7 @@ class UpstreamSessionManager extends task_spawner_js_1.TaskSpawner {
|
|
|
299
372
|
throw new errors_js_1.FameConnectError('Admission client is required to attach upstream');
|
|
300
373
|
}
|
|
301
374
|
this.connectEpoch += 1;
|
|
302
|
-
const welcome = await this.admissionClient.hello(this.node.
|
|
375
|
+
const welcome = await this.admissionClient.hello(this.node.provisionalId, (0, core_1.generateId)(), this.requestedLogicals);
|
|
303
376
|
const connectionGrants = welcome.frame.connectionGrants;
|
|
304
377
|
if (!connectionGrants?.length) {
|
|
305
378
|
throw new Error('Welcome frame missing connection grants');
|
|
@@ -341,6 +414,11 @@ class UpstreamSessionManager extends task_spawner_js_1.TaskSpawner {
|
|
|
341
414
|
Array.isArray(welcome.frame.connectionGrants)) {
|
|
342
415
|
for (const grant of welcome.frame.connectionGrants) {
|
|
343
416
|
if (grant && typeof grant === 'object') {
|
|
417
|
+
const grantType = grant.type;
|
|
418
|
+
if (grantType === 'WebSocketConnectionGrant' ||
|
|
419
|
+
grantType === 'HttpConnectionGrant') {
|
|
420
|
+
continue;
|
|
421
|
+
}
|
|
344
422
|
// Avoid duplicates by checking if grant already exists
|
|
345
423
|
const isDuplicate = callbackGrants.some((existing) => JSON.stringify(existing) === JSON.stringify(grant));
|
|
346
424
|
if (!isDuplicate) {
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isMaterializableTokenProvider = isMaterializableTokenProvider;
|
|
4
|
+
function isMaterializableTokenProvider(candidate) {
|
|
5
|
+
return (typeof candidate === 'object' &&
|
|
6
|
+
candidate !== null &&
|
|
7
|
+
typeof candidate.materialize ===
|
|
8
|
+
'function');
|
|
9
|
+
}
|
|
@@ -275,6 +275,15 @@ class OAuth2PkceTokenProvider {
|
|
|
275
275
|
constructor(rawOptions) {
|
|
276
276
|
this.options = normalizeOptions(rawOptions);
|
|
277
277
|
}
|
|
278
|
+
async materialize() {
|
|
279
|
+
const token = await this.getToken();
|
|
280
|
+
return {
|
|
281
|
+
type: 'StaticTokenProvider',
|
|
282
|
+
token: token.value,
|
|
283
|
+
expiresAt: token.expiresAt,
|
|
284
|
+
expires_at: token.expiresAt,
|
|
285
|
+
};
|
|
286
|
+
}
|
|
278
287
|
async getToken() {
|
|
279
288
|
if (!isBrowserEnvironment()) {
|
|
280
289
|
throw new Error('OAuth2PkceTokenProvider requires a browser environment with sessionStorage support');
|
|
@@ -44,6 +44,50 @@ class StaticTokenProvider {
|
|
|
44
44
|
async getToken() {
|
|
45
45
|
return { ...this.token };
|
|
46
46
|
}
|
|
47
|
+
async getIdentity() {
|
|
48
|
+
const tokenValue = this.token.value;
|
|
49
|
+
const parts = tokenValue.split('.');
|
|
50
|
+
if (parts.length !== 3) {
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
const payloadSegment = parts[1];
|
|
55
|
+
// Fix padding for base64url
|
|
56
|
+
const padding = '='.repeat((4 - (payloadSegment.length % 4)) % 4);
|
|
57
|
+
const base64 = (payloadSegment + padding)
|
|
58
|
+
.replace(/-/g, '+')
|
|
59
|
+
.replace(/_/g, '/');
|
|
60
|
+
let jsonString;
|
|
61
|
+
if (typeof Buffer !== 'undefined') {
|
|
62
|
+
jsonString = Buffer.from(base64, 'base64').toString('utf-8');
|
|
63
|
+
}
|
|
64
|
+
else if (typeof atob === 'function') {
|
|
65
|
+
jsonString = atob(base64);
|
|
66
|
+
try {
|
|
67
|
+
jsonString = decodeURIComponent(jsonString
|
|
68
|
+
.split('')
|
|
69
|
+
.map(function (c) {
|
|
70
|
+
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
|
|
71
|
+
})
|
|
72
|
+
.join(''));
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
// ignore
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
const payload = JSON.parse(jsonString);
|
|
82
|
+
if (payload && typeof payload.sub === 'string') {
|
|
83
|
+
return { subject: payload.sub, claims: payload };
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
// ignore decoding errors
|
|
88
|
+
}
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
47
91
|
}
|
|
48
92
|
exports.StaticTokenProvider = StaticTokenProvider;
|
|
49
93
|
function normalizeOptions(input) {
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.isTokenProvider = isTokenProvider;
|
|
4
|
+
exports.isIdentityExposingTokenProvider = isIdentityExposingTokenProvider;
|
|
4
5
|
function isTokenProvider(candidate) {
|
|
5
6
|
return (typeof candidate === 'object' &&
|
|
6
7
|
candidate !== null &&
|
|
7
8
|
typeof candidate.getToken === 'function');
|
|
8
9
|
}
|
|
10
|
+
function isIdentityExposingTokenProvider(candidate) {
|
|
11
|
+
return (isTokenProvider(candidate) &&
|
|
12
|
+
typeof candidate.getIdentity ===
|
|
13
|
+
'function');
|
|
14
|
+
}
|
|
@@ -380,7 +380,7 @@ class DefaultSecurityManager {
|
|
|
380
380
|
async onNodeInitialized(node) {
|
|
381
381
|
this._node = node;
|
|
382
382
|
logger.debug('security_manager_node_initialized', {
|
|
383
|
-
node_id: node.
|
|
383
|
+
node_id: node.provisionalId,
|
|
384
384
|
has_node_crypto_provider: Boolean(node.cryptoProvider),
|
|
385
385
|
provider_type: node.cryptoProvider
|
|
386
386
|
? (node.cryptoProvider.constructor?.name ?? 'unknown')
|
|
@@ -404,7 +404,9 @@ class DefaultSecurityManager {
|
|
|
404
404
|
if (encryption && hasNodeListenerMethod(encryption, 'onNodeInitialized')) {
|
|
405
405
|
await encryption.onNodeInitialized(node);
|
|
406
406
|
}
|
|
407
|
-
logger.debug('node_security_initialization_complete', {
|
|
407
|
+
logger.debug('node_security_initialization_complete', {
|
|
408
|
+
node_id: node.provisionalId,
|
|
409
|
+
});
|
|
408
410
|
}
|
|
409
411
|
async onNodeAttachToPeer(node, attachInfo, connector) {
|
|
410
412
|
const attachRecord = attachInfo;
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
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 = exports.CREDENTIAL_PROVIDER_FACTORY_BASE_TYPE = exports.EdDSAEnvelopeSigner = exports.encodeUtf8 = exports.immutableHeaders = exports.frameDigest = exports.decodeBase64Url = exports.canonicalJson = exports.SigningConfigClass = exports.SECURITY_MANAGER_FACTORY_BASE_TYPE = exports.SECURITY_POLICY_FACTORY_BASE_TYPE = exports.KEY_STORE_FACTORY_BASE_TYPE = exports.ATTACHMENT_KEY_VALIDATOR_FACTORY_BASE_TYPE = exports.KEY_MANAGER_FACTORY_BASE_TYPE = exports.SecureChannelManagerFactory = exports.SECURE_CHANNEL_MANAGER_FACTORY_BASE_TYPE = exports.ENCRYPTION_MANAGER_FACTORY_BASE_TYPE = exports.NoopTrustStoreProvider = exports.TrustStoreProviderFactory = exports.TRUST_STORE_PROVIDER_FACTORY_BASE_TYPE = exports.CertificateManagerFactory = exports.CERTIFICATE_MANAGER_FACTORY_BASE_TYPE = exports.TokenProviderFactory = exports.TOKEN_PROVIDER_FACTORY_BASE_TYPE = exports.TokenVerifierFactory = exports.TOKEN_VERIFIER_FACTORY_BASE_TYPE = exports.TokenIssuerFactory = exports.TOKEN_ISSUER_FACTORY_BASE_TYPE = exports.AuthInjectionStrategyFactory = exports.AUTH_INJECTION_STRATEGY_FACTORY_BASE_TYPE = exports.AuthorizerFactory = exports.AUTHORIZER_FACTORY_BASE_TYPE = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
tslib_1.__exportStar(require("./auth/authorizer.js"), exports);
|
|
6
|
+
tslib_1.__exportStar(require("./auth/auth-identity.js"), exports);
|
|
6
7
|
var authorizer_factory_js_1 = require("./auth/authorizer-factory.js");
|
|
7
8
|
Object.defineProperty(exports, "AUTHORIZER_FACTORY_BASE_TYPE", { enumerable: true, get: function () { return authorizer_factory_js_1.AUTHORIZER_FACTORY_BASE_TYPE; } });
|
|
8
9
|
Object.defineProperty(exports, "AuthorizerFactory", { enumerable: true, get: function () { return authorizer_factory_js_1.AuthorizerFactory; } });
|
|
@@ -95,7 +95,7 @@ class DefaultKeyManager {
|
|
|
95
95
|
this.node = node;
|
|
96
96
|
this.routingNode = isRoutingNode(node) ? node : null;
|
|
97
97
|
logger.debug('key_manager_started', {
|
|
98
|
-
node_id:
|
|
98
|
+
node_id: node.provisionalId,
|
|
99
99
|
physical_path: this.physicalPath,
|
|
100
100
|
has_upstream: this.hasUpstream,
|
|
101
101
|
});
|
|
@@ -278,6 +278,14 @@ class TaskSpawner {
|
|
|
278
278
|
});
|
|
279
279
|
return;
|
|
280
280
|
}
|
|
281
|
+
// Handle PKCE redirect "errors" as info
|
|
282
|
+
if (error.name === 'OAuth2PkceRedirectInitiatedError') {
|
|
283
|
+
logger.debug('background_task_redirecting', {
|
|
284
|
+
task_name: taskName,
|
|
285
|
+
note: 'Task interrupted for PKCE redirect',
|
|
286
|
+
});
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
281
289
|
// Check if this is a retriable connection error (will be logged and retried by FSM)
|
|
282
290
|
const isRetriableError = error.name === 'FameConnectError' ||
|
|
283
291
|
error.message.includes('missed heartbeat') ||
|
package/dist/cjs/version.js
CHANGED
|
@@ -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.
|
|
3
|
+
// Generated from package.json version: 0.3.13
|
|
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.
|
|
10
|
+
exports.VERSION = '0.3.13';
|
|
@@ -239,7 +239,7 @@ export class BroadcastChannelConnector extends BaseAsyncConnector {
|
|
|
239
239
|
// Track page lifecycle events to detect browser unload/discard
|
|
240
240
|
if (typeof window !== 'undefined') {
|
|
241
241
|
const lifecycleLogger = (event) => {
|
|
242
|
-
logger.
|
|
242
|
+
logger.debug('broadcast_channel_page_lifecycle', {
|
|
243
243
|
channel: this.channelName,
|
|
244
244
|
connector_id: this.connectorId,
|
|
245
245
|
event_type: event.type,
|
|
@@ -21,7 +21,10 @@ export const MODULES = [
|
|
|
21
21
|
"./node/admission/direct-admission-client-factory.js",
|
|
22
22
|
"./node/admission/noop-admission-client-factory.js",
|
|
23
23
|
"./node/admission/welcome-service-client-factory.js",
|
|
24
|
+
"./node/default-node-identity-policy-factory.js",
|
|
24
25
|
"./node/node-factory.js",
|
|
26
|
+
"./node/node-identity-policy-profile-factory.js",
|
|
27
|
+
"./node/token-subject-node-identity-policy-factory.js",
|
|
25
28
|
"./placement/static-node-placement-strategy-factory.js",
|
|
26
29
|
"./security/auth/bearer-token-header-auth-injection-strategy-factory.js",
|
|
27
30
|
"./security/auth/default-authorizer-factory.js",
|
|
@@ -96,7 +99,10 @@ export const MODULE_LOADERS = {
|
|
|
96
99
|
"./node/admission/direct-admission-client-factory.js": () => import("./node/admission/direct-admission-client-factory.js"),
|
|
97
100
|
"./node/admission/noop-admission-client-factory.js": () => import("./node/admission/noop-admission-client-factory.js"),
|
|
98
101
|
"./node/admission/welcome-service-client-factory.js": () => import("./node/admission/welcome-service-client-factory.js"),
|
|
102
|
+
"./node/default-node-identity-policy-factory.js": () => import("./node/default-node-identity-policy-factory.js"),
|
|
99
103
|
"./node/node-factory.js": () => import("./node/node-factory.js"),
|
|
104
|
+
"./node/node-identity-policy-profile-factory.js": () => import("./node/node-identity-policy-profile-factory.js"),
|
|
105
|
+
"./node/token-subject-node-identity-policy-factory.js": () => import("./node/token-subject-node-identity-policy-factory.js"),
|
|
100
106
|
"./placement/static-node-placement-strategy-factory.js": () => import("./placement/static-node-placement-strategy-factory.js"),
|
|
101
107
|
"./security/auth/bearer-token-header-auth-injection-strategy-factory.js": () => import("./security/auth/bearer-token-header-auth-injection-strategy-factory.js"),
|
|
102
108
|
"./security/auth/default-authorizer-factory.js": () => import("./security/auth/default-authorizer-factory.js"),
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { TokenProviderFactory } from '../security/auth/token-provider-factory.js';
|
|
2
|
+
import { isMaterializableTokenProvider } from '../security/auth/materializable-token-provider.js';
|
|
3
|
+
import { getLogger } from '../util/logging.js';
|
|
4
|
+
const logger = getLogger('naylence.fame.grants.grant_materializer');
|
|
5
|
+
export class GrantMaterializer {
|
|
6
|
+
static async materialize(grant) {
|
|
7
|
+
const candidate = grant;
|
|
8
|
+
const auth = candidate.auth;
|
|
9
|
+
if (!auth) {
|
|
10
|
+
return grant;
|
|
11
|
+
}
|
|
12
|
+
const tokenProviderConfig = (auth.tokenProvider ??
|
|
13
|
+
auth.token_provider);
|
|
14
|
+
if (!tokenProviderConfig || typeof tokenProviderConfig.type !== 'string') {
|
|
15
|
+
return grant;
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
const provider = await TokenProviderFactory.createTokenProvider(tokenProviderConfig);
|
|
19
|
+
if (isMaterializableTokenProvider(provider)) {
|
|
20
|
+
const materializedConfig = await provider.materialize();
|
|
21
|
+
if (materializedConfig) {
|
|
22
|
+
logger.debug('grant_materialized', {
|
|
23
|
+
grantType: candidate.type,
|
|
24
|
+
providerType: tokenProviderConfig.type,
|
|
25
|
+
});
|
|
26
|
+
const newAuth = { ...auth };
|
|
27
|
+
if ('tokenProvider' in newAuth) {
|
|
28
|
+
newAuth.tokenProvider = materializedConfig;
|
|
29
|
+
}
|
|
30
|
+
if ('token_provider' in newAuth) {
|
|
31
|
+
newAuth.token_provider = materializedConfig;
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
...grant,
|
|
35
|
+
auth: newAuth,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
if (error &&
|
|
42
|
+
error.name === 'OAuth2PkceRedirectInitiatedError') {
|
|
43
|
+
logger.info('grant_materialization_redirecting', {
|
|
44
|
+
grantType: candidate.type,
|
|
45
|
+
});
|
|
46
|
+
throw error;
|
|
47
|
+
}
|
|
48
|
+
logger.warning('grant_materialization_failed', {
|
|
49
|
+
error: error instanceof Error ? error.message : String(error),
|
|
50
|
+
grantType: candidate.type,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
return grant;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -234,11 +234,13 @@ export class AdmissionProfileFactory extends AdmissionClientFactory {
|
|
|
234
234
|
super(...arguments);
|
|
235
235
|
this.type = 'AdmissionProfile';
|
|
236
236
|
}
|
|
237
|
-
async create(config) {
|
|
237
|
+
async create(config, ...factoryArgs) {
|
|
238
238
|
const normalized = normalizeConfig(config);
|
|
239
239
|
const profileConfig = resolveProfileConfig(normalized.profile);
|
|
240
240
|
logger.debug('enabling_admission_profile', { profile: normalized.profile });
|
|
241
|
-
return AdmissionClientFactory.createAdmissionClient(profileConfig
|
|
241
|
+
return AdmissionClientFactory.createAdmissionClient(profileConfig, {
|
|
242
|
+
factoryArgs,
|
|
243
|
+
});
|
|
242
244
|
}
|
|
243
245
|
}
|
|
244
246
|
function normalizeConfig(config) {
|