iframe.io 1.1.0 → 1.3.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.
- package/README.md +407 -110
- package/dist/index.d.ts +162 -0
- package/dist/index.js +866 -229
- package/package.json +4 -6
- package/src/index.ts +872 -12
package/README.md
CHANGED
|
@@ -4,8 +4,10 @@ Easy and friendly API to connect and interact between content window and its con
|
|
|
4
4
|
|
|
5
5
|
## New Features & Improvements
|
|
6
6
|
|
|
7
|
-
### 🆕 Version 1.
|
|
7
|
+
### 🆕 Version 1.3.0 Updates
|
|
8
8
|
|
|
9
|
+
- **📋 Protocol Versioning**: Message format versioning for future-proof evolution
|
|
10
|
+
- **🔐 Session Key Rotation**: Opt-in session-derived keys for high-security applications
|
|
9
11
|
- **🔒 Enhanced Security**: Origin validation, message sanitization, and payload size limits
|
|
10
12
|
- **🔄 Auto-Reconnection**: Automatic reconnection with exponential backoff strategy
|
|
11
13
|
- **💓 Heartbeat Monitoring**: Connection health monitoring with configurable intervals
|
|
@@ -78,6 +80,277 @@ iframeIO.on('hello', (data) => {
|
|
|
78
80
|
})
|
|
79
81
|
```
|
|
80
82
|
|
|
83
|
+
## Protocol Versioning
|
|
84
|
+
|
|
85
|
+
### What is Protocol Versioning?
|
|
86
|
+
|
|
87
|
+
Every message sent includes a protocol version number (`v` field). This allows the library to evolve over time while maintaining backward compatibility. If a peer receives a message with an unsupported version, it can handle it gracefully.
|
|
88
|
+
|
|
89
|
+
### Current Version
|
|
90
|
+
|
|
91
|
+
The current protocol version is **v1**.
|
|
92
|
+
|
|
93
|
+
### How It Works
|
|
94
|
+
|
|
95
|
+
```javascript
|
|
96
|
+
// All messages automatically include version
|
|
97
|
+
const messageData = {
|
|
98
|
+
v: 1, // Protocol version
|
|
99
|
+
_event: 'userAction',
|
|
100
|
+
payload: { action: 'click' },
|
|
101
|
+
timestamp: 1609459200000
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Version Checking
|
|
106
|
+
|
|
107
|
+
```javascript
|
|
108
|
+
// Library automatically handles version checking
|
|
109
|
+
iframeIO.on('error', (error) => {
|
|
110
|
+
if (error.type === 'UNSUPPORTED_VERSION') {
|
|
111
|
+
console.log(`Peer using v${error.received}, we support v${error.supported}`)
|
|
112
|
+
// Handle gracefully - maybe show upgrade notice
|
|
113
|
+
}
|
|
114
|
+
})
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Get Version Information
|
|
118
|
+
|
|
119
|
+
```javascript
|
|
120
|
+
const stats = iframeIO.getStats()
|
|
121
|
+
console.log(`Our version: v${stats.protocolVersion}`)
|
|
122
|
+
console.log(`Peer version: v${stats.peerProtocolVersion}`)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Future-Proofing Benefits
|
|
126
|
+
|
|
127
|
+
1. **Seamless Upgrades**: Deploy new versions without breaking old clients
|
|
128
|
+
2. **Feature Detection**: Enable features based on peer capabilities
|
|
129
|
+
3. **Debugging**: Identify which version caused issues
|
|
130
|
+
4. **Graceful Degradation**: Fall back to older protocol when needed
|
|
131
|
+
|
|
132
|
+
## Session Key Rotation (Opt-In)
|
|
133
|
+
|
|
134
|
+
### When to Use Session Keys
|
|
135
|
+
|
|
136
|
+
Enable session key rotation for:
|
|
137
|
+
- ✅ **Long-lived connections** (> 1 hour)
|
|
138
|
+
- ✅ **High-security applications** (banking, healthcare)
|
|
139
|
+
- ✅ **Compliance requirements** (PCI-DSS, HIPAA, SOC 2)
|
|
140
|
+
- ✅ **Multiple concurrent iframe connections**
|
|
141
|
+
- ✅ **Applications handling sensitive data**
|
|
142
|
+
|
|
143
|
+
Don't enable for:
|
|
144
|
+
- ❌ Short-lived connections (< 30 minutes)
|
|
145
|
+
- ❌ Low-risk internal tools
|
|
146
|
+
- ❌ Same-origin communication
|
|
147
|
+
- ❌ Applications with minimal security requirements
|
|
148
|
+
|
|
149
|
+
### Basic Configuration
|
|
150
|
+
|
|
151
|
+
```javascript
|
|
152
|
+
const iframeIO = new IOF({
|
|
153
|
+
type: 'WINDOW',
|
|
154
|
+
cryptoAuth: {
|
|
155
|
+
secret: 'your-master-secret-key',
|
|
156
|
+
requireSigned: true,
|
|
157
|
+
|
|
158
|
+
// Enable session key rotation
|
|
159
|
+
enableSessionKeys: true,
|
|
160
|
+
sessionKeyRotationInterval: 3600000 // 1 hour (default)
|
|
161
|
+
}
|
|
162
|
+
})
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### How It Works
|
|
166
|
+
|
|
167
|
+
1. **Connection**: Normal ping/pong handshake
|
|
168
|
+
2. **Key Exchange**: Peers exchange random session IDs
|
|
169
|
+
3. **Key Derivation**: Both derive identical session key using HKDF
|
|
170
|
+
4. **Secure Communication**: All signed messages use session key
|
|
171
|
+
5. **Periodic Rotation**: New key derived every rotation interval
|
|
172
|
+
6. **Graceful Transition**: Old key accepted during grace period
|
|
173
|
+
|
|
174
|
+
```
|
|
175
|
+
Parent IFrame
|
|
176
|
+
| |
|
|
177
|
+
|---- ping -------------------->|
|
|
178
|
+
|<--- pong ----------------------|
|
|
179
|
+
| |
|
|
180
|
+
|-- __session_key_init -------->|
|
|
181
|
+
| (sessionId: abc123) |
|
|
182
|
+
| |
|
|
183
|
+
|<- __session_key_ack -----------|
|
|
184
|
+
| (sessionId: def456, keyId) |
|
|
185
|
+
| |
|
|
186
|
+
[Both derive: key = HKDF(master, abc123+def456, keyId)]
|
|
187
|
+
| |
|
|
188
|
+
|-- signed message (keyId) ---->|
|
|
189
|
+
|<- signed message (keyId) ------|
|
|
190
|
+
| |
|
|
191
|
+
[After rotation interval] |
|
|
192
|
+
|-- __session_key_rotate ------>|
|
|
193
|
+
| (newKeyId) |
|
|
194
|
+
| |
|
|
195
|
+
[Both derive new key] |
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Configuration Options
|
|
199
|
+
|
|
200
|
+
```javascript
|
|
201
|
+
const iframeIO = new IOF({
|
|
202
|
+
type: 'WINDOW',
|
|
203
|
+
debug: true,
|
|
204
|
+
cryptoAuth: {
|
|
205
|
+
// Master secret (never transmitted)
|
|
206
|
+
secret: 'replace-with-shared-secret',
|
|
207
|
+
|
|
208
|
+
// Require all messages to be signed
|
|
209
|
+
requireSigned: true,
|
|
210
|
+
|
|
211
|
+
// Clock skew tolerance (2 minutes default)
|
|
212
|
+
maxSkewMs: 120000,
|
|
213
|
+
|
|
214
|
+
// Replay protection window (500 nonces default)
|
|
215
|
+
replayWindowSize: 500,
|
|
216
|
+
|
|
217
|
+
// SESSION KEY ROTATION (opt-in)
|
|
218
|
+
enableSessionKeys: true,
|
|
219
|
+
|
|
220
|
+
// Rotate every hour (adjust based on security needs)
|
|
221
|
+
// - High security: 15-30 minutes
|
|
222
|
+
// - Standard: 1 hour
|
|
223
|
+
// - Low risk: 2-4 hours
|
|
224
|
+
sessionKeyRotationInterval: 3600000
|
|
225
|
+
}
|
|
226
|
+
})
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Monitoring Session Keys
|
|
230
|
+
|
|
231
|
+
```javascript
|
|
232
|
+
// Listen for session key events
|
|
233
|
+
iframeIO.on('session_key_established', (data) => {
|
|
234
|
+
console.log(`Session key established: ${data.keyId}`)
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
iframeIO.on('session_key_rotating', (data) => {
|
|
238
|
+
console.log(`Rotating to new key: ${data.keyId}`)
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
iframeIO.on('session_key_rotated', (data) => {
|
|
242
|
+
console.log(`Peer rotated to: ${data.keyId}`)
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
// Check current key status
|
|
246
|
+
const stats = iframeIO.getStats()
|
|
247
|
+
console.log(`Session key active: ${stats.sessionKeyActive}`)
|
|
248
|
+
console.log(`Current key ID: ${stats.sessionKeyId}`)
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### Security Considerations
|
|
252
|
+
|
|
253
|
+
**✅ What Session Keys Provide:**
|
|
254
|
+
- Forward secrecy: Past sessions remain secure if master key leaks
|
|
255
|
+
- Session isolation: Each connection uses unique keys
|
|
256
|
+
- Automatic rotation: Limits exposure window
|
|
257
|
+
- Replay protection: Per-session nonce tracking
|
|
258
|
+
|
|
259
|
+
**⚠️ Important Security Notes:**
|
|
260
|
+
|
|
261
|
+
1. **Not a Sandbox Boundary**: If attacker can execute JS in either peer, they can read keys
|
|
262
|
+
2. **Master Secret Security**: Protect your master secret like a password
|
|
263
|
+
3. **HTTPS Required**: Always use HTTPS for iframe communication
|
|
264
|
+
4. **Origin Validation**: Session keys don't replace origin checking
|
|
265
|
+
|
|
266
|
+
### Performance Impact
|
|
267
|
+
|
|
268
|
+
Session key rotation has minimal performance impact:
|
|
269
|
+
- **Key derivation**: ~5-10ms (uses WebCrypto HKDF)
|
|
270
|
+
- **Memory**: ~1KB per active key
|
|
271
|
+
- **Network**: 3 small handshake messages on connect
|
|
272
|
+
- **CPU**: Negligible during rotation (background process)
|
|
273
|
+
|
|
274
|
+
### Example: Banking Application
|
|
275
|
+
|
|
276
|
+
```javascript
|
|
277
|
+
// High-security banking app
|
|
278
|
+
const bankingIO = new IOF({
|
|
279
|
+
type: 'WINDOW',
|
|
280
|
+
debug: false,
|
|
281
|
+
cryptoAuth: {
|
|
282
|
+
secret: process.env.IFRAME_MASTER_SECRET,
|
|
283
|
+
requireSigned: true,
|
|
284
|
+
enableSessionKeys: true,
|
|
285
|
+
sessionKeyRotationInterval: 1800000 // 30 minutes
|
|
286
|
+
},
|
|
287
|
+
// Additional security
|
|
288
|
+
allowedIncomingEvents: [
|
|
289
|
+
'transaction_request',
|
|
290
|
+
'balance_query',
|
|
291
|
+
'account_info'
|
|
292
|
+
],
|
|
293
|
+
validateIncoming: (event, payload, origin) => {
|
|
294
|
+
// Validate payload structure
|
|
295
|
+
if (event === 'transaction_request') {
|
|
296
|
+
return payload &&
|
|
297
|
+
typeof payload.amount === 'number' &&
|
|
298
|
+
typeof payload.recipient === 'string'
|
|
299
|
+
}
|
|
300
|
+
return true
|
|
301
|
+
}
|
|
302
|
+
})
|
|
303
|
+
|
|
304
|
+
// Monitor security events
|
|
305
|
+
bankingIO.on('session_key_rotating', () => {
|
|
306
|
+
console.log('Security: Rotating session keys')
|
|
307
|
+
// Log to security audit trail
|
|
308
|
+
})
|
|
309
|
+
|
|
310
|
+
bankingIO.on('error', (error) => {
|
|
311
|
+
if (error.type === 'AUTH_FAILED') {
|
|
312
|
+
console.error('Security: Authentication failed')
|
|
313
|
+
// Trigger security alert
|
|
314
|
+
}
|
|
315
|
+
})
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### Example: Healthcare Application (HIPAA Compliance)
|
|
319
|
+
|
|
320
|
+
```javascript
|
|
321
|
+
// HIPAA-compliant healthcare app
|
|
322
|
+
const healthcareIO = new IOF({
|
|
323
|
+
type: 'IFRAME',
|
|
324
|
+
debug: false,
|
|
325
|
+
cryptoAuth: {
|
|
326
|
+
secret: process.env.PHI_MASTER_SECRET,
|
|
327
|
+
requireSigned: true,
|
|
328
|
+
enableSessionKeys: true,
|
|
329
|
+
sessionKeyRotationInterval: 3600000 // 1 hour
|
|
330
|
+
},
|
|
331
|
+
maxMessageSize: 512 * 1024, // 512KB limit for PHI
|
|
332
|
+
heartbeatInterval: 60000 // 1 minute health check
|
|
333
|
+
})
|
|
334
|
+
|
|
335
|
+
// Audit logging
|
|
336
|
+
healthcareIO.on('session_key_established', (data) => {
|
|
337
|
+
auditLog({
|
|
338
|
+
event: 'SESSION_KEY_ESTABLISHED',
|
|
339
|
+
keyId: data.keyId,
|
|
340
|
+
timestamp: new Date().toISOString(),
|
|
341
|
+
user: getCurrentUser()
|
|
342
|
+
})
|
|
343
|
+
})
|
|
344
|
+
|
|
345
|
+
healthcareIO.on('connect', () => {
|
|
346
|
+
// Send encrypted patient data
|
|
347
|
+
healthcareIO.emitSigned('patient_data', {
|
|
348
|
+
patientId: 'encrypted-id',
|
|
349
|
+
records: encryptedRecords
|
|
350
|
+
})
|
|
351
|
+
})
|
|
352
|
+
```
|
|
353
|
+
|
|
81
354
|
## Enhanced Configuration Options
|
|
82
355
|
|
|
83
356
|
```javascript
|
|
@@ -89,7 +362,18 @@ const iframeIO = new IOF({
|
|
|
89
362
|
maxMessageSize: 1024 * 1024, // Max message size in bytes (1MB)
|
|
90
363
|
maxMessagesPerSecond: 100, // Rate limit (100 messages/second)
|
|
91
364
|
autoReconnect: true, // Enable automatic reconnection
|
|
92
|
-
messageQueueSize: 50
|
|
365
|
+
messageQueueSize: 50, // Max queued messages when disconnected
|
|
366
|
+
allowedIncomingEvents: ['hello', 'response'], // Optional incoming event allowlist
|
|
367
|
+
validateIncoming: (event, payload, origin) => true, // Optional custom validator
|
|
368
|
+
cryptoAuth: {
|
|
369
|
+
secret: 'replace-with-shared-secret',
|
|
370
|
+
requireSigned: false,
|
|
371
|
+
maxSkewMs: 120000, // 2 minutes clock skew
|
|
372
|
+
replayWindowSize: 500,
|
|
373
|
+
// Session key rotation (opt-in)
|
|
374
|
+
enableSessionKeys: false, // Set to true for high-security apps
|
|
375
|
+
sessionKeyRotationInterval: 3600000 // 1 hour
|
|
376
|
+
}
|
|
93
377
|
})
|
|
94
378
|
```
|
|
95
379
|
|
|
@@ -98,9 +382,9 @@ const iframeIO = new IOF({
|
|
|
98
382
|
### Send Messages with Acknowledgments
|
|
99
383
|
|
|
100
384
|
```javascript
|
|
101
|
-
// Send message and wait for
|
|
385
|
+
// Send message and wait for response with timeout
|
|
102
386
|
try {
|
|
103
|
-
const response = await iframeIO.emitAsync('getData', { id: 123 }, 10000)
|
|
387
|
+
const response = await iframeIO.emitAsync('getData', { id: 123 }, 10000)
|
|
104
388
|
console.log('Response:', response)
|
|
105
389
|
} catch (error) {
|
|
106
390
|
console.error('Request failed:', error.message)
|
|
@@ -110,9 +394,9 @@ try {
|
|
|
110
394
|
iframeIO.on('getData', async (data, ack) => {
|
|
111
395
|
try {
|
|
112
396
|
const result = await fetchData(data.id)
|
|
113
|
-
ack(false, result)
|
|
397
|
+
ack(false, result)
|
|
114
398
|
} catch (error) {
|
|
115
|
-
ack(error.message)
|
|
399
|
+
ack(error.message)
|
|
116
400
|
}
|
|
117
401
|
})
|
|
118
402
|
```
|
|
@@ -122,15 +406,11 @@ iframeIO.on('getData', async (data, ack) => {
|
|
|
122
406
|
```javascript
|
|
123
407
|
// Wait for connection with timeout
|
|
124
408
|
try {
|
|
125
|
-
await iframeIO.connectAsync(5000)
|
|
409
|
+
await iframeIO.connectAsync(5000)
|
|
126
410
|
console.log('Connection established!')
|
|
127
411
|
} catch (error) {
|
|
128
412
|
console.error('Connection failed:', error.message)
|
|
129
413
|
}
|
|
130
|
-
|
|
131
|
-
// Wait for single event
|
|
132
|
-
const userData = await iframeIO.onceAsync('userProfile')
|
|
133
|
-
console.log('User data received:', userData)
|
|
134
414
|
```
|
|
135
415
|
|
|
136
416
|
## Enhanced Connection Management
|
|
@@ -165,7 +445,11 @@ console.log(stats)
|
|
|
165
445
|
// queuedMessages: 0,
|
|
166
446
|
// reconnectAttempts: 0,
|
|
167
447
|
// activeListeners: 5,
|
|
168
|
-
// messageRate: 2
|
|
448
|
+
// messageRate: 2,
|
|
449
|
+
// protocolVersion: 1,
|
|
450
|
+
// peerProtocolVersion: 1,
|
|
451
|
+
// sessionKeyActive: true,
|
|
452
|
+
// sessionKeyId: 'key-1609459200000-abc123'
|
|
169
453
|
// }
|
|
170
454
|
```
|
|
171
455
|
|
|
@@ -175,9 +459,8 @@ console.log(stats)
|
|
|
175
459
|
|
|
176
460
|
```javascript
|
|
177
461
|
// Strict origin checking
|
|
178
|
-
iframeIO.listen('https://trusted-domain.com')
|
|
462
|
+
iframeIO.listen('https://trusted-domain.com')
|
|
179
463
|
|
|
180
|
-
// Error handling for invalid origins
|
|
181
464
|
iframeIO.on('error', (error) => {
|
|
182
465
|
if (error.type === 'INVALID_ORIGIN') {
|
|
183
466
|
console.log(`Rejected message from ${error.received}`)
|
|
@@ -188,72 +471,28 @@ iframeIO.on('error', (error) => {
|
|
|
188
471
|
### Message Sanitization
|
|
189
472
|
|
|
190
473
|
```javascript
|
|
191
|
-
// Automatic payload sanitization
|
|
474
|
+
// Automatic payload sanitization
|
|
192
475
|
iframeIO.emit('data', {
|
|
193
476
|
text: 'Hello',
|
|
194
|
-
func: () => {},
|
|
195
|
-
undef: undefined
|
|
477
|
+
func: () => {}, // Automatically removed
|
|
478
|
+
undef: undefined // Automatically removed
|
|
196
479
|
})
|
|
197
480
|
```
|
|
198
481
|
|
|
199
|
-
###
|
|
482
|
+
### Cryptographic Message Authentication
|
|
200
483
|
|
|
201
484
|
```javascript
|
|
202
485
|
const iframeIO = new IOF({
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
if (error.type === 'RATE_LIMIT_EXCEEDED') {
|
|
208
|
-
console.log(`Rate limited: ${error.current}/${error.limit}`)
|
|
209
|
-
}
|
|
210
|
-
})
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
## Comprehensive Error Handling
|
|
214
|
-
|
|
215
|
-
```javascript
|
|
216
|
-
iframeIO.on('error', (error) => {
|
|
217
|
-
switch (error.type) {
|
|
218
|
-
case 'INVALID_ORIGIN':
|
|
219
|
-
console.error(`Invalid origin: expected ${error.expected}, got ${error.received}`)
|
|
220
|
-
break
|
|
221
|
-
case 'ORIGIN_MISMATCH':
|
|
222
|
-
console.error(`Origin mismatch: expected ${error.expected}, got ${error.received}`)
|
|
223
|
-
break
|
|
224
|
-
case 'RATE_LIMIT_EXCEEDED':
|
|
225
|
-
console.warn(`Rate limit exceeded: ${error.current}/${error.limit} messages/second`)
|
|
226
|
-
break
|
|
227
|
-
case 'MESSAGE_HANDLING_ERROR':
|
|
228
|
-
console.error(`Error handling event ${error.event}: ${error.error}`)
|
|
229
|
-
break
|
|
230
|
-
case 'EMIT_ERROR':
|
|
231
|
-
console.error(`Error sending event ${error.event}: ${error.error}`)
|
|
232
|
-
break
|
|
233
|
-
case 'LISTENER_ERROR':
|
|
234
|
-
console.error(`Error in listener for ${error.event}: ${error.error}`)
|
|
235
|
-
break
|
|
236
|
-
case 'NO_CONNECTION':
|
|
237
|
-
console.error(`Attempted to send ${error.event} without connection`)
|
|
238
|
-
break
|
|
239
|
-
default:
|
|
240
|
-
console.error('Unknown error:', error)
|
|
486
|
+
type: 'WINDOW',
|
|
487
|
+
cryptoAuth: {
|
|
488
|
+
secret: 'replace-with-shared-secret',
|
|
489
|
+
requireSigned: true
|
|
241
490
|
}
|
|
242
491
|
})
|
|
243
|
-
```
|
|
244
|
-
|
|
245
|
-
## Message Queuing
|
|
246
492
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
iframeIO.
|
|
250
|
-
|
|
251
|
-
// Clear queue manually if needed
|
|
252
|
-
iframeIO.clearQueue()
|
|
253
|
-
|
|
254
|
-
// Check queue status
|
|
255
|
-
const stats = iframeIO.getStats()
|
|
256
|
-
console.log(`${stats.queuedMessages} messages queued`)
|
|
493
|
+
// Send signed messages
|
|
494
|
+
await iframeIO.emitSigned('hello', { msg: 'signed' })
|
|
495
|
+
const reply = await iframeIO.emitAsyncSigned('getData', { id: 123 }, 5000)
|
|
257
496
|
```
|
|
258
497
|
|
|
259
498
|
## API Reference
|
|
@@ -261,24 +500,26 @@ console.log(`${stats.queuedMessages} messages queued`)
|
|
|
261
500
|
### New Methods
|
|
262
501
|
|
|
263
502
|
#### Async Methods
|
|
264
|
-
- **`emitAsync(event, payload?, timeout?)`** - Send message and wait for response
|
|
265
|
-
- **`
|
|
266
|
-
- **`
|
|
503
|
+
- **`emitAsync(event, payload?, timeout?)`** - Send message and wait for response
|
|
504
|
+
- **`emitAsyncSigned(event, payload?, timeout?)`** - Send signed message and wait for response
|
|
505
|
+
- **`connectAsync(timeout?)`** - Wait for connection with timeout
|
|
506
|
+
- **`onceAsync(event)`** - Wait for single event
|
|
267
507
|
|
|
268
508
|
#### Utility Methods
|
|
269
|
-
- **`getStats()`** - Get connection statistics
|
|
509
|
+
- **`getStats()`** - Get connection statistics (includes version and session key info)
|
|
270
510
|
- **`clearQueue()`** - Clear queued messages
|
|
271
511
|
|
|
272
512
|
### Connection Methods
|
|
273
513
|
|
|
274
|
-
- **`initiate(contentWindow, iframeOrigin)`** - Establish connection (WINDOW peer
|
|
275
|
-
- **`listen(hostOrigin?)`** - Listen for connection (IFRAME peer
|
|
514
|
+
- **`initiate(contentWindow, iframeOrigin)`** - Establish connection (WINDOW peer)
|
|
515
|
+
- **`listen(hostOrigin?)`** - Listen for connection (IFRAME peer)
|
|
276
516
|
- **`disconnect(callback?)`** - Disconnect and cleanup
|
|
277
517
|
- **`isConnected()`** - Check connection status
|
|
278
518
|
|
|
279
519
|
### Messaging Methods
|
|
280
520
|
|
|
281
521
|
- **`emit(event, payload?, callback?)`** - Send message
|
|
522
|
+
- **`emitSigned(event, payload?, callback?)`** - Send signed message
|
|
282
523
|
- **`on(event, listener)`** - Add event listener
|
|
283
524
|
- **`once(event, listener)`** - Add one-time event listener
|
|
284
525
|
- **`off(event, listener?)`** - Remove event listener(s)
|
|
@@ -292,6 +533,11 @@ console.log(`${stats.queuedMessages} messages queued`)
|
|
|
292
533
|
- **`reconnecting`** - Reconnection attempt started
|
|
293
534
|
- **`reconnection_failed`** - All reconnection attempts failed
|
|
294
535
|
|
|
536
|
+
#### Session Key Events (when enabled)
|
|
537
|
+
- **`session_key_established`** - Session key derived and active
|
|
538
|
+
- **`session_key_rotating`** - New key being generated
|
|
539
|
+
- **`session_key_rotated`** - Peer rotated to new key
|
|
540
|
+
|
|
295
541
|
#### Error Events
|
|
296
542
|
- **`error`** - Various error conditions with detailed error objects
|
|
297
543
|
|
|
@@ -300,33 +546,24 @@ console.log(`${stats.queuedMessages} messages queued`)
|
|
|
300
546
|
Full TypeScript support with comprehensive type definitions:
|
|
301
547
|
|
|
302
548
|
```typescript
|
|
303
|
-
import IOF, { Options, Listener, AckFunction } from 'iframe.io'
|
|
549
|
+
import IOF, { Options, Listener, AckFunction, SessionKeyInfo } from 'iframe.io'
|
|
304
550
|
|
|
305
551
|
const options: Options = {
|
|
306
552
|
type: 'WINDOW',
|
|
307
553
|
debug: true,
|
|
308
|
-
|
|
309
|
-
|
|
554
|
+
cryptoAuth: {
|
|
555
|
+
secret: 'my-secret',
|
|
556
|
+
enableSessionKeys: true,
|
|
557
|
+
sessionKeyRotationInterval: 1800000
|
|
558
|
+
}
|
|
310
559
|
}
|
|
311
560
|
|
|
312
561
|
const iframeIO = new IOF(options)
|
|
313
562
|
|
|
314
|
-
//
|
|
315
|
-
iframeIO.on('
|
|
316
|
-
console.log(`
|
|
563
|
+
// Session key events are typed
|
|
564
|
+
iframeIO.on('session_key_established', (data: { keyId: string }) => {
|
|
565
|
+
console.log(`Key established: ${data.keyId}`)
|
|
317
566
|
})
|
|
318
|
-
|
|
319
|
-
// Typed async responses
|
|
320
|
-
interface ApiResponse {
|
|
321
|
-
success: boolean
|
|
322
|
-
data: any[]
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
const response = await iframeIO.emitAsync<{ query: string }, ApiResponse>(
|
|
326
|
-
'search',
|
|
327
|
-
{ query: 'hello' },
|
|
328
|
-
5000 // 5 second timeout
|
|
329
|
-
)
|
|
330
567
|
```
|
|
331
568
|
|
|
332
569
|
## Error Types Reference
|
|
@@ -335,11 +572,16 @@ const response = await iframeIO.emitAsync<{ query: string }, ApiResponse>(
|
|
|
335
572
|
|------------|-------------|
|
|
336
573
|
| `INVALID_ORIGIN` | Message from unexpected origin |
|
|
337
574
|
| `ORIGIN_MISMATCH` | Origin changed during session |
|
|
575
|
+
| `UNSUPPORTED_VERSION` | Peer using unsupported protocol version |
|
|
338
576
|
| `MESSAGE_HANDLING_ERROR` | Error processing incoming message |
|
|
339
577
|
| `EMIT_ERROR` | Error sending message |
|
|
340
578
|
| `LISTENER_ERROR` | Error in event listener |
|
|
579
|
+
| `DISALLOWED_EVENT` | Event rejected by allowlist |
|
|
580
|
+
| `INVALID_MESSAGE` | Message rejected by validator |
|
|
581
|
+
| `AUTH_FAILED` | Cryptographic authentication failed |
|
|
582
|
+
| `AUTH_ERROR` | Crypto verification errored |
|
|
341
583
|
| `RATE_LIMIT_EXCEEDED` | Too many messages sent |
|
|
342
|
-
| `NO_CONNECTION` | Attempted
|
|
584
|
+
| `NO_CONNECTION` | Attempted send without connection |
|
|
343
585
|
|
|
344
586
|
## Browser Compatibility
|
|
345
587
|
|
|
@@ -348,37 +590,70 @@ const response = await iframeIO.emitAsync<{ query: string }, ApiResponse>(
|
|
|
348
590
|
- Safari 12+
|
|
349
591
|
- Edge 79+
|
|
350
592
|
|
|
593
|
+
WebCrypto API required for session key rotation feature.
|
|
594
|
+
|
|
351
595
|
## Migration Guide
|
|
352
596
|
|
|
353
|
-
### From v1.
|
|
597
|
+
### From v1.2.x to v1.3.0
|
|
354
598
|
|
|
355
599
|
All existing code continues to work without changes. New features are additive:
|
|
356
600
|
|
|
357
601
|
```javascript
|
|
358
602
|
// Old way (still works)
|
|
359
|
-
iframeIO
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
console.log('Result:', result)
|
|
603
|
+
const iframeIO = new IOF({
|
|
604
|
+
type: 'WINDOW',
|
|
605
|
+
cryptoAuth: {
|
|
606
|
+
secret: 'my-secret'
|
|
364
607
|
}
|
|
365
608
|
})
|
|
366
609
|
|
|
367
|
-
// New
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
}
|
|
610
|
+
// New way with session keys (opt-in)
|
|
611
|
+
const iframeIO = new IOF({
|
|
612
|
+
type: 'WINDOW',
|
|
613
|
+
cryptoAuth: {
|
|
614
|
+
secret: 'my-secret',
|
|
615
|
+
enableSessionKeys: true // Enable for high-security apps
|
|
616
|
+
}
|
|
617
|
+
})
|
|
618
|
+
|
|
619
|
+
// Protocol version is automatic - no code changes needed
|
|
374
620
|
```
|
|
375
621
|
|
|
376
622
|
## Performance Considerations
|
|
377
623
|
|
|
378
|
-
- **Message Size**: Keep
|
|
379
|
-
- **Rate Limiting**: Respect
|
|
624
|
+
- **Message Size**: Keep under `maxMessageSize` (default 1MB)
|
|
625
|
+
- **Rate Limiting**: Respect `maxMessagesPerSecond` (default 100/sec)
|
|
380
626
|
- **Queue Size**: Monitor queued messages to avoid memory issues
|
|
381
|
-
- **Heartbeat**: Adjust `heartbeatInterval` based on
|
|
627
|
+
- **Heartbeat**: Adjust `heartbeatInterval` based on needs
|
|
628
|
+
- **Session Keys**: Minimal overhead (~5-10ms derivation, ~1KB memory)
|
|
629
|
+
|
|
630
|
+
## Best Practices
|
|
631
|
+
|
|
632
|
+
### Security
|
|
633
|
+
|
|
634
|
+
1. **Always use HTTPS** for production deployments
|
|
635
|
+
2. **Validate origins** strictly in both peers
|
|
636
|
+
3. **Enable session keys** for applications handling sensitive data
|
|
637
|
+
4. **Use allowedIncomingEvents** to limit attack surface
|
|
638
|
+
5. **Implement custom validation** for critical payloads
|
|
639
|
+
6. **Monitor error events** for security incidents
|
|
640
|
+
7. **Rotate master secrets** periodically (outside of session rotation)
|
|
641
|
+
|
|
642
|
+
### Performance
|
|
643
|
+
|
|
644
|
+
1. **Batch related messages** when possible
|
|
645
|
+
2. **Use appropriate queue sizes** for your use case
|
|
646
|
+
3. **Monitor connection stats** regularly
|
|
647
|
+
4. **Adjust heartbeat intervals** based on reliability needs
|
|
648
|
+
5. **Set reasonable rotation intervals** (1 hour is good default)
|
|
649
|
+
|
|
650
|
+
### Reliability
|
|
651
|
+
|
|
652
|
+
1. **Handle all error types** appropriately
|
|
653
|
+
2. **Implement retry logic** at application level for critical operations
|
|
654
|
+
3. **Use `emitAsync`** for operations requiring acknowledgment
|
|
655
|
+
4. **Monitor reconnection events** and alert on failures
|
|
656
|
+
5. **Test disconnection scenarios** thoroughly
|
|
382
657
|
|
|
383
658
|
## License
|
|
384
659
|
|
|
@@ -393,3 +668,25 @@ Contributions are welcome! Please read our contributing guidelines and submit pu
|
|
|
393
668
|
- Create an issue on GitHub for bug reports
|
|
394
669
|
- Check existing issues for common problems
|
|
395
670
|
- Review the documentation for usage examples
|
|
671
|
+
|
|
672
|
+
## Changelog
|
|
673
|
+
|
|
674
|
+
### v1.3.0
|
|
675
|
+
- Added protocol versioning for future-proof evolution
|
|
676
|
+
- Added optional session key rotation for high-security applications
|
|
677
|
+
- Enhanced connection statistics with version and key information
|
|
678
|
+
- Improved error handling for version mismatches
|
|
679
|
+
- Added comprehensive documentation and examples
|
|
680
|
+
|
|
681
|
+
### v1.2.0
|
|
682
|
+
- Enhanced security features (origin validation, sanitization, rate limiting)
|
|
683
|
+
- Auto-reconnection with exponential backoff
|
|
684
|
+
- Heartbeat monitoring
|
|
685
|
+
- Message queuing
|
|
686
|
+
- Modern async/await APIs
|
|
687
|
+
- Comprehensive error handling
|
|
688
|
+
|
|
689
|
+
### v1.1.0
|
|
690
|
+
- Initial release
|
|
691
|
+
- Basic iframe communication
|
|
692
|
+
- Event-based API
|