@rapidmx/meet-plugin 0.9.1 → 0.9.2

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.
@@ -40,9 +40,11 @@
40
40
  * request carries no `Authorization` header at all and instead relies on `fetch()`'s own default same-origin
41
41
  * credentials mode, which already attaches the browser's real `jwt` cookie to a same-origin request with zero
42
42
  * extra code - `JWTStrategy.getAuthToken()`'s cookie fallback authenticates it exactly as it would any other
43
- * same-origin authenticated call. Fire-and-forget either way, matching every other push publish in this codebase -
44
- * a failed send is logged, never thrown, since a caller (`MeshConnectionManager`) has no meaningful per-message
45
- * retry of its own.
43
+ * same-origin authenticated call. Either way `send()` also attaches the CSRF double-submit header
44
+ * (`withCsrfHeader()`) - required server-side only for the cookie case, but harmless to send for the bearer one
45
+ * too - see `send()`'s own doc comment. Fire-and-forget either way, matching every other push publish in this
46
+ * codebase - a failed send is logged, never thrown, since a caller (`MeshConnectionManager`) has no meaningful
47
+ * per-message retry of its own.
46
48
  * - An incoming meeting message arrives wrapped `{ type: "MESSAGE", channel, data: <the posted body> }` - this
47
49
  * client unwraps it and delivers `data` (expected to be a `SignalMessage`) to `onMessage()` listeners.
48
50
  *
@@ -63,7 +65,7 @@
63
65
  * absent, as described above - the browser's real cookie was always going to win that write anyway, so the
64
66
  * correct behavior is to not fight it and let it authenticate normally instead.
65
67
  */
66
- import { apiOrigin } from "@rapidmx/web-client/lib/util/api.js";
68
+ import { apiOrigin, withCsrfHeader } from "@rapidmx/web-client/lib/util/api.js";
67
69
  import { pushUrl } from "@rapidmx/web-client/lib/mail/pushClient.js";
68
70
  import type { SignalMessage, SignalingChannel } from "../webrtc/types.js";
69
71
 
@@ -174,7 +176,16 @@ export class GuestSignalingClient implements SignalingChannel {
174
176
 
175
177
  /** Publishes over `POST /push/:id`, with `token` as a bearer credential when present - see this module's doc
176
178
  * comment on why this, unlike subscribing, needs no cookie workaround at all, and on the omitted-`token` case
177
- * (an already-authenticated real caller), which relies on the browser's own real `jwt` cookie instead.
179
+ * (an already-authenticated real caller), which relies on the browser's own real `jwt` cookie instead - and,
180
+ * because that's a *cookie*-authenticated request, also needs the matching CSRF header
181
+ * (`@rapidmx/web-client`'s `withCsrfHeader()`, the same double-submit echo `apiFetch()` applies to every other
182
+ * mutating call this server answers): without it, `RouteUtils.checkCsrf()` refuses the request with 403
183
+ * `"This request is missing a valid CSRF token."` before it ever reaches `BasePushRoute.send()`'s own ACL
184
+ * check - every `hello`/offer/answer/ICE candidate/`bye` a participant on that path sends 403s and is
185
+ * silently dropped (`send()` is fire-and-forget), which looks exactly like "stuck connecting, then no audio or
186
+ * video" from everyone else's side. `withCsrfHeader()` is harmless to also add on the bearer-token (guest)
187
+ * branch - the server only enforces the check for `req.auth.source === "cookie"` - so it's applied
188
+ * unconditionally here rather than only for the cookie case.
178
189
  * Fire-and-forget: a failure is swallowed (there is nothing a signaling message's own sender can usefully do
179
190
  * about a dropped publish beyond what the mesh's own `hello` re-announcement/renegotiation already tolerates -
180
191
  * see `MeshConnectionManager`'s doc comment on fire-and-forget delivery). */
@@ -190,7 +201,7 @@ export class GuestSignalingClient implements SignalingChannel {
190
201
  }
191
202
  void fetchImpl(`${origin}/push/${encodeURIComponent(this.options.channel)}`, {
192
203
  method: "POST",
193
- headers,
204
+ headers: withCsrfHeader(headers, "POST"),
194
205
  body: JSON.stringify(message),
195
206
  // Only a `bye` needs to survive the page unloading - every other message is sent while the page (and
196
207
  // the rest of this call's signaling) is very much alive. `keepalive` is a real-world liability to set
@@ -60,7 +60,16 @@ export declare class GuestSignalingClient implements SignalingChannel {
60
60
  onMessage(handler: (message: SignalMessage) => void): () => void;
61
61
  /** Publishes over `POST /push/:id`, with `token` as a bearer credential when present - see this module's doc
62
62
  * comment on why this, unlike subscribing, needs no cookie workaround at all, and on the omitted-`token` case
63
- * (an already-authenticated real caller), which relies on the browser's own real `jwt` cookie instead.
63
+ * (an already-authenticated real caller), which relies on the browser's own real `jwt` cookie instead - and,
64
+ * because that's a *cookie*-authenticated request, also needs the matching CSRF header
65
+ * (`@rapidmx/web-client`'s `withCsrfHeader()`, the same double-submit echo `apiFetch()` applies to every other
66
+ * mutating call this server answers): without it, `RouteUtils.checkCsrf()` refuses the request with 403
67
+ * `"This request is missing a valid CSRF token."` before it ever reaches `BasePushRoute.send()`'s own ACL
68
+ * check - every `hello`/offer/answer/ICE candidate/`bye` a participant on that path sends 403s and is
69
+ * silently dropped (`send()` is fire-and-forget), which looks exactly like "stuck connecting, then no audio or
70
+ * video" from everyone else's side. `withCsrfHeader()` is harmless to also add on the bearer-token (guest)
71
+ * branch - the server only enforces the check for `req.auth.source === "cookie"` - so it's applied
72
+ * unconditionally here rather than only for the cookie case.
64
73
  * Fire-and-forget: a failure is swallowed (there is nothing a signaling message's own sender can usefully do
65
74
  * about a dropped publish beyond what the mesh's own `hello` re-announcement/renegotiation already tolerates -
66
75
  * see `MeshConnectionManager`'s doc comment on fire-and-forget delivery). */
@@ -40,9 +40,11 @@
40
40
  * request carries no `Authorization` header at all and instead relies on `fetch()`'s own default same-origin
41
41
  * credentials mode, which already attaches the browser's real `jwt` cookie to a same-origin request with zero
42
42
  * extra code - `JWTStrategy.getAuthToken()`'s cookie fallback authenticates it exactly as it would any other
43
- * same-origin authenticated call. Fire-and-forget either way, matching every other push publish in this codebase -
44
- * a failed send is logged, never thrown, since a caller (`MeshConnectionManager`) has no meaningful per-message
45
- * retry of its own.
43
+ * same-origin authenticated call. Either way `send()` also attaches the CSRF double-submit header
44
+ * (`withCsrfHeader()`) - required server-side only for the cookie case, but harmless to send for the bearer one
45
+ * too - see `send()`'s own doc comment. Fire-and-forget either way, matching every other push publish in this
46
+ * codebase - a failed send is logged, never thrown, since a caller (`MeshConnectionManager`) has no meaningful
47
+ * per-message retry of its own.
46
48
  * - An incoming meeting message arrives wrapped `{ type: "MESSAGE", channel, data: <the posted body> }` - this
47
49
  * client unwraps it and delivers `data` (expected to be a `SignalMessage`) to `onMessage()` listeners.
48
50
  *
@@ -63,7 +65,7 @@
63
65
  * absent, as described above - the browser's real cookie was always going to win that write anyway, so the
64
66
  * correct behavior is to not fight it and let it authenticate normally instead.
65
67
  */
66
- import { apiOrigin } from "@rapidmx/web-client/lib/util/api.js";
68
+ import { apiOrigin, withCsrfHeader } from "@rapidmx/web-client/lib/util/api.js";
67
69
  import { pushUrl } from "@rapidmx/web-client/lib/mail/pushClient.js";
68
70
  const SOCKET_OPEN = 1;
69
71
  function defaultSocketFactory() {
@@ -132,7 +134,16 @@ export class GuestSignalingClient {
132
134
  }
133
135
  /** Publishes over `POST /push/:id`, with `token` as a bearer credential when present - see this module's doc
134
136
  * comment on why this, unlike subscribing, needs no cookie workaround at all, and on the omitted-`token` case
135
- * (an already-authenticated real caller), which relies on the browser's own real `jwt` cookie instead.
137
+ * (an already-authenticated real caller), which relies on the browser's own real `jwt` cookie instead - and,
138
+ * because that's a *cookie*-authenticated request, also needs the matching CSRF header
139
+ * (`@rapidmx/web-client`'s `withCsrfHeader()`, the same double-submit echo `apiFetch()` applies to every other
140
+ * mutating call this server answers): without it, `RouteUtils.checkCsrf()` refuses the request with 403
141
+ * `"This request is missing a valid CSRF token."` before it ever reaches `BasePushRoute.send()`'s own ACL
142
+ * check - every `hello`/offer/answer/ICE candidate/`bye` a participant on that path sends 403s and is
143
+ * silently dropped (`send()` is fire-and-forget), which looks exactly like "stuck connecting, then no audio or
144
+ * video" from everyone else's side. `withCsrfHeader()` is harmless to also add on the bearer-token (guest)
145
+ * branch - the server only enforces the check for `req.auth.source === "cookie"` - so it's applied
146
+ * unconditionally here rather than only for the cookie case.
136
147
  * Fire-and-forget: a failure is swallowed (there is nothing a signaling message's own sender can usefully do
137
148
  * about a dropped publish beyond what the mesh's own `hello` re-announcement/renegotiation already tolerates -
138
149
  * see `MeshConnectionManager`'s doc comment on fire-and-forget delivery). */
@@ -148,7 +159,7 @@ export class GuestSignalingClient {
148
159
  }
149
160
  void fetchImpl(`${origin}/push/${encodeURIComponent(this.options.channel)}`, {
150
161
  method: "POST",
151
- headers,
162
+ headers: withCsrfHeader(headers, "POST"),
152
163
  body: JSON.stringify(message),
153
164
  // Only a `bye` needs to survive the page unloading - every other message is sent while the page (and
154
165
  // the rest of this call's signaling) is very much alive. `keepalive` is a real-world liability to set
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rapidmx/meet-plugin",
3
- "version": "0.9.1",
3
+ "version": "0.9.2",
4
4
  "description": "RapidMX plugin that provides WebRTC video conferencing, joinable from a calendar invite or a shared link.",
5
5
  "repository": "https://github.com/rapidmx/meet-plugin.git",
6
6
  "author": "RapidREST <rapidrests@gmail.com>",