@polyguard/sdk 1.4.0 → 1.4.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.
- package/dist/sdk.esm.js +6 -2
- package/dist/sdk.js +6 -2
- package/package.json +1 -1
- package/src/PolyguardWebsocketClientImpl.js +2 -1
- package/src/__tests__/sidebar.test.js +38 -14
- package/src/messageHandler.js +6 -1
package/dist/sdk.esm.js
CHANGED
|
@@ -10980,10 +10980,11 @@ function handleWebSocketMessage(event, ctx) {
|
|
|
10980
10980
|
return;
|
|
10981
10981
|
}
|
|
10982
10982
|
if (data.jwt) {
|
|
10983
|
-
const redirectUrl = data.redirect_url;
|
|
10983
|
+
const redirectUrl = data.jwt.redirect_url;
|
|
10984
10984
|
const { sidebarUrl, link_uuid } = ctx;
|
|
10985
10985
|
if (sidebarUrl && redirectUrl) {
|
|
10986
10986
|
const hasMsftSession = document.cookie.includes("pg_msft_session");
|
|
10987
|
+
ctx.markClosed();
|
|
10987
10988
|
cleanup();
|
|
10988
10989
|
ws.close();
|
|
10989
10990
|
if (hasMsftSession) {
|
|
@@ -11145,7 +11146,10 @@ var PolyguardWebsocketClientImpl = class {
|
|
|
11145
11146
|
reconnectionDelayGrowFactor: 1.5
|
|
11146
11147
|
};
|
|
11147
11148
|
ws = new reconnecting_websocket_mjs_default(wsUrl, [], options);
|
|
11148
|
-
const
|
|
11149
|
+
const markClosed = () => {
|
|
11150
|
+
closed = true;
|
|
11151
|
+
};
|
|
11152
|
+
const ctx = { ws, qrDiv, isTargetMode, modal, cleanup, markClosed, returnError, clearError, resolve, rawJwt, sidebarUrl: this.sidebarUrl, link_uuid: this.link_uuid };
|
|
11149
11153
|
ws.addEventListener("message", (event) => handleWebSocketMessage(event, ctx));
|
|
11150
11154
|
ws.addEventListener("error", () => {
|
|
11151
11155
|
console.error("WebSocket error");
|
package/dist/sdk.js
CHANGED
|
@@ -11013,10 +11013,11 @@ var Polyguard = (() => {
|
|
|
11013
11013
|
return;
|
|
11014
11014
|
}
|
|
11015
11015
|
if (data.jwt) {
|
|
11016
|
-
const redirectUrl = data.redirect_url;
|
|
11016
|
+
const redirectUrl = data.jwt.redirect_url;
|
|
11017
11017
|
const { sidebarUrl, link_uuid } = ctx;
|
|
11018
11018
|
if (sidebarUrl && redirectUrl) {
|
|
11019
11019
|
const hasMsftSession = document.cookie.includes("pg_msft_session");
|
|
11020
|
+
ctx.markClosed();
|
|
11020
11021
|
cleanup();
|
|
11021
11022
|
ws.close();
|
|
11022
11023
|
if (hasMsftSession) {
|
|
@@ -11178,7 +11179,10 @@ var Polyguard = (() => {
|
|
|
11178
11179
|
reconnectionDelayGrowFactor: 1.5
|
|
11179
11180
|
};
|
|
11180
11181
|
ws = new reconnecting_websocket_mjs_default(wsUrl, [], options);
|
|
11181
|
-
const
|
|
11182
|
+
const markClosed = () => {
|
|
11183
|
+
closed = true;
|
|
11184
|
+
};
|
|
11185
|
+
const ctx = { ws, qrDiv, isTargetMode, modal, cleanup, markClosed, returnError, clearError, resolve, rawJwt, sidebarUrl: this.sidebarUrl, link_uuid: this.link_uuid };
|
|
11182
11186
|
ws.addEventListener("message", (event) => handleWebSocketMessage(event, ctx));
|
|
11183
11187
|
ws.addEventListener("error", () => {
|
|
11184
11188
|
console.error("WebSocket error");
|
package/package.json
CHANGED
|
@@ -136,7 +136,8 @@ export class PolyguardWebsocketClientImpl {
|
|
|
136
136
|
};
|
|
137
137
|
ws = new ReconnectingWebSocket(wsUrl, [], options);
|
|
138
138
|
|
|
139
|
-
const
|
|
139
|
+
const markClosed = () => { closed = true; };
|
|
140
|
+
const ctx = { ws, qrDiv, isTargetMode, modal, cleanup, markClosed, returnError, clearError, resolve, rawJwt, sidebarUrl: this.sidebarUrl, link_uuid: this.link_uuid };
|
|
140
141
|
ws.addEventListener('message', (event) => handleWebSocketMessage(event, ctx));
|
|
141
142
|
|
|
142
143
|
ws.addEventListener('error', () => {
|
|
@@ -159,9 +159,9 @@ describe('sidebarUrl feature', () => {
|
|
|
159
159
|
link_uuid: 'link-abc',
|
|
160
160
|
});
|
|
161
161
|
document.body.innerHTML = '<div id="test-target"></div>';
|
|
162
|
-
const
|
|
162
|
+
const jwtClaims = { redirect_url: 'https://teams.microsoft.com/meet/123' };
|
|
163
163
|
const { promise, ws } = await startVerifyAndGetWs(client, 'test-target');
|
|
164
|
-
ws.simulateMessage({ jwt:
|
|
164
|
+
ws.simulateMessage({ jwt: jwtClaims });
|
|
165
165
|
await promise;
|
|
166
166
|
expect(window.location.assign).toHaveBeenCalledWith('https://teams.microsoft.com/meet/123');
|
|
167
167
|
});
|
|
@@ -174,9 +174,9 @@ describe('sidebarUrl feature', () => {
|
|
|
174
174
|
link_uuid: 'link-abc',
|
|
175
175
|
});
|
|
176
176
|
document.body.innerHTML = '<div id="test-target"></div>';
|
|
177
|
-
const
|
|
177
|
+
const jwtClaims = { redirect_url: 'https://teams.microsoft.com/meet/123' };
|
|
178
178
|
const { promise, ws } = await startVerifyAndGetWs(client, 'test-target');
|
|
179
|
-
ws.simulateMessage({ jwt:
|
|
179
|
+
ws.simulateMessage({ jwt: jwtClaims });
|
|
180
180
|
await promise;
|
|
181
181
|
expect(document.querySelector('#polyguard-join-meeting')).toBeNull();
|
|
182
182
|
});
|
|
@@ -191,9 +191,9 @@ describe('sidebarUrl feature', () => {
|
|
|
191
191
|
link_uuid: 'link-abc',
|
|
192
192
|
});
|
|
193
193
|
document.body.innerHTML = '<div id="test-target"></div>';
|
|
194
|
-
const
|
|
194
|
+
const jwtClaims = { redirect_url: 'https://teams.microsoft.com/meet/123' };
|
|
195
195
|
const { promise, ws } = await startVerifyAndGetWs(client, 'test-target');
|
|
196
|
-
ws.simulateMessage({ jwt:
|
|
196
|
+
ws.simulateMessage({ jwt: jwtClaims });
|
|
197
197
|
// Should not have redirected yet
|
|
198
198
|
expect(window.location.assign).not.toHaveBeenCalled();
|
|
199
199
|
// Should show join button
|
|
@@ -214,9 +214,9 @@ describe('sidebarUrl feature', () => {
|
|
|
214
214
|
link_uuid: 'link-abc',
|
|
215
215
|
});
|
|
216
216
|
document.body.innerHTML = '<div id="test-target"></div>';
|
|
217
|
-
const
|
|
217
|
+
const jwtClaims = { redirect_url: 'https://teams.microsoft.com/meet/123' };
|
|
218
218
|
const { promise, ws } = await startVerifyAndGetWs(client, 'test-target');
|
|
219
|
-
ws.simulateMessage({ jwt:
|
|
219
|
+
ws.simulateMessage({ jwt: jwtClaims });
|
|
220
220
|
const btn = document.querySelector('#polyguard-join-meeting');
|
|
221
221
|
btn.click();
|
|
222
222
|
await promise;
|
|
@@ -236,13 +236,37 @@ describe('sidebarUrl feature', () => {
|
|
|
236
236
|
link_uuid: 'link-abc',
|
|
237
237
|
});
|
|
238
238
|
document.body.innerHTML = '<div id="test-target"></div>';
|
|
239
|
-
const
|
|
239
|
+
const jwtClaims = { redirect_url: 'https://teams.microsoft.com/meet/123' };
|
|
240
240
|
const { promise, ws } = await startVerifyAndGetWs(client, 'test-target');
|
|
241
|
-
ws.simulateMessage({ jwt:
|
|
241
|
+
ws.simulateMessage({ jwt: jwtClaims });
|
|
242
242
|
document.querySelector('#polyguard-join-meeting').click();
|
|
243
243
|
await promise;
|
|
244
244
|
expect(window.location.assign).toHaveBeenCalledWith('https://teams.microsoft.com/meet/123');
|
|
245
245
|
});
|
|
246
|
+
|
|
247
|
+
it('button survives a late WebSocket close event (race regression)', async () => {
|
|
248
|
+
// Real browsers dispatch the 'close' event asynchronously after ws.close(),
|
|
249
|
+
// so it lands after the synchronous button append. If the close listener's
|
|
250
|
+
// cleanup() runs in that window, it wipes the qrDiv — and the button with it.
|
|
251
|
+
// markClosed() in the message handler must defuse the close listener.
|
|
252
|
+
clearCookies();
|
|
253
|
+
vi.stubGlobal('open', vi.fn().mockReturnValue({}));
|
|
254
|
+
const client = new PolyguardWebsocketClientImpl({
|
|
255
|
+
...DEFAULT_PARAMS,
|
|
256
|
+
sidebarUrl: 'https://teams.polyguard.ai/ms-teams/sidebar-standalone',
|
|
257
|
+
link_uuid: 'link-abc',
|
|
258
|
+
});
|
|
259
|
+
document.body.innerHTML = '<div id="test-target"></div>';
|
|
260
|
+
const jwtClaims = { redirect_url: 'https://teams.microsoft.com/meet/123' };
|
|
261
|
+
const { promise, ws } = await startVerifyAndGetWs(client, 'test-target');
|
|
262
|
+
ws.simulateMessage({ jwt: jwtClaims });
|
|
263
|
+
expect(document.querySelector('#polyguard-join-meeting')).not.toBeNull();
|
|
264
|
+
// Mimic a real browser dispatching close on the next tick (after the message handler returned).
|
|
265
|
+
ws.simulateClose();
|
|
266
|
+
expect(document.querySelector('#polyguard-join-meeting')).not.toBeNull();
|
|
267
|
+
document.querySelector('#polyguard-join-meeting').click();
|
|
268
|
+
await promise;
|
|
269
|
+
});
|
|
246
270
|
});
|
|
247
271
|
|
|
248
272
|
describe('with sidebarUrl but no redirect_url in JWT (non-meeting links)', () => {
|
|
@@ -253,12 +277,12 @@ describe('sidebarUrl feature', () => {
|
|
|
253
277
|
sidebarUrl: 'https://teams.polyguard.ai/ms-teams/sidebar-standalone',
|
|
254
278
|
link_uuid: 'link-abc',
|
|
255
279
|
});
|
|
256
|
-
const
|
|
280
|
+
const jwtClaims = { confirmation_code: 'ABC123' };
|
|
257
281
|
const { promise, ws } = await startVerifyAndGetWs(client);
|
|
258
|
-
// No redirect_url in the
|
|
259
|
-
ws.simulateMessage({ jwt:
|
|
282
|
+
// No redirect_url in the JWT claims
|
|
283
|
+
ws.simulateMessage({ jwt: jwtClaims });
|
|
260
284
|
const result = await promise;
|
|
261
|
-
expect(result).
|
|
285
|
+
expect(result).toEqual(jwtClaims);
|
|
262
286
|
expect(window.location.assign).not.toHaveBeenCalled();
|
|
263
287
|
});
|
|
264
288
|
});
|
package/src/messageHandler.js
CHANGED
|
@@ -54,13 +54,18 @@ export function handleWebSocketMessage(event, ctx) {
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
if (data.jwt) {
|
|
57
|
-
const redirectUrl = data.redirect_url;
|
|
57
|
+
const redirectUrl = data.jwt.redirect_url;
|
|
58
58
|
const { sidebarUrl, link_uuid } = ctx;
|
|
59
59
|
|
|
60
60
|
// When sidebarUrl is configured and the server provides a redirect_url,
|
|
61
61
|
// the SDK handles the redirect (and optionally opens a sidebar popup).
|
|
62
62
|
if (sidebarUrl && redirectUrl) {
|
|
63
63
|
const hasMsftSession = document.cookie.includes('pg_msft_session');
|
|
64
|
+
// Mark the socket as intentionally closed BEFORE ws.close() so the
|
|
65
|
+
// 'close' event listener in verify() skips its own cleanup(). Otherwise
|
|
66
|
+
// the async close handler races with the synchronous button append below
|
|
67
|
+
// and wipes the qrDiv after we've put the button in it.
|
|
68
|
+
ctx.markClosed();
|
|
64
69
|
cleanup();
|
|
65
70
|
ws.close();
|
|
66
71
|
|