@kya-os/mcp-i 1.7.13 → 1.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/dist/225.js +1 -0
  2. package/dist/227.js +1 -0
  3. package/dist/242.js +1 -0
  4. package/dist/283.js +1 -0
  5. package/dist/295.js +1 -1
  6. package/dist/354.js +1 -0
  7. package/dist/361.js +1 -0
  8. package/dist/452.js +1 -0
  9. package/dist/533.js +1 -0
  10. package/dist/622.js +1 -0
  11. package/dist/65.js +1 -0
  12. package/dist/843.js +1 -0
  13. package/dist/861.js +1 -0
  14. package/dist/866.js +1 -1
  15. package/dist/914.js +1 -0
  16. package/dist/95.js +1 -0
  17. package/dist/{941.js → 966.js} +1 -1
  18. package/dist/providers/node-providers.d.ts +1 -1
  19. package/dist/providers/node-providers.js +2 -2
  20. package/dist/runtime/adapter-express.js +6 -6
  21. package/dist/runtime/adapter-nextjs.js +6 -6
  22. package/dist/runtime/auth-handshake.d.ts +4 -159
  23. package/dist/runtime/auth-handshake.js +8 -249
  24. package/dist/runtime/http.js +6 -6
  25. package/dist/runtime/mcpi-runtime.d.ts +4 -0
  26. package/dist/runtime/mcpi-runtime.js +58 -43
  27. package/dist/runtime/outbound-delegation.d.ts +34 -0
  28. package/dist/runtime/outbound-delegation.js +134 -0
  29. package/dist/runtime/proof.d.ts +13 -88
  30. package/dist/runtime/proof.js +11 -225
  31. package/dist/runtime/request-context.d.ts +41 -0
  32. package/dist/runtime/request-context.js +48 -0
  33. package/dist/runtime/session.d.ts +13 -104
  34. package/dist/runtime/session.js +31 -267
  35. package/dist/runtime/stdio.js +6 -6
  36. package/dist/runtime/utils/tools.js +17 -3
  37. package/dist/runtime/verifier-middleware.js +4 -4
  38. package/package.json +19 -19
  39. package/dist/207.js +0 -1
  40. package/dist/25.js +0 -1
  41. package/dist/360.js +0 -1
  42. package/dist/387.js +0 -1
  43. package/dist/406.js +0 -1
  44. package/dist/448.js +0 -1
  45. package/dist/478.js +0 -1
  46. package/dist/575.js +0 -1
  47. package/dist/67.js +0 -1
  48. package/dist/743.js +0 -1
  49. package/dist/784.js +0 -1
  50. package/dist/844.js +0 -1
  51. package/dist/936.js +0 -1
  52. package/dist/988.js +0 -1
@@ -8,10 +8,21 @@
8
8
  * was set during handshake processing.
9
9
  */
10
10
  import type { SessionContext } from '@kya-os/contracts/handshake';
11
+ import type { Ed25519PrivateJWK } from '@kya-os/mcp-i-core';
11
12
  export interface RequestContext {
12
13
  session?: SessionContext;
13
14
  requestId?: string;
14
15
  startTime?: number;
16
+ /** Delegation VC credential ID (set after delegation verification succeeds) */
17
+ delegationRef?: string;
18
+ /** Reference chain string — format: vc_id_1>del_id_1>... */
19
+ delegationChain?: string;
20
+ /** Scopes granted by the active delegation */
21
+ delegationScopes?: string[];
22
+ /** Agent Ed25519 private key JWK for delegation proof signing */
23
+ delegationPrivateKeyJwk?: Ed25519PrivateJWK;
24
+ /** Key ID used in delegation proof JWT header */
25
+ delegationAgentKid?: string;
15
26
  }
16
27
  /**
17
28
  * Run a function with request context
@@ -35,3 +46,33 @@ export declare function getCurrentAgentDid(): string | undefined;
35
46
  * NOTE: This only works if called within a runWithContext block
36
47
  */
37
48
  export declare function setSession(session: SessionContext): void;
49
+ /**
50
+ * Update delegation context fields in the current request context.
51
+ * Called after delegation is verified for a protected tool call.
52
+ *
53
+ * NOTE: This only works if called within a runWithContext block
54
+ */
55
+ export declare function setDelegationContext(fields: {
56
+ delegationRef: string;
57
+ delegationChain: string;
58
+ delegationScopes: string[];
59
+ delegationPrivateKeyJwk?: Ed25519PrivateJWK;
60
+ delegationAgentKid?: string;
61
+ }): void;
62
+ /**
63
+ * Convenience helper that wires delegation metadata from a verified delegation
64
+ * result and agent identity into AsyncLocalStorage in a single call.
65
+ *
66
+ * Avoids duplicating the base64-to-base64url JWK conversion and the
67
+ * setDelegationContext call across mcpi-runtime.ts and utils/tools.ts.
68
+ */
69
+ export declare function setDelegationContextFromIdentity(params: {
70
+ delegationId: string;
71
+ delegationChain: string;
72
+ delegationScopes: string[];
73
+ identity: {
74
+ privateKey: string;
75
+ publicKey: string;
76
+ kid: string;
77
+ } | null | undefined;
78
+ }): void;
@@ -14,6 +14,8 @@ exports.getContext = getContext;
14
14
  exports.getCurrentSession = getCurrentSession;
15
15
  exports.getCurrentAgentDid = getCurrentAgentDid;
16
16
  exports.setSession = setSession;
17
+ exports.setDelegationContext = setDelegationContext;
18
+ exports.setDelegationContextFromIdentity = setDelegationContextFromIdentity;
17
19
  const async_hooks_1 = require("async_hooks");
18
20
  const asyncLocalStorage = new async_hooks_1.AsyncLocalStorage();
19
21
  /**
@@ -54,3 +56,49 @@ function setSession(session) {
54
56
  console.warn('[RequestContext] Attempted to set session outside of request context');
55
57
  }
56
58
  }
59
+ /**
60
+ * Update delegation context fields in the current request context.
61
+ * Called after delegation is verified for a protected tool call.
62
+ *
63
+ * NOTE: This only works if called within a runWithContext block
64
+ */
65
+ function setDelegationContext(fields) {
66
+ const context = getContext();
67
+ if (!context) {
68
+ console.warn('[RequestContext] setDelegationContext called outside of runWithContext — delegation headers will not be injected');
69
+ return;
70
+ }
71
+ context.delegationRef = fields.delegationRef;
72
+ context.delegationChain = fields.delegationChain;
73
+ context.delegationScopes = fields.delegationScopes;
74
+ if (fields.delegationPrivateKeyJwk !== undefined) {
75
+ context.delegationPrivateKeyJwk = fields.delegationPrivateKeyJwk;
76
+ }
77
+ if (fields.delegationAgentKid !== undefined) {
78
+ context.delegationAgentKid = fields.delegationAgentKid;
79
+ }
80
+ }
81
+ /**
82
+ * Convenience helper that wires delegation metadata from a verified delegation
83
+ * result and agent identity into AsyncLocalStorage in a single call.
84
+ *
85
+ * Avoids duplicating the base64-to-base64url JWK conversion and the
86
+ * setDelegationContext call across mcpi-runtime.ts and utils/tools.ts.
87
+ */
88
+ function setDelegationContextFromIdentity(params) {
89
+ setDelegationContext({
90
+ delegationRef: params.delegationId,
91
+ delegationChain: params.delegationChain,
92
+ delegationScopes: params.delegationScopes,
93
+ delegationPrivateKeyJwk: params.identity?.privateKey
94
+ ? {
95
+ kty: "OKP",
96
+ crv: "Ed25519",
97
+ d: Buffer.from(params.identity.privateKey, "base64").toString("base64url"),
98
+ x: Buffer.from(params.identity.publicKey, "base64").toString("base64url"),
99
+ kid: params.identity.kid,
100
+ }
101
+ : undefined,
102
+ delegationAgentKid: params.identity?.kid,
103
+ });
104
+ }
@@ -1,108 +1,17 @@
1
1
  /**
2
- * Handshake and Session Management for XMCP-I Runtime
2
+ * Session Management for XMCP-I Runtime — Node.js adapter
3
3
  *
4
- * Handles handshake enforcement, session management, and nonce validation
5
- * according to requirements 4.5-4.9 and 19.1-19.2.
6
- */
7
- import { HandshakeRequest, SessionContext, NonceCache } from "@kya-os/contracts/handshake";
8
- /**
9
- * Session management configuration
10
- */
11
- export interface SessionConfig {
12
- timestampSkewSeconds?: number;
13
- sessionTtlMinutes?: number;
14
- absoluteSessionLifetime?: number;
15
- nonceCache?: NonceCache;
16
- serverDid?: string;
17
- }
18
- /**
19
- * Handshake validation result
20
- */
21
- export interface HandshakeResult {
22
- success: boolean;
23
- session?: SessionContext;
24
- error?: {
25
- code: string;
26
- message: string;
27
- remediation?: string;
28
- };
29
- }
30
- /**
31
- * Session manager class
32
- */
33
- export declare class SessionManager {
34
- private config;
35
- private sessions;
36
- constructor(config?: SessionConfig);
37
- /**
38
- * Set server DID for session creation
39
- * Called after identity is loaded
40
- */
41
- setServerDid(serverDid: string): void;
42
- /**
43
- * Validate handshake and create or retrieve session
44
- * Requirements: 4.5, 4.6, 4.7, 4.8, 4.9
45
- */
46
- validateHandshake(request: HandshakeRequest): Promise<HandshakeResult>;
47
- /**
48
- * Get session by ID and update last activity
49
- */
50
- getSession(sessionId: string): Promise<SessionContext | null>;
51
- /**
52
- * Generate a unique session ID
53
- * Uses mcpi_{uuid} format for MCP protocol compliance and traceability
54
- */
55
- private generateSessionId;
56
- /**
57
- * Generate a deterministic client identifier when the client
58
- * does not provide one during the handshake.
59
- */
60
- private generateClientId;
61
- /**
62
- * Normalize string fields from handshake metadata
63
- */
64
- private normalizeClientInfoString;
65
- /**
66
- * Build MCP client metadata for the session when provided during handshake
67
- */
68
- private buildClientInfo;
69
- /**
70
- * Generate a cryptographically secure nonce
71
- */
72
- static generateNonce(): string;
73
- /**
74
- * Cleanup expired sessions and nonces
75
- */
76
- cleanup(): Promise<void>;
77
- /**
78
- * Get session statistics
79
- */
80
- getStats(): {
81
- activeSessions: number;
82
- config: {
83
- timestampSkewSeconds: number;
84
- sessionTtlMinutes: number;
85
- absoluteSessionLifetime?: number;
86
- cacheType: string;
87
- };
88
- };
89
- /**
90
- * Clear all sessions (useful for testing)
91
- */
92
- clearSessions(): void;
4
+ * Re-exports SessionManager from @kya-os/mcp-i-core and wires in
5
+ * NodeCryptoProvider as the default. All existing call sites remain
6
+ * compatible: `new SessionManager()` and `new SessionManager(config)` work.
7
+ *
8
+ * Requirements: 4.5-4.9, 19.1-19.2
9
+ */
10
+ import { SessionManager as CoreSessionManager, createHandshakeRequest, validateHandshakeFormat, type SessionConfig, type HandshakeResult } from "@kya-os/mcp-i-core";
11
+ import { CryptoProvider } from "@kya-os/mcp-i-core";
12
+ export type { SessionConfig, HandshakeResult };
13
+ export { createHandshakeRequest, validateHandshakeFormat };
14
+ export declare class SessionManager extends CoreSessionManager {
15
+ constructor(configOrCrypto?: SessionConfig | CryptoProvider, config?: SessionConfig);
93
16
  }
94
- /**
95
- * Default session manager instance
96
- */
97
17
  export declare const defaultSessionManager: SessionManager;
98
- /**
99
- * Utility functions
100
- */
101
- /**
102
- * Create a handshake request
103
- */
104
- export declare function createHandshakeRequest(audience: string): HandshakeRequest;
105
- /**
106
- * Validate handshake request format
107
- */
108
- export declare function validateHandshakeFormat(request: any): request is HandshakeRequest;
@@ -1,278 +1,42 @@
1
1
  "use strict";
2
2
  /**
3
- * Handshake and Session Management for XMCP-I Runtime
3
+ * Session Management for XMCP-I Runtime — Node.js adapter
4
4
  *
5
- * Handles handshake enforcement, session management, and nonce validation
6
- * according to requirements 4.5-4.9 and 19.1-19.2.
5
+ * Re-exports SessionManager from @kya-os/mcp-i-core and wires in
6
+ * NodeCryptoProvider as the default. All existing call sites remain
7
+ * compatible: `new SessionManager()` and `new SessionManager(config)` work.
8
+ *
9
+ * Requirements: 4.5-4.9, 19.1-19.2
7
10
  */
8
11
  Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.defaultSessionManager = exports.SessionManager = void 0;
10
- exports.createHandshakeRequest = createHandshakeRequest;
11
- exports.validateHandshakeFormat = validateHandshakeFormat;
12
- const crypto_1 = require("crypto");
13
- const memory_nonce_cache_1 = require("../cache/memory-nonce-cache");
14
- /**
15
- * Session manager class
16
- */
17
- class SessionManager {
18
- config;
19
- sessions = new Map();
20
- constructor(config = {}) {
21
- this.config = {
22
- timestampSkewSeconds: parseInt(process.env.XMCP_I_TS_SKEW_SEC || "120"),
23
- sessionTtlMinutes: parseInt(process.env.XMCP_I_SESSION_TTL_MIN || "30"),
24
- // absoluteSessionLifetime: undefined, // Disabled by default (omit to use undefined)
25
- nonceCache: new memory_nonce_cache_1.MemoryNonceCache(),
26
- ...config,
27
- };
28
- // Warn about multi-instance deployments with memory cache (only in production or if explicitly enabled)
29
- if (this.config.nonceCache instanceof memory_nonce_cache_1.MemoryNonceCache &&
30
- (process.env.NODE_ENV === "production" ||
31
- process.env.MCPI_WARN_MEMORY_CACHE === "true")) {
32
- console.warn("Warning: Using MemoryNonceCache - not suitable for multi-instance deployments. " +
33
- "Consider using Redis, DynamoDB, or Cloudflare KV for production.");
34
- }
35
- }
36
- /**
37
- * Set server DID for session creation
38
- * Called after identity is loaded
39
- */
40
- setServerDid(serverDid) {
41
- this.config.serverDid = serverDid;
42
- }
43
- /**
44
- * Validate handshake and create or retrieve session
45
- * Requirements: 4.5, 4.6, 4.7, 4.8, 4.9
46
- */
47
- async validateHandshake(request) {
48
- try {
49
- // Validate timestamp (±120s clock skew by default)
50
- const now = Math.floor(Date.now() / 1000);
51
- const timeDiff = Math.abs(now - request.timestamp);
52
- if (timeDiff > this.config.timestampSkewSeconds) {
53
- return {
54
- success: false,
55
- error: {
56
- code: "XMCP_I_EHANDSHAKE",
57
- message: `Timestamp outside acceptable range (±${this.config.timestampSkewSeconds}s)`,
58
- remediation: `Check NTP sync on client and server. Current server time: ${now}, received: ${request.timestamp}, diff: ${timeDiff}s. Adjust XMCP_I_TS_SKEW_SEC if needed.`,
59
- },
60
- };
61
- }
62
- // Validate nonce (must be unique within session window)
63
- const nonceExists = await this.config.nonceCache.has(request.nonce, request.agentDid);
64
- if (nonceExists) {
65
- return {
66
- success: false,
67
- error: {
68
- code: "XMCP_I_EHANDSHAKE",
69
- message: "Nonce already used (replay attack prevention)",
70
- remediation: "Generate a new unique nonce for each request",
71
- },
72
- };
73
- }
74
- // Add nonce to cache with TTL >= session TTL
75
- const nonceTtlSeconds = this.config.sessionTtlMinutes * 60 + 60; // Session TTL + 1 minute buffer
76
- await this.config.nonceCache.add(request.nonce, nonceTtlSeconds, request.agentDid);
77
- // Generate session ID
78
- const sessionId = this.generateSessionId();
79
- const clientInfo = this.buildClientInfo(request);
80
- // Create session context
81
- // Phase 5: Sessions start anonymous until OAuth completes
82
- const session = {
83
- sessionId,
84
- audience: request.audience,
85
- nonce: request.nonce,
86
- timestamp: request.timestamp,
87
- createdAt: now,
88
- lastActivity: now,
89
- ttlMinutes: this.config.sessionTtlMinutes,
90
- identityState: "anonymous", // Phase 5: Anonymous until OAuth
91
- agentDid: request.agentDid, // Pass through agent DID for delegation verification
92
- ...(this.config.serverDid && { serverDid: this.config.serverDid }), // Include server DID if provided
93
- ...(clientInfo && { clientInfo }),
94
- };
95
- // Store session
96
- this.sessions.set(sessionId, session);
97
- return {
98
- success: true,
99
- session,
100
- };
101
- }
102
- catch (error) {
103
- return {
104
- success: false,
105
- error: {
106
- code: "XMCP_I_EHANDSHAKE",
107
- message: `Handshake validation failed: ${error instanceof Error ? error.message : "Unknown error"}`,
108
- },
109
- };
110
- }
111
- }
112
- /**
113
- * Get session by ID and update last activity
114
- */
115
- async getSession(sessionId) {
116
- const session = this.sessions.get(sessionId);
117
- if (!session) {
118
- return null;
119
- }
120
- const now = Math.floor(Date.now() / 1000);
121
- // Check if session has expired (idle timeout)
122
- const idleTimeSeconds = now - session.lastActivity;
123
- const maxIdleSeconds = session.ttlMinutes * 60;
124
- if (idleTimeSeconds > maxIdleSeconds) {
125
- this.sessions.delete(sessionId);
126
- return null;
127
- }
128
- // Check absolute session lifetime if configured
129
- if (this.config.absoluteSessionLifetime) {
130
- const sessionAgeSeconds = now - session.createdAt;
131
- const maxAgeSeconds = this.config.absoluteSessionLifetime * 60;
132
- if (sessionAgeSeconds > maxAgeSeconds) {
133
- this.sessions.delete(sessionId);
134
- return null;
135
- }
136
- }
137
- // Update last activity
138
- session.lastActivity = now;
139
- this.sessions.set(sessionId, session);
140
- return session;
141
- }
142
- /**
143
- * Generate a unique session ID
144
- * Uses mcpi_{uuid} format for MCP protocol compliance and traceability
145
- */
146
- generateSessionId() {
147
- // Generate UUID v4 using crypto random bytes
148
- const bytes = (0, crypto_1.randomBytes)(16);
149
- // Set version (4) and variant (RFC4122)
150
- bytes[6] = (bytes[6] & 0x0f) | 0x40;
151
- bytes[8] = (bytes[8] & 0x3f) | 0x80;
152
- const hex = bytes.toString("hex");
153
- const uuid = `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
154
- return `mcpi_${uuid}`;
155
- }
156
- /**
157
- * Generate a deterministic client identifier when the client
158
- * does not provide one during the handshake.
159
- */
160
- generateClientId() {
161
- return `client_${(0, crypto_1.randomBytes)(6).toString("hex")}`;
162
- }
163
- /**
164
- * Normalize string fields from handshake metadata
165
- */
166
- normalizeClientInfoString(value) {
167
- if (typeof value !== "string") {
168
- return undefined;
169
- }
170
- const trimmed = value.trim();
171
- return trimmed.length > 0 ? trimmed : undefined;
172
- }
173
- /**
174
- * Build MCP client metadata for the session when provided during handshake
175
- */
176
- buildClientInfo(request) {
177
- const hasMetadata = !!request.clientInfo ||
178
- typeof request.clientProtocolVersion === "string" ||
179
- request.clientCapabilities !== undefined;
180
- if (!hasMetadata) {
181
- return undefined;
12
+ exports.defaultSessionManager = exports.SessionManager = exports.validateHandshakeFormat = exports.createHandshakeRequest = void 0;
13
+ const mcp_i_core_1 = require("@kya-os/mcp-i-core");
14
+ Object.defineProperty(exports, "createHandshakeRequest", { enumerable: true, get: function () { return mcp_i_core_1.createHandshakeRequest; } });
15
+ Object.defineProperty(exports, "validateHandshakeFormat", { enumerable: true, get: function () { return mcp_i_core_1.validateHandshakeFormat; } });
16
+ const mcp_i_core_2 = require("@kya-os/mcp-i-core");
17
+ const node_providers_1 = require("../providers/node-providers");
18
+ function resolveNodeConfig(config) {
19
+ const skewEnv = process.env["XMCP_I_TS_SKEW_SEC"];
20
+ const ttlEnv = process.env["XMCP_I_SESSION_TTL_MIN"];
21
+ const parsedSkew = skewEnv ? parseInt(skewEnv, 10) : undefined;
22
+ const parsedTtl = ttlEnv ? parseInt(ttlEnv, 10) : undefined;
23
+ return {
24
+ ...config,
25
+ timestampSkewSeconds: config.timestampSkewSeconds ??
26
+ (parsedSkew !== undefined && !isNaN(parsedSkew) ? parsedSkew : undefined),
27
+ sessionTtlMinutes: config.sessionTtlMinutes ??
28
+ (parsedTtl !== undefined && !isNaN(parsedTtl) ? parsedTtl : undefined),
29
+ };
30
+ }
31
+ class SessionManager extends mcp_i_core_1.SessionManager {
32
+ constructor(configOrCrypto, config) {
33
+ if (configOrCrypto instanceof mcp_i_core_2.CryptoProvider) {
34
+ super(configOrCrypto, resolveNodeConfig(config ?? {}));
182
35
  }
183
- const source = request.clientInfo;
184
- return {
185
- name: this.normalizeClientInfoString(source?.name) ?? "unknown",
186
- title: this.normalizeClientInfoString(source?.title),
187
- version: this.normalizeClientInfoString(source?.version),
188
- platform: this.normalizeClientInfoString(source?.platform),
189
- vendor: this.normalizeClientInfoString(source?.vendor),
190
- persistentId: this.normalizeClientInfoString(source?.persistentId),
191
- clientId: this.normalizeClientInfoString(source?.clientId) ??
192
- this.generateClientId(),
193
- protocolVersion: this.normalizeClientInfoString(request.clientProtocolVersion),
194
- capabilities: request.clientCapabilities,
195
- };
196
- }
197
- /**
198
- * Generate a cryptographically secure nonce
199
- */
200
- static generateNonce() {
201
- return (0, crypto_1.randomBytes)(16).toString("base64url"); // 128-bit nonce
202
- }
203
- /**
204
- * Cleanup expired sessions and nonces
205
- */
206
- async cleanup() {
207
- const now = Math.floor(Date.now() / 1000);
208
- // Clean up expired sessions
209
- for (const [sessionId, session] of this.sessions.entries()) {
210
- const idleTimeSeconds = now - session.lastActivity;
211
- const maxIdleSeconds = session.ttlMinutes * 60;
212
- let expired = idleTimeSeconds > maxIdleSeconds;
213
- // Check absolute lifetime
214
- if (!expired && this.config.absoluteSessionLifetime) {
215
- const sessionAgeSeconds = now - session.createdAt;
216
- const maxAgeSeconds = this.config.absoluteSessionLifetime * 60;
217
- expired = sessionAgeSeconds > maxAgeSeconds;
218
- }
219
- if (expired) {
220
- this.sessions.delete(sessionId);
221
- }
36
+ else {
37
+ super(new node_providers_1.NodeCryptoProvider(), resolveNodeConfig(configOrCrypto ?? {}));
222
38
  }
223
- // Clean up expired nonces
224
- await this.config.nonceCache.cleanup();
225
- }
226
- /**
227
- * Get session statistics
228
- */
229
- getStats() {
230
- return {
231
- activeSessions: this.sessions.size,
232
- config: {
233
- timestampSkewSeconds: this.config.timestampSkewSeconds,
234
- sessionTtlMinutes: this.config.sessionTtlMinutes,
235
- absoluteSessionLifetime: this.config.absoluteSessionLifetime,
236
- cacheType: this.config.nonceCache.constructor.name,
237
- },
238
- };
239
- }
240
- /**
241
- * Clear all sessions (useful for testing)
242
- */
243
- clearSessions() {
244
- this.sessions.clear();
245
39
  }
246
40
  }
247
41
  exports.SessionManager = SessionManager;
248
- /**
249
- * Default session manager instance
250
- */
251
42
  exports.defaultSessionManager = new SessionManager();
252
- /**
253
- * Utility functions
254
- */
255
- /**
256
- * Create a handshake request
257
- */
258
- function createHandshakeRequest(audience) {
259
- return {
260
- nonce: SessionManager.generateNonce(),
261
- audience,
262
- timestamp: Math.floor(Date.now() / 1000),
263
- };
264
- }
265
- /**
266
- * Validate handshake request format
267
- */
268
- function validateHandshakeFormat(request) {
269
- return (typeof request === "object" &&
270
- request !== null &&
271
- typeof request.nonce === "string" &&
272
- request.nonce.length > 0 &&
273
- typeof request.audience === "string" &&
274
- request.audience.length > 0 &&
275
- typeof request.timestamp === "number" &&
276
- request.timestamp > 0 &&
277
- Number.isInteger(request.timestamp));
278
- }