api-ape 3.0.2 → 4.1.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 (186) hide show
  1. package/README.md +59 -572
  2. package/client/README.md +73 -14
  3. package/client/auth/crypto/aead.js +214 -0
  4. package/client/auth/crypto/constants.js +32 -0
  5. package/client/auth/crypto/encoding.js +104 -0
  6. package/client/auth/crypto/files.md +27 -0
  7. package/client/auth/crypto/kdf.js +217 -0
  8. package/client/auth/crypto-utils.js +118 -0
  9. package/client/auth/files.md +52 -0
  10. package/client/auth/key-recovery.js +288 -0
  11. package/client/auth/recovery/constants.js +37 -0
  12. package/client/auth/recovery/files.md +23 -0
  13. package/client/auth/recovery/key-derivation.js +61 -0
  14. package/client/auth/recovery/sss-browser.js +189 -0
  15. package/client/auth/share-storage.js +205 -0
  16. package/client/auth/storage/constants.js +18 -0
  17. package/client/auth/storage/db.js +132 -0
  18. package/client/auth/storage/files.md +27 -0
  19. package/client/auth/storage/keys.js +173 -0
  20. package/client/auth/storage/shares.js +200 -0
  21. package/client/browser.js +190 -23
  22. package/client/connectSocket.js +418 -988
  23. package/client/connection/README.md +23 -0
  24. package/client/connection/fileDownload.js +256 -0
  25. package/client/connection/fileHandling.js +450 -0
  26. package/client/connection/fileUtils.js +346 -0
  27. package/client/connection/files.md +71 -0
  28. package/client/connection/messageHandler.js +105 -0
  29. package/client/connection/network.js +350 -0
  30. package/client/connection/proxy.js +233 -0
  31. package/client/connection/sender.js +333 -0
  32. package/client/connection/state.js +321 -0
  33. package/client/connection/subscriptions.js +151 -0
  34. package/client/files.md +53 -0
  35. package/client/index.js +298 -142
  36. package/client/transports/README.md +50 -0
  37. package/client/transports/files.md +41 -0
  38. package/client/transports/streamParser.js +195 -0
  39. package/client/transports/streaming.js +555 -203
  40. package/dist/ape.js +6 -1
  41. package/dist/ape.js.map +4 -4
  42. package/index.d.ts +38 -16
  43. package/package.json +31 -6
  44. package/server/README.md +272 -67
  45. package/server/adapters/README.md +23 -14
  46. package/server/adapters/files.md +68 -0
  47. package/server/adapters/firebase.js +543 -160
  48. package/server/adapters/index.js +362 -112
  49. package/server/adapters/mongo.js +530 -140
  50. package/server/adapters/postgres.js +534 -155
  51. package/server/adapters/redis.js +508 -143
  52. package/server/adapters/supabase.js +555 -186
  53. package/server/client/README.md +43 -0
  54. package/server/client/connection.js +586 -0
  55. package/server/client/files.md +40 -0
  56. package/server/client/index.js +342 -0
  57. package/server/files.md +54 -0
  58. package/server/index.js +322 -71
  59. package/server/lib/README.md +26 -0
  60. package/server/lib/broadcast/clients.js +219 -0
  61. package/server/lib/broadcast/files.md +58 -0
  62. package/server/lib/broadcast/index.js +57 -0
  63. package/server/lib/broadcast/publishProxy.js +110 -0
  64. package/server/lib/broadcast/pubsub.js +137 -0
  65. package/server/lib/broadcast/sendProxy.js +103 -0
  66. package/server/lib/bun.js +315 -99
  67. package/server/lib/fileTransfer/README.md +63 -0
  68. package/server/lib/fileTransfer/files.md +30 -0
  69. package/server/lib/fileTransfer/streaming.js +435 -0
  70. package/server/lib/fileTransfer.js +710 -326
  71. package/server/lib/files.md +111 -0
  72. package/server/lib/httpUtils.js +283 -0
  73. package/server/lib/loader.js +208 -7
  74. package/server/lib/longPolling/README.md +63 -0
  75. package/server/lib/longPolling/files.md +44 -0
  76. package/server/lib/longPolling/getHandler.js +365 -0
  77. package/server/lib/longPolling/postHandler.js +327 -0
  78. package/server/lib/longPolling.js +174 -219
  79. package/server/lib/main.js +369 -532
  80. package/server/lib/runtimes/README.md +42 -0
  81. package/server/lib/runtimes/bun.js +586 -0
  82. package/server/lib/runtimes/files.md +56 -0
  83. package/server/lib/runtimes/node.js +511 -0
  84. package/server/lib/wiring.js +539 -98
  85. package/server/lib/ws/README.md +35 -0
  86. package/server/lib/ws/adapters/README.md +54 -0
  87. package/server/lib/ws/adapters/bun.js +538 -170
  88. package/server/lib/ws/adapters/deno.js +623 -149
  89. package/server/lib/ws/adapters/files.md +42 -0
  90. package/server/lib/ws/files.md +74 -0
  91. package/server/lib/ws/frames.js +532 -154
  92. package/server/lib/ws/index.js +207 -10
  93. package/server/lib/ws/server.js +385 -92
  94. package/server/lib/ws/socket.js +549 -181
  95. package/server/lib/wsProvider.js +363 -89
  96. package/server/plugins/binary.js +282 -0
  97. package/server/security/README.md +92 -0
  98. package/server/security/auth/README.md +319 -0
  99. package/server/security/auth/adapters/files.md +95 -0
  100. package/server/security/auth/adapters/ldap/constants.js +37 -0
  101. package/server/security/auth/adapters/ldap/files.md +19 -0
  102. package/server/security/auth/adapters/ldap/helpers.js +111 -0
  103. package/server/security/auth/adapters/ldap.js +353 -0
  104. package/server/security/auth/adapters/oauth2/constants.js +41 -0
  105. package/server/security/auth/adapters/oauth2/files.md +19 -0
  106. package/server/security/auth/adapters/oauth2/helpers.js +123 -0
  107. package/server/security/auth/adapters/oauth2.js +273 -0
  108. package/server/security/auth/adapters/opaque-handlers.js +314 -0
  109. package/server/security/auth/adapters/opaque.js +205 -0
  110. package/server/security/auth/adapters/saml/constants.js +52 -0
  111. package/server/security/auth/adapters/saml/files.md +19 -0
  112. package/server/security/auth/adapters/saml/helpers.js +74 -0
  113. package/server/security/auth/adapters/saml.js +173 -0
  114. package/server/security/auth/adapters/totp.js +703 -0
  115. package/server/security/auth/adapters/webauthn.js +625 -0
  116. package/server/security/auth/files.md +61 -0
  117. package/server/security/auth/framework/constants.js +27 -0
  118. package/server/security/auth/framework/files.md +23 -0
  119. package/server/security/auth/framework/handlers.js +272 -0
  120. package/server/security/auth/framework/socket-auth.js +177 -0
  121. package/server/security/auth/handlers/auth-messages.js +143 -0
  122. package/server/security/auth/handlers/files.md +28 -0
  123. package/server/security/auth/index.js +290 -0
  124. package/server/security/auth/mfa/crypto/aead.js +148 -0
  125. package/server/security/auth/mfa/crypto/constants.js +35 -0
  126. package/server/security/auth/mfa/crypto/files.md +27 -0
  127. package/server/security/auth/mfa/crypto/kdf.js +120 -0
  128. package/server/security/auth/mfa/crypto/utils.js +68 -0
  129. package/server/security/auth/mfa/crypto-utils.js +80 -0
  130. package/server/security/auth/mfa/files.md +77 -0
  131. package/server/security/auth/mfa/ledger/constants.js +75 -0
  132. package/server/security/auth/mfa/ledger/errors.js +73 -0
  133. package/server/security/auth/mfa/ledger/files.md +23 -0
  134. package/server/security/auth/mfa/ledger/share-record.js +32 -0
  135. package/server/security/auth/mfa/ledger.js +255 -0
  136. package/server/security/auth/mfa/recovery/constants.js +67 -0
  137. package/server/security/auth/mfa/recovery/files.md +19 -0
  138. package/server/security/auth/mfa/recovery/handlers.js +216 -0
  139. package/server/security/auth/mfa/recovery.js +191 -0
  140. package/server/security/auth/mfa/sss/constants.js +21 -0
  141. package/server/security/auth/mfa/sss/files.md +23 -0
  142. package/server/security/auth/mfa/sss/gf256.js +103 -0
  143. package/server/security/auth/mfa/sss/serialization.js +82 -0
  144. package/server/security/auth/mfa/sss.js +161 -0
  145. package/server/security/auth/mfa/two-of-three/constants.js +58 -0
  146. package/server/security/auth/mfa/two-of-three/files.md +23 -0
  147. package/server/security/auth/mfa/two-of-three/handlers.js +241 -0
  148. package/server/security/auth/mfa/two-of-three/helpers.js +71 -0
  149. package/server/security/auth/mfa/two-of-three.js +136 -0
  150. package/server/security/auth/nonce-manager.js +89 -0
  151. package/server/security/auth/state-machine-mfa.js +269 -0
  152. package/server/security/auth/state-machine.js +257 -0
  153. package/server/security/extractRootDomain.js +144 -16
  154. package/server/security/files.md +51 -0
  155. package/server/security/origin.js +197 -15
  156. package/server/security/reply.js +274 -16
  157. package/server/socket/README.md +119 -0
  158. package/server/socket/authMiddleware.js +299 -0
  159. package/server/socket/files.md +86 -0
  160. package/server/socket/open.js +154 -8
  161. package/server/socket/pluginHooks.js +334 -0
  162. package/server/socket/receive.js +184 -224
  163. package/server/socket/receiveContext.js +117 -0
  164. package/server/socket/send.js +416 -78
  165. package/server/socket/tagUtils.js +402 -0
  166. package/server/utils/README.md +19 -0
  167. package/server/utils/deepRequire.js +255 -30
  168. package/server/utils/files.md +57 -0
  169. package/server/utils/genId.js +182 -20
  170. package/server/utils/parseUserAgent.js +313 -251
  171. package/server/utils/userAgent/README.md +65 -0
  172. package/server/utils/userAgent/files.md +46 -0
  173. package/server/utils/userAgent/patterns.js +545 -0
  174. package/utils/README.md +21 -0
  175. package/utils/files.md +66 -0
  176. package/utils/jss/README.md +21 -0
  177. package/utils/jss/decode.js +471 -0
  178. package/utils/jss/encode.js +312 -0
  179. package/utils/jss/files.md +68 -0
  180. package/utils/jss/plugins.js +210 -0
  181. package/utils/jss.js +219 -273
  182. package/utils/messageHash.js +238 -35
  183. package/dist/api-ape.min.js +0 -2
  184. package/dist/api-ape.min.js.map +0 -7
  185. package/server/client.js +0 -311
  186. package/server/lib/broadcast.js +0 -146
@@ -1,21 +1,279 @@
1
- module.exports = function(){
2
- let requestCheck = []
3
- return (queryId,createdAt)=>{
1
+ /**
2
+ * @fileoverview Replay Attack Prevention for api-ape Server
3
+ *
4
+ * This module provides protection against replay attacks on WebSocket messages.
5
+ * Replay attacks occur when an attacker intercepts valid messages and resends
6
+ * them to perform unauthorized actions.
7
+ *
8
+ * ## How Replay Attacks Work
9
+ *
10
+ * ```
11
+ * Normal Request:
12
+ * Client ──────► Server
13
+ * { type: '/transfer', data: { amount: 100 } }
14
+ *
15
+ * Replay Attack:
16
+ * Attacker captures message and resends it multiple times:
17
+ * Attacker ──────► Server (same message)
18
+ * Attacker ──────► Server (same message)
19
+ * Attacker ──────► Server (same message)
20
+ * Result: Multiple transfers instead of one!
21
+ * ```
22
+ *
23
+ * ## Protection Mechanism
24
+ *
25
+ * This module prevents replay attacks using two strategies:
26
+ *
27
+ * ### 1. Request ID Tracking
28
+ *
29
+ * Each message has a unique queryId generated from its content hash.
30
+ * The server tracks recently seen queryIds and rejects duplicates.
31
+ *
32
+ * ```
33
+ * Request 1: queryId = "K7M3NP" ✓ (new, allowed)
34
+ * Request 2: queryId = "K7M3NP" ✗ (duplicate, rejected)
35
+ * Request 3: queryId = "X9W2QR" ✓ (new, allowed)
36
+ * ```
37
+ *
38
+ * ### 2. Timestamp Validation
39
+ *
40
+ * Messages include a `createdAt` timestamp. The server rejects messages that:
41
+ * - Are too old (> 10 seconds in the past)
42
+ * - Are from the future (clock skew attack)
43
+ *
44
+ * ```
45
+ * Server Time: 12:00:00
46
+ *
47
+ * createdAt: 11:59:55 ✓ (5 seconds ago, valid)
48
+ * createdAt: 11:59:45 ✗ (15 seconds ago, too old)
49
+ * createdAt: 12:00:05 ✗ (5 seconds in future, invalid)
50
+ * ```
51
+ *
52
+ * ## Window Management
53
+ *
54
+ * The request tracking window is limited to 10 seconds worth of requests.
55
+ * Older entries are automatically purged to prevent memory growth.
56
+ *
57
+ * ```
58
+ * ┌──────────────────────────────────────────────────────────────┐
59
+ * │ 10-second Window │
60
+ * │ │
61
+ * │ [old requests purged] ← ─ ─ ─ [tracked requests] ─ ─ ─ → [now] │
62
+ * │ │
63
+ * │ Requests older than 10 seconds are removed from tracking │
64
+ * │ New duplicates within window are rejected │
65
+ * └──────────────────────────────────────────────────────────────┘
66
+ * ```
67
+ *
68
+ * ## Per-Connection Isolation
69
+ *
70
+ * Each WebSocket connection gets its own replay checker instance.
71
+ * This provides:
72
+ * - Isolation between clients (one client's requests don't affect another)
73
+ * - Automatic cleanup when connection closes
74
+ * - Memory efficiency (only tracks requests for active connections)
75
+ *
76
+ * @module server/security/reply
77
+ * @see {@link module:server/socket/receive} for usage in message handling
78
+ * @see {@link module:utils/messageHash} for queryId generation
79
+ *
80
+ * @example <caption>Creating a Replay Checker</caption>
81
+ * const replySecurity = require('./reply')
82
+ *
83
+ * // Create checker for a connection
84
+ * const checkReply = replySecurity()
85
+ *
86
+ * // In message handler
87
+ * try {
88
+ * checkReply(queryId, createdAt)
89
+ * // Request is valid - process it
90
+ * } catch (err) {
91
+ * // Request is invalid - replay or expired
92
+ * console.error('Replay attack or stale request:', err.message)
93
+ * }
94
+ *
95
+ * @example <caption>Integration with Socket Receive</caption>
96
+ * // In wiring.js
97
+ * const checkReply = replySecurity()
98
+ *
99
+ * socket.on('message', async (msg) => {
100
+ * const { queryId, createdAt, type, data } = parseMessage(msg)
101
+ *
102
+ * try {
103
+ * checkReply(queryId, createdAt)
104
+ * const result = await controllers[type](data)
105
+ * send(queryId, null, result, null)
106
+ * } catch (err) {
107
+ * send(queryId, null, null, err)
108
+ * }
109
+ * })
110
+ *
111
+ * @example <caption>Error Messages</caption>
112
+ * // Future timestamp
113
+ * checkReply('abc', Date.now() + 60000)
114
+ * // Throws: "createdAt ahead of server by 60 secs"
115
+ *
116
+ * // Old timestamp
117
+ * checkReply('abc', Date.now() - 30000)
118
+ * // Throws: "request is old by 30 secs"
119
+ *
120
+ * // Duplicate request
121
+ * checkReply('abc', Date.now()) // First call - OK
122
+ * checkReply('abc', Date.now()) // Second call
123
+ * // Throws: "Reply: abc"
124
+ */
125
+
126
+ /**
127
+ * Create a replay attack checker for a WebSocket connection
128
+ *
129
+ * Returns a function that validates incoming requests against:
130
+ * - Previously seen request IDs (prevents duplicates)
131
+ * - Request timestamps (prevents stale/future requests)
132
+ *
133
+ * Each checker maintains its own request history, isolated from other
134
+ * connections. The history is automatically pruned to only keep requests
135
+ * from the last 10 seconds.
136
+ *
137
+ * @returns {function(string, number): void} Check function that throws on invalid requests
138
+ *
139
+ * @example
140
+ * // Create a checker for this connection
141
+ * const checkReply = replySecurity()
142
+ *
143
+ * // Check a request (throws if invalid)
144
+ * checkReply('queryId123', Date.now())
145
+ *
146
+ * // Same queryId again - throws
147
+ * try {
148
+ * checkReply('queryId123', Date.now())
149
+ * } catch (err) {
150
+ * console.log(err.message) // "Reply: queryId123"
151
+ * }
152
+ */
153
+ module.exports = function () {
154
+ /**
155
+ * Array of recently seen requests
156
+ *
157
+ * Each entry is a tuple of [queryId, createdAt timestamp].
158
+ * Entries older than 10 seconds are pruned on each check.
159
+ *
160
+ * @type {Array<[string, number]>}
161
+ * @private
162
+ */
163
+ let requestCheck = [];
164
+
165
+ /**
166
+ * Validate a request against replay attacks
167
+ *
168
+ * Performs three validations:
169
+ * 1. **Future check**: Rejects if createdAt is ahead of server time
170
+ * 2. **Staleness check**: Rejects if createdAt is more than 10 seconds old
171
+ * 3. **Duplicate check**: Rejects if queryId was seen in the last 10 seconds
172
+ *
173
+ * If validation passes, the request is added to the tracking list.
174
+ *
175
+ * ## Time Tolerance
176
+ *
177
+ * The 10-second window provides:
178
+ * - Tolerance for minor network latency
179
+ * - Tolerance for minor clock skew between client and server
180
+ * - Protection against replay attacks (attacker must replay within 10 seconds)
181
+ * - Memory efficiency (limited tracking window)
182
+ *
183
+ * @param {string} queryId - Unique request identifier (hash of message content)
184
+ * @param {number} createdAt - Timestamp when the request was created (client-side)
185
+ * @throws {Error} If request is from the future (clock skew)
186
+ * @throws {Error} If request is too old (> 10 seconds)
187
+ * @throws {Error} If request is a duplicate (same queryId within window)
188
+ *
189
+ * @example
190
+ * // Valid request
191
+ * checkReply('K7M3NP2Q', Date.now() - 1000) // 1 second ago - OK
192
+ *
193
+ * @example
194
+ * // Future request (clock skew or tampering)
195
+ * checkReply('K7M3NP2Q', Date.now() + 5000)
196
+ * // Error: "createdAt ahead of server by 5 secs"
197
+ *
198
+ * @example
199
+ * // Stale request
200
+ * checkReply('K7M3NP2Q', Date.now() - 15000)
201
+ * // Error: "request is old by 15 secs"
202
+ *
203
+ * @example
204
+ * // Duplicate request (replay attack)
205
+ * checkReply('K7M3NP2Q', Date.now()) // First - OK
206
+ * checkReply('K7M3NP2Q', Date.now()) // Second - Error
207
+ * // Error: "Reply: K7M3NP2Q"
208
+ */
209
+ return (queryId, createdAt) => {
210
+ /**
211
+ * Current server timestamp
212
+ * @type {number}
213
+ */
4
214
  const startTime = Date.now();
5
- if (createdAt > startTime) {
6
- throw new Error("createdAt ahead of server by `${(createdAt - startTime) / 1000}secs. +${msg}`")
215
+
216
+ /**
217
+ * Check 1: Reject requests from the future
218
+ *
219
+ * This catches:
220
+ * - Client clock running ahead
221
+ * - Timestamp manipulation attacks
222
+ * - Replay of pre-generated future requests
223
+ */
224
+ if (createdAt > startTime) {
225
+ const skewSeconds = (createdAt - startTime) / 1000;
226
+ throw new Error(
227
+ `createdAt ahead of server by ${skewSeconds} secs. Request rejected.`,
228
+ );
7
229
  }
8
- const tenSecAgo = startTime - 10000
9
- if(createdAt < tenSecAgo) {
10
- throw new Error("request is old by `${(startTime - createdAt) / 1000}secs. +${msg}`")
230
+
231
+ /**
232
+ * Calculate the cutoff time (10 seconds ago)
233
+ * Requests older than this are considered stale
234
+ * @type {number}
235
+ */
236
+ const tenSecAgo = startTime - 10000;
237
+
238
+ /**
239
+ * Check 2: Reject stale requests
240
+ *
241
+ * This catches:
242
+ * - Slow replay attacks
243
+ * - Network issues causing extreme delays
244
+ * - Replay of old captured requests
245
+ */
246
+ if (createdAt < tenSecAgo) {
247
+ const staleSeconds = (startTime - createdAt) / 1000;
248
+ throw new Error(
249
+ `request is old by ${staleSeconds} secs. Request rejected.`,
250
+ );
11
251
  }
12
-
13
- requestCheck = requestCheck.filter(([passQueryId,createdWhen])=>{
252
+
253
+ /**
254
+ * Check 3: Reject duplicates and prune old entries
255
+ *
256
+ * Filter the request history to:
257
+ * 1. Check if this queryId was already seen (throws if found)
258
+ * 2. Remove entries older than 10 seconds (memory cleanup)
259
+ *
260
+ * This single pass efficiently handles both operations.
261
+ */
262
+ requestCheck = requestCheck.filter(([passQueryId, createdWhen]) => {
263
+ // If we find the same queryId, it's a replay attack
14
264
  if (passQueryId === queryId) {
15
- throw new Error(`Reply: ${queryId} ${msg}`)
265
+ throw new Error(`Reply: ${queryId} - Duplicate request rejected.`);
16
266
  }
17
- return createdWhen > tenSecAgo
18
- })
19
- requestCheck.push([queryId,createdAt])
20
- } // END checkReply
21
- } // END replySecurity
267
+ // Keep entries that are still within the 10-second window
268
+ return createdWhen > tenSecAgo;
269
+ });
270
+
271
+ /**
272
+ * Request passed all checks - add to tracking list
273
+ *
274
+ * Store [queryId, createdAt] for future duplicate detection.
275
+ * This entry will be automatically pruned after 10 seconds.
276
+ */
277
+ requestCheck.push([queryId, createdAt]);
278
+ };
279
+ };
@@ -0,0 +1,119 @@
1
+ # Socket Module
2
+
3
+ ## Overview
4
+
5
+ The socket module handles WebSocket message processing for api-ape servers. It manages the complete lifecycle of messages flowing between clients and controllers—from initial connection validation through message parsing, controller invocation, and response serialization.
6
+
7
+ **Key capabilities:**
8
+
9
+ - **Connection validation** — Verify origin headers and enforce security policies before accepting connections
10
+ - **Message parsing** — Deserialize incoming WebSocket messages with JSS encoding support
11
+ - **Controller routing** — Route messages to the appropriate controller based on the `type` field
12
+ - **Pub/sub handling** — Process `subscribe` and `unsubscribe` messages for channel subscriptions
13
+ - **Binary data coordination** — Detect upload tags, wait for HTTP uploads, and inject binary data into messages
14
+ - **Response serialization** — Serialize controller responses and handle binary data with download links
15
+ - **Request correlation** — Match responses to requests via `queryId` for Promise resolution on the client
16
+
17
+ This module is the bridge between raw WebSocket frames and the high-level controller functions that developers write.
18
+
19
+ > **Contributing?** See [`files.md`](./files.md) for directory structure and file descriptions.
20
+
21
+ ## Message Flow
22
+
23
+ ```
24
+ ┌──────────────────────────────────────────────────────────────────┐
25
+ │ Incoming Message │
26
+ ├──────────────────────────────────────────────────────────────────┤
27
+ │ │
28
+ │ WebSocket Frame │
29
+ │ │ │
30
+ │ ▼ │
31
+ │ receive.js │
32
+ │ │ Parse JSS → { type, data, queryId } │
33
+ │ │ Find upload tags → [{ path, hash, tag }] │
34
+ │ │ Wait for HTTP uploads (if any) │
35
+ │ │ Inject binary data at paths │
36
+ │ ▼ │
37
+ │ Controller │
38
+ │ │ this = { clientId, sessionId, broadcast, ...embed } │
39
+ │ │ return result │
40
+ │ ▼ │
41
+ │ send.js │
42
+ │ │ Detect Buffers → register downloads → add <!L> tags │
43
+ │ │ Serialize with JSS │
44
+ │ ▼ │
45
+ │ WebSocket Frame → Client │
46
+ │ │
47
+ └──────────────────────────────────────────────────────────────────┘
48
+ ```
49
+
50
+ ## Authentication & Authorization
51
+
52
+ The socket module integrates with the auth system to enforce access control:
53
+
54
+ ### Authorization Middleware
55
+
56
+ The `authMiddleware.js` module checks tier and permission requirements before controller dispatch:
57
+
58
+ ```js
59
+ const { createAuthMiddleware } = require('api-ape/server/socket/authMiddleware');
60
+
61
+ const authMiddleware = createAuthMiddleware({
62
+ requirements: {
63
+ 'admin/*': { tier: 2, permissions: ['admin.access'] },
64
+ 'user/*': { tier: 1 },
65
+ 'public/*': { tier: 0 }
66
+ },
67
+ defaultTier: 0,
68
+ requireAuthByDefault: false
69
+ });
70
+ ```
71
+
72
+ ### Controller Context
73
+
74
+ When auth is configured, controllers have access to auth state via `this`:
75
+
76
+ | Property | Type | Description |
77
+ |----------|------|-------------|
78
+ | `this.isAuthenticated` | `boolean` | Whether socket is authenticated (Tier ≥ 1) |
79
+ | `this.authTier` | `number` | Current tier (0-3) |
80
+ | `this.principal` | `object\|null` | User info: `{ userId, roles, permissions }` |
81
+ | `this.authState` | `object\|null` | Full auth state object |
82
+ | `this.requiresTier(n)` | `function` | Check if socket meets minimum tier |
83
+
84
+ ### Message Flow with Auth
85
+
86
+ ```
87
+ ┌──────────────────────────────────────────────────────────────────┐
88
+ │ Incoming Message │
89
+ ├──────────────────────────────────────────────────────────────────┤
90
+ │ │
91
+ │ WebSocket Frame │
92
+ │ │ │
93
+ │ ▼ │
94
+ │ receive.js │
95
+ │ │ Parse JSS → { type, data, queryId } │
96
+ │ │ │
97
+ │ │ ── Is auth message? ────────────────────────────────── │
98
+ │ │ │ YES: Route to auth handler (opaque_*, mfa_*) │
99
+ │ │ │ NO: Continue to authorization │
100
+ │ │ ▼ │
101
+ │ │ ── Authorization check ─────────────────────────────── │
102
+ │ │ │ FAIL: Return authz_fail │
103
+ │ │ │ PASS: Continue to controller │
104
+ │ ▼ │
105
+ │ Controller │
106
+ │ │ this = { clientId, isAuthenticated, principal, ... } │
107
+ │ ▼ │
108
+ │ send.js → Client │
109
+ │ │
110
+ └──────────────────────────────────────────────────────────────────┘
111
+ ```
112
+
113
+ ## See Also
114
+
115
+ - [`../lib/wiring.js`](../lib/wiring.js) — WebSocket connection setup that calls these handlers
116
+ - [`../lib/fileTransfer.js`](../lib/fileTransfer.js) — Binary file transfer coordination
117
+ - [`../security/README.md`](../security/README.md) — Origin validation used by `open.js`
118
+ - [`../security/auth/README.md`](../security/auth/README.md) — Full authentication documentation
119
+ - [`tagUtils.js` JSDoc](./tagUtils.js) — Detailed tag system documentation