aws-appsync-subscription-link 4.0.1 → 4.0.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/CHANGELOG.md CHANGED
@@ -3,6 +3,17 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ <a name="4.0.2"></a>
7
+ ## [4.0.2](https://github.com/awslabs/aws-mobile-appsync-sdk-js/compare/aws-appsync-subscription-link@4.0.1...aws-appsync-subscription-link@4.0.2) (2026-03-19)
8
+
9
+
10
+ ### Bug Fixes
11
+
12
+ * **subscription-link:** move auth credentials from URL query string to Sec-WebSocket-Protocol header ([fb5dd3e](https://github.com/awslabs/aws-mobile-appsync-sdk-js/commit/fb5dd3e))
13
+
14
+
15
+
16
+
6
17
  <a name="4.1.0"></a>
7
18
  # [4.1.0] (Unreleased)
8
19
 
@@ -69,6 +69,20 @@ class myWebSocket implements WebSocket {
69
69
  }
70
70
  }
71
71
 
72
+ /**
73
+ * Helper to decode a base64url-encoded header from the Sec-WebSocket-Protocol value.
74
+ * The protocol value is prefixed with "header-".
75
+ */
76
+ function decodeProtocolHeader(protocols: string | string[]): Record<string, string> {
77
+ const arr = Array.isArray(protocols) ? protocols : [protocols];
78
+ const headerProtocol = arr.find(p => p.startsWith("header-"));
79
+ if (!headerProtocol) throw new Error("No header- protocol found");
80
+ const base64url = headerProtocol.slice("header-".length);
81
+ // Convert base64url back to standard base64
82
+ const base64 = base64url.replace(/-/g, "+").replace(/_/g, "/");
83
+ return JSON.parse(Buffer.from(base64, "base64").toString());
84
+ }
85
+
72
86
  describe("RealTime subscription link", () => {
73
87
  test("Can instantiate link", () => {
74
88
  expect.assertions(1);
@@ -99,7 +113,7 @@ describe("RealTime subscription link", () => {
99
113
  });
100
114
 
101
115
  test("Initialize WebSocket correctly for API KEY", (done) => {
102
- expect.assertions(2);
116
+ expect.assertions(3);
103
117
  jest.spyOn(Date.prototype, "toISOString").mockImplementation(
104
118
  jest.fn(() => {
105
119
  return "2019-11-13T18:47:04.733Z";
@@ -107,10 +121,14 @@ describe("RealTime subscription link", () => {
107
121
  );
108
122
  AppSyncRealTimeSubscriptionHandshakeLink.createWebSocket = jest.fn(
109
123
  (url, protocol) => {
124
+ // URL should be clean — no query string with credentials
110
125
  expect(url).toBe(
111
- "wss://apikeytesturl1234567890123.appsync-realtime-api.us-west-2.amazonaws.com/graphql?header=eyJob3N0IjoiYXBpa2V5dGVzdHVybDEyMzQ1Njc4OTAxMjMuYXBwc3luYy1hcGkudXMtd2VzdC0yLmFtYXpvbmF3cy5jb20iLCJ4LWFtei1kYXRlIjoiMjAxOTExMTNUMTg0NzA0WiIsIngtYXBpLWtleSI6Inh4eHh4In0=&payload=e30="
126
+ "wss://apikeytesturl1234567890123.appsync-realtime-api.us-west-2.amazonaws.com/graphql"
112
127
  );
113
- expect(protocol).toBe("graphql-ws");
128
+ // Protocol should be an array with graphql-ws and header- prefix
129
+ expect(Array.isArray(protocol)).toBe(true);
130
+ const header = decodeProtocolHeader(protocol);
131
+ expect(header["x-api-key"]).toBe("xxxxx");
114
132
  done();
115
133
  return new myWebSocket();
116
134
  }
@@ -141,7 +159,7 @@ describe("RealTime subscription link", () => {
141
159
  });
142
160
 
143
161
  test("Initialize WebSocket correctly for API KEY with custom domain", (done) => {
144
- expect.assertions(2);
162
+ expect.assertions(3);
145
163
  jest.spyOn(Date.prototype, "toISOString").mockImplementation(
146
164
  jest.fn(() => {
147
165
  return "2019-11-13T18:47:04.733Z";
@@ -150,9 +168,11 @@ describe("RealTime subscription link", () => {
150
168
  AppSyncRealTimeSubscriptionHandshakeLink.createWebSocket = jest.fn(
151
169
  (url, protocol) => {
152
170
  expect(url).toBe(
153
- "wss://apikeytest.testcustomdomain.com/graphql/realtime?header=eyJob3N0IjoiYXBpa2V5dGVzdC50ZXN0Y3VzdG9tZG9tYWluLmNvbSIsIngtYW16LWRhdGUiOiIyMDE5MTExM1QxODQ3MDRaIiwieC1hcGkta2V5IjoieHh4eHgifQ==&payload=e30="
171
+ "wss://apikeytest.testcustomdomain.com/graphql/realtime"
154
172
  );
155
- expect(protocol).toBe("graphql-ws");
173
+ expect(Array.isArray(protocol)).toBe(true);
174
+ const header = decodeProtocolHeader(protocol);
175
+ expect(header["x-api-key"]).toBe("xxxxx");
156
176
  done();
157
177
  return new myWebSocket();
158
178
  }
@@ -183,7 +203,7 @@ describe("RealTime subscription link", () => {
183
203
  });
184
204
 
185
205
  test("Initialize WebSocket correctly for COGNITO USER POOLS", (done) => {
186
- expect.assertions(2);
206
+ expect.assertions(3);
187
207
  jest.spyOn(Date.prototype, "toISOString").mockImplementation(
188
208
  jest.fn(() => {
189
209
  return "2019-11-13T18:47:04.733Z";
@@ -192,9 +212,11 @@ describe("RealTime subscription link", () => {
192
212
  AppSyncRealTimeSubscriptionHandshakeLink.createWebSocket = jest.fn(
193
213
  (url, protocol) => {
194
214
  expect(url).toBe(
195
- "wss://cognitouserpooltesturl1234.appsync-realtime-api.us-west-2.amazonaws.com/graphql?header=eyJBdXRob3JpemF0aW9uIjoidG9rZW4iLCJob3N0IjoiY29nbml0b3VzZXJwb29sdGVzdHVybDEyMzQuYXBwc3luYy1hcGkudXMtd2VzdC0yLmFtYXpvbmF3cy5jb20ifQ==&payload=e30="
215
+ "wss://cognitouserpooltesturl1234.appsync-realtime-api.us-west-2.amazonaws.com/graphql"
196
216
  );
197
- expect(protocol).toBe("graphql-ws");
217
+ expect(Array.isArray(protocol)).toBe(true);
218
+ const header = decodeProtocolHeader(protocol);
219
+ expect(header["Authorization"]).toBe("token");
198
220
  done();
199
221
  return new myWebSocket();
200
222
  }
@@ -225,7 +247,7 @@ describe("RealTime subscription link", () => {
225
247
  });
226
248
 
227
249
  test("Initialize WebSocket correctly for COGNITO USER POOLS with custom domain", (done) => {
228
- expect.assertions(2);
250
+ expect.assertions(3);
229
251
  jest.spyOn(Date.prototype, "toISOString").mockImplementation(
230
252
  jest.fn(() => {
231
253
  return "2019-11-13T18:47:04.733Z";
@@ -234,9 +256,11 @@ describe("RealTime subscription link", () => {
234
256
  AppSyncRealTimeSubscriptionHandshakeLink.createWebSocket = jest.fn(
235
257
  (url, protocol) => {
236
258
  expect(url).toBe(
237
- "wss://cognitouserpools.testcustomdomain.com/graphql/realtime?header=eyJBdXRob3JpemF0aW9uIjoidG9rZW4iLCJob3N0IjoiY29nbml0b3VzZXJwb29scy50ZXN0Y3VzdG9tZG9tYWluLmNvbSJ9&payload=e30="
259
+ "wss://cognitouserpools.testcustomdomain.com/graphql/realtime"
238
260
  );
239
- expect(protocol).toBe("graphql-ws");
261
+ expect(Array.isArray(protocol)).toBe(true);
262
+ const header = decodeProtocolHeader(protocol);
263
+ expect(header["Authorization"]).toBe("token");
240
264
  done();
241
265
  return new myWebSocket();
242
266
  }
@@ -267,7 +291,7 @@ describe("RealTime subscription link", () => {
267
291
  });
268
292
 
269
293
  test("Initialize WebSocket correctly for OPENID_CONNECT", (done) => {
270
- expect.assertions(2);
294
+ expect.assertions(3);
271
295
  jest.spyOn(Date.prototype, "toISOString").mockImplementation(
272
296
  jest.fn(() => {
273
297
  return "2019-11-13T18:47:04.733Z";
@@ -276,9 +300,11 @@ describe("RealTime subscription link", () => {
276
300
  AppSyncRealTimeSubscriptionHandshakeLink.createWebSocket = jest.fn(
277
301
  (url, protocol) => {
278
302
  expect(url).toBe(
279
- "wss://openidconnecttesturl123456.appsync-realtime-api.us-west-2.amazonaws.com/graphql?header=eyJBdXRob3JpemF0aW9uIjoidG9rZW4iLCJob3N0Ijoib3BlbmlkY29ubmVjdHRlc3R1cmwxMjM0NTYuYXBwc3luYy1hcGkudXMtd2VzdC0yLmFtYXpvbmF3cy5jb20ifQ==&payload=e30="
303
+ "wss://openidconnecttesturl123456.appsync-realtime-api.us-west-2.amazonaws.com/graphql"
280
304
  );
281
- expect(protocol).toBe("graphql-ws");
305
+ expect(Array.isArray(protocol)).toBe(true);
306
+ const header = decodeProtocolHeader(protocol);
307
+ expect(header["Authorization"]).toBe("token");
282
308
  done();
283
309
  return new myWebSocket();
284
310
  }
@@ -309,7 +335,7 @@ describe("RealTime subscription link", () => {
309
335
  });
310
336
 
311
337
  test("Initialize WebSocket correctly for OPENID_CONNECT with custom domain", (done) => {
312
- expect.assertions(2);
338
+ expect.assertions(3);
313
339
  jest.spyOn(Date.prototype, "toISOString").mockImplementation(
314
340
  jest.fn(() => {
315
341
  return "2019-11-13T18:47:04.733Z";
@@ -318,9 +344,11 @@ describe("RealTime subscription link", () => {
318
344
  AppSyncRealTimeSubscriptionHandshakeLink.createWebSocket = jest.fn(
319
345
  (url, protocol) => {
320
346
  expect(url).toBe(
321
- "wss://openidconnecttesturl.testcustomdomain.com/graphql/realtime?header=eyJBdXRob3JpemF0aW9uIjoidG9rZW4iLCJob3N0Ijoib3BlbmlkY29ubmVjdHRlc3R1cmwudGVzdGN1c3RvbWRvbWFpbi5jb20ifQ==&payload=e30="
347
+ "wss://openidconnecttesturl.testcustomdomain.com/graphql/realtime"
322
348
  );
323
- expect(protocol).toBe("graphql-ws");
349
+ expect(Array.isArray(protocol)).toBe(true);
350
+ const header = decodeProtocolHeader(protocol);
351
+ expect(header["Authorization"]).toBe("token");
324
352
  done();
325
353
  return new myWebSocket();
326
354
  }
@@ -351,7 +379,7 @@ describe("RealTime subscription link", () => {
351
379
  });
352
380
 
353
381
  test("Initialize WebSocket correctly for AWS_LAMBDA", (done) => {
354
- expect.assertions(2);
382
+ expect.assertions(3);
355
383
  jest.spyOn(Date.prototype, "toISOString").mockImplementation(
356
384
  jest.fn(() => {
357
385
  return "2019-11-13T18:47:04.733Z";
@@ -360,9 +388,11 @@ describe("RealTime subscription link", () => {
360
388
  AppSyncRealTimeSubscriptionHandshakeLink.createWebSocket = jest.fn(
361
389
  (url, protocol) => {
362
390
  expect(url).toBe(
363
- "wss://awslambdatesturl1234567890.appsync-realtime-api.us-west-2.amazonaws.com/graphql?header=eyJBdXRob3JpemF0aW9uIjoidG9rZW4iLCJob3N0IjoiYXdzbGFtYmRhdGVzdHVybDEyMzQ1Njc4OTAuYXBwc3luYy1hcGkudXMtd2VzdC0yLmFtYXpvbmF3cy5jb20ifQ==&payload=e30="
391
+ "wss://awslambdatesturl1234567890.appsync-realtime-api.us-west-2.amazonaws.com/graphql"
364
392
  );
365
- expect(protocol).toBe("graphql-ws");
393
+ expect(Array.isArray(protocol)).toBe(true);
394
+ const header = decodeProtocolHeader(protocol);
395
+ expect(header["Authorization"]).toBe("token");
366
396
  done();
367
397
  return new myWebSocket();
368
398
  }
@@ -390,7 +420,7 @@ describe("RealTime subscription link", () => {
390
420
  });
391
421
 
392
422
  test("Initialize WebSocket correctly for AWS_LAMBDA with custom domain", (done) => {
393
- expect.assertions(2);
423
+ expect.assertions(3);
394
424
  jest.spyOn(Date.prototype, "toISOString").mockImplementation(
395
425
  jest.fn(() => {
396
426
  return "2019-11-13T18:47:04.733Z";
@@ -399,9 +429,11 @@ describe("RealTime subscription link", () => {
399
429
  AppSyncRealTimeSubscriptionHandshakeLink.createWebSocket = jest.fn(
400
430
  (url, protocol) => {
401
431
  expect(url).toBe(
402
- "wss://awslambdatesturl.testcustomdomain.com/graphql/realtime?header=eyJBdXRob3JpemF0aW9uIjoidG9rZW4iLCJob3N0IjoiYXdzbGFtYmRhdGVzdHVybC50ZXN0Y3VzdG9tZG9tYWluLmNvbSJ9&payload=e30="
432
+ "wss://awslambdatesturl.testcustomdomain.com/graphql/realtime"
403
433
  );
404
- expect(protocol).toBe("graphql-ws");
434
+ expect(Array.isArray(protocol)).toBe(true);
435
+ const header = decodeProtocolHeader(protocol);
436
+ expect(header["Authorization"]).toBe("token");
405
437
  done();
406
438
  return new myWebSocket();
407
439
  }
@@ -455,14 +487,11 @@ describe("RealTime subscription link", () => {
455
487
  AppSyncRealTimeSubscriptionHandshakeLink.createWebSocket = jest.fn(
456
488
  (url, protocol) => {
457
489
  // WebSocket should connect to the proxy, not directly to AppSync
458
- // The header should contain the original AppSync host for auth
459
- const urlObj = new URL(url);
460
- expect(urlObj.origin + urlObj.pathname).toBe(
490
+ expect(url).toBe(
461
491
  "wss://d111111abcdef8.cloudfront.net/realtime"
462
492
  );
463
- // Decode the header to verify the host is the AppSync endpoint, not CloudFront
464
- const headerB64 = urlObj.searchParams.get("header");
465
- const header = JSON.parse(Buffer.from(headerB64, "base64").toString());
493
+ // Decode the header from the protocol to verify the host is the AppSync endpoint, not CloudFront
494
+ const header = decodeProtocolHeader(protocol);
466
495
  expect(header.host).toBe(
467
496
  "proxytesturl12345678901234567.appsync-api.us-west-2.amazonaws.com"
468
497
  );
@@ -504,12 +533,10 @@ describe("RealTime subscription link", () => {
504
533
  );
505
534
  AppSyncRealTimeSubscriptionHandshakeLink.createWebSocket = jest.fn(
506
535
  (url, protocol) => {
507
- const urlObj = new URL(url);
508
- expect(urlObj.origin + urlObj.pathname).toBe(
536
+ expect(url).toBe(
509
537
  "wss://mycdn.example.com/realtime"
510
538
  );
511
- const headerB64 = urlObj.searchParams.get("header");
512
- const header = JSON.parse(Buffer.from(headerB64, "base64").toString());
539
+ const header = decodeProtocolHeader(protocol);
513
540
  expect(header.host).toBe(
514
541
  "cognitoproxytesturl123456789.appsync-api.us-west-2.amazonaws.com"
515
542
  );
@@ -555,9 +582,9 @@ describe("RealTime subscription link", () => {
555
582
  AppSyncRealTimeSubscriptionHandshakeLink.createWebSocket = jest.fn(
556
583
  (url, protocol) => {
557
584
  expect(url).toBe(
558
- "wss://apikeytest.testcustomdomain.com/graphql/realtime?header=eyJob3N0IjoiYXBpa2V5dGVzdC50ZXN0Y3VzdG9tZG9tYWluLmNvbSIsIngtYW16LWRhdGUiOiIyMDE5MTExM1QxODQ3MDRaIiwieC1hcGkta2V5IjoieHh4eHgifQ==&payload=e30="
585
+ "wss://apikeytest.testcustomdomain.com/graphql/realtime"
559
586
  );
560
- expect(protocol).toBe("graphql-ws");
587
+ expect(Array.isArray(protocol)).toBe(true);
561
588
  const socket = new myWebSocket();
562
589
 
563
590
  setTimeout(() => {
@@ -638,9 +665,9 @@ describe("RealTime subscription link", () => {
638
665
  AppSyncRealTimeSubscriptionHandshakeLink.createWebSocket = jest.fn(
639
666
  (url, protocol) => {
640
667
  expect(url).toBe(
641
- "wss://apikeytest.testcustomdomain.com/graphql/realtime?header=eyJob3N0IjoiYXBpa2V5dGVzdC50ZXN0Y3VzdG9tZG9tYWluLmNvbSIsIngtYW16LWRhdGUiOiIyMDE5MTExM1QxODQ3MDRaIiwieC1hcGkta2V5IjoieHh4eHgifQ==&payload=e30="
668
+ "wss://apikeytest.testcustomdomain.com/graphql/realtime"
642
669
  );
643
- expect(protocol).toBe("graphql-ws");
670
+ expect(Array.isArray(protocol)).toBe(true);
644
671
  const socket = new myWebSocket();
645
672
 
646
673
  setTimeout(() => {
@@ -706,4 +733,38 @@ describe("RealTime subscription link", () => {
706
733
  },
707
734
  });
708
735
  });
736
+
737
+ test("URL does not contain credentials in query string", (done) => {
738
+ expect.assertions(2);
739
+ jest.spyOn(Date.prototype, "toISOString").mockImplementation(
740
+ jest.fn(() => {
741
+ return "2019-11-13T18:47:04.733Z";
742
+ })
743
+ );
744
+ AppSyncRealTimeSubscriptionHandshakeLink.createWebSocket = jest.fn(
745
+ (url, protocol) => {
746
+ // The URL must not contain any query parameters with auth material
747
+ expect(url.includes("?")).toBe(false);
748
+ // Auth should be in the protocol header instead
749
+ const header = decodeProtocolHeader(protocol);
750
+ expect(header["x-api-key"]).toBe("my-secret-key");
751
+ done();
752
+ return new myWebSocket();
753
+ }
754
+ );
755
+ const link = new AppSyncRealTimeSubscriptionHandshakeLink({
756
+ auth: {
757
+ type: AUTH_TYPE.API_KEY,
758
+ apiKey: "my-secret-key",
759
+ },
760
+ region: "us-west-2",
761
+ url: "https://securitytesturl12345678901.appsync-api.us-west-2.amazonaws.com/graphql",
762
+ });
763
+
764
+ execute(link, { query }, { client: mockClient }).subscribe({
765
+ error: () => { fail; },
766
+ next: () => { done(); },
767
+ complete: () => { done(); },
768
+ });
769
+ });
709
770
  });
package/lib/index.js CHANGED
@@ -2594,7 +2594,7 @@ function retry(functionToRetry, args, delayFn) {
2594
2594
  return _ts_generator(this, function(_state) {
2595
2595
  switch(_state.label){
2596
2596
  case 0:
2597
- logger2("Attempt #".concat(attempt, " for this vars: ").concat(JSON.stringify(args)));
2597
+ logger2("Attempt #".concat(attempt));
2598
2598
  _state.label = 1;
2599
2599
  case 1:
2600
2600
  _state.trys.push([
@@ -2707,6 +2707,12 @@ var AppSyncRealTimeSubscriptionHandshakeLink = /*#__PURE__*/ function(_import_co
2707
2707
  _this.auth = theAuth;
2708
2708
  _this.proxyUrl = proxy === null || proxy === void 0 ? void 0 : proxy.url;
2709
2709
  _this.keepAliveTimeout = keepAliveTimeoutMs;
2710
+ if (_this.url && !_this.url.startsWith("https://")) {
2711
+ logger3("WARNING: AppSync endpoint URL is not using HTTPS. Credentials may be sent unencrypted: ".concat(_this.url));
2712
+ }
2713
+ if (_this.proxyUrl && !_this.proxyUrl.startsWith("https://")) {
2714
+ logger3("WARNING: Proxy URL is not using HTTPS. Credentials may be sent unencrypted: ".concat(_this.proxyUrl));
2715
+ }
2710
2716
  if (_this.keepAliveTimeout < SERVER_KEEP_ALIVE_TIMEOUT) {
2711
2717
  var configName = "keepAliveTimeoutMs";
2712
2718
  throw new Error("".concat(configName, " must be greater than or equal to ").concat(SERVER_KEEP_ALIVE_TIMEOUT, " (").concat(_this.keepAliveTimeout, " used)."));
@@ -3004,7 +3010,7 @@ var AppSyncRealTimeSubscriptionHandshakeLink = /*#__PURE__*/ function(_import_co
3004
3010
  }
3005
3011
  return new Promise(function(res, rej) {
3006
3012
  return _async_to_generator(function() {
3007
- var payloadString, headerString, _, headerQs, payloadQs, discoverableEndpoint, awsRealTimeUrl, err;
3013
+ var payloadString, headerObj, headerString, headerBase64url, discoverableEndpoint, protocols, err;
3008
3014
  return _ts_generator(this, function(_state) {
3009
3015
  switch(_state.label){
3010
3016
  case 0:
@@ -3026,7 +3032,6 @@ var AppSyncRealTimeSubscriptionHandshakeLink = /*#__PURE__*/ function(_import_co
3026
3032
  ]);
3027
3033
  this.socketStatus = 2 /* CONNECTING */ ;
3028
3034
  payloadString = "{}";
3029
- _ = JSON.stringify;
3030
3035
  return [
3031
3036
  4,
3032
3037
  this._awsRealTimeHeaderBasedAuth({
@@ -3042,11 +3047,9 @@ var AppSyncRealTimeSubscriptionHandshakeLink = /*#__PURE__*/ function(_import_co
3042
3047
  })
3043
3048
  ];
3044
3049
  case 2:
3045
- headerString = _.apply(JSON, [
3046
- _state.sent()
3047
- ]);
3048
- headerQs = Buffer.from(headerString).toString("base64");
3049
- payloadQs = Buffer.from(payloadString).toString("base64");
3050
+ headerObj = _state.sent();
3051
+ headerString = JSON.stringify(headerObj);
3052
+ headerBase64url = Buffer.from(headerString).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
3050
3053
  if (this.proxyUrl) {
3051
3054
  discoverableEndpoint = this.proxyUrl.replace(/\/graphql$/, "").concat(customDomainPath).replace("https://", "wss://").replace("http://", "ws://");
3052
3055
  } else if (this.isCustomDomain(appSyncGraphqlEndpoint)) {
@@ -3056,11 +3059,15 @@ var AppSyncRealTimeSubscriptionHandshakeLink = /*#__PURE__*/ function(_import_co
3056
3059
  discoverableEndpoint = appSyncGraphqlEndpoint.replace("appsync-api", "appsync-realtime-api").replace("gogi-beta", "grt-beta");
3057
3060
  discoverableEndpoint = discoverableEndpoint.replace("https://", "wss://").replace("http://", "ws://");
3058
3061
  }
3059
- awsRealTimeUrl = "".concat(discoverableEndpoint, "?header=").concat(headerQs, "&payload=").concat(payloadQs);
3062
+ protocols = [
3063
+ "graphql-ws",
3064
+ "header-".concat(headerBase64url)
3065
+ ];
3060
3066
  return [
3061
3067
  4,
3062
3068
  this._initializeRetryableHandshake({
3063
- awsRealTimeUrl: awsRealTimeUrl
3069
+ awsRealTimeUrl: discoverableEndpoint,
3070
+ protocols: protocols
3064
3071
  })
3065
3072
  ];
3066
3073
  case 3:
@@ -3120,11 +3127,7 @@ var AppSyncRealTimeSubscriptionHandshakeLink = /*#__PURE__*/ function(_import_co
3120
3127
  };
3121
3128
  handler = headerHandler[authenticationType];
3122
3129
  if (typeof handler !== "function") {
3123
- logger3("Authentication type ".concat(authenticationType, " not supported"));
3124
- return [
3125
- 2,
3126
- {}
3127
- ];
3130
+ throw new NonRetryableError("Authentication type ".concat(authenticationType, " not supported"));
3128
3131
  }
3129
3132
  host = url.parse(this.url).host;
3130
3133
  return [
@@ -3298,17 +3301,18 @@ var AppSyncRealTimeSubscriptionHandshakeLink = /*#__PURE__*/ function(_import_co
3298
3301
  key: "_initializeRetryableHandshake",
3299
3302
  value: function _initializeRetryableHandshake(_0) {
3300
3303
  return _async_to_generator(function(param) {
3301
- var awsRealTimeUrl;
3304
+ var awsRealTimeUrl, protocols;
3302
3305
  return _ts_generator(this, function(_state) {
3303
3306
  switch(_state.label){
3304
3307
  case 0:
3305
- awsRealTimeUrl = param.awsRealTimeUrl;
3308
+ awsRealTimeUrl = param.awsRealTimeUrl, protocols = param.protocols;
3306
3309
  logger3("Initializaling retryable Handshake");
3307
3310
  return [
3308
3311
  4,
3309
3312
  jitteredExponentialRetry(this._initializeHandshake.bind(this), [
3310
3313
  {
3311
- awsRealTimeUrl: awsRealTimeUrl
3314
+ awsRealTimeUrl: awsRealTimeUrl,
3315
+ protocols: protocols
3312
3316
  }
3313
3317
  ])
3314
3318
  ];
@@ -3326,12 +3330,12 @@ var AppSyncRealTimeSubscriptionHandshakeLink = /*#__PURE__*/ function(_import_co
3326
3330
  key: "_initializeHandshake",
3327
3331
  value: function _initializeHandshake(_0) {
3328
3332
  return _async_to_generator(function(param) {
3329
- var _this, awsRealTimeUrl, err, errorType, errorCode;
3333
+ var _this, awsRealTimeUrl, protocols, err, errorType, errorCode;
3330
3334
  return _ts_generator(this, function(_state) {
3331
3335
  switch(_state.label){
3332
3336
  case 0:
3333
3337
  _this = this;
3334
- awsRealTimeUrl = param.awsRealTimeUrl;
3338
+ awsRealTimeUrl = param.awsRealTimeUrl, protocols = param.protocols;
3335
3339
  logger3("Initializing handshake ".concat(awsRealTimeUrl));
3336
3340
  _state.label = 1;
3337
3341
  case 1:
@@ -3345,7 +3349,7 @@ var AppSyncRealTimeSubscriptionHandshakeLink = /*#__PURE__*/ function(_import_co
3345
3349
  4,
3346
3350
  function() {
3347
3351
  return new Promise(function(res, rej) {
3348
- var newSocket = _AppSyncRealTimeSubscriptionHandshakeLink.createWebSocket(awsRealTimeUrl, "graphql-ws");
3352
+ var newSocket = _AppSyncRealTimeSubscriptionHandshakeLink.createWebSocket(awsRealTimeUrl, protocols);
3349
3353
  newSocket.onerror = function() {
3350
3354
  logger3("WebSocket connection error");
3351
3355
  };
@@ -3379,13 +3383,20 @@ var AppSyncRealTimeSubscriptionHandshakeLink = /*#__PURE__*/ function(_import_co
3379
3383
  rej(new Error(JSON.stringify(event)));
3380
3384
  };
3381
3385
  _this.awsRealTimeSocket.onmessage = function(message) {
3382
- logger3("subscription message from AWS AppSyncRealTime: ".concat(message.data, " "));
3383
- var data = JSON.parse(message.data);
3386
+ var data;
3387
+ try {
3388
+ data = JSON.parse(message.data);
3389
+ } catch (e) {
3390
+ logger3("Failed to parse WebSocket message");
3391
+ return;
3392
+ }
3384
3393
  var type = data.type, tmp = data.payload, _ref = tmp === void 0 ? {} : tmp, _ref_connectionTimeoutMs = _ref.connectionTimeoutMs, connectionTimeoutMs = _ref_connectionTimeoutMs === void 0 ? DEFAULT_KEEP_ALIVE_TIMEOUT : _ref_connectionTimeoutMs;
3394
+ logger3("subscription message from AWS AppSyncRealTime: ".concat(type));
3385
3395
  if (type === "connection_ack" /* GQL_CONNECTION_ACK */ ) {
3386
3396
  ackOk = true;
3397
+ var validTimeout = typeof connectionTimeoutMs === "number" && connectionTimeoutMs >= SERVER_KEEP_ALIVE_TIMEOUT ? connectionTimeoutMs : DEFAULT_KEEP_ALIVE_TIMEOUT;
3387
3398
  var _this_keepAliveTimeout;
3388
- _this.keepAliveTimeout = (_this_keepAliveTimeout = _this.keepAliveTimeout) !== null && _this_keepAliveTimeout !== void 0 ? _this_keepAliveTimeout : connectionTimeoutMs;
3399
+ _this.keepAliveTimeout = (_this_keepAliveTimeout = _this.keepAliveTimeout) !== null && _this_keepAliveTimeout !== void 0 ? _this_keepAliveTimeout : validTimeout;
3389
3400
  _this.awsRealTimeSocket.onmessage = _this._handleIncomingSubscriptionMessage.bind(_this);
3390
3401
  _this.awsRealTimeSocket.onerror = function(err) {
3391
3402
  logger3(err);
@@ -3446,8 +3457,15 @@ var AppSyncRealTimeSubscriptionHandshakeLink = /*#__PURE__*/ function(_import_co
3446
3457
  {
3447
3458
  key: "_handleIncomingSubscriptionMessage",
3448
3459
  value: function _handleIncomingSubscriptionMessage(message) {
3449
- logger3("subscription message from AWS AppSync RealTime: ".concat(message.data));
3450
- var _JSON_parse = JSON.parse(message.data), _JSON_parse_id = _JSON_parse.id, id = _JSON_parse_id === void 0 ? "" : _JSON_parse_id, payload = _JSON_parse.payload, type = _JSON_parse.type;
3460
+ var parsed;
3461
+ try {
3462
+ parsed = JSON.parse(message.data);
3463
+ } catch (e) {
3464
+ logger3("Failed to parse incoming subscription message");
3465
+ return;
3466
+ }
3467
+ var _parsed_id = parsed.id, id = _parsed_id === void 0 ? "" : _parsed_id, payload = parsed.payload, type = parsed.type;
3468
+ logger3("subscription message from AWS AppSync RealTime: ".concat(type, " id: ").concat(id));
3451
3469
  var _ref = this.subscriptionObserverMap.get(id) || {}, _ref_observer = _ref.observer, observer = _ref_observer === void 0 ? null : _ref_observer, _ref_query = _ref.query, query = _ref_query === void 0 ? "" : _ref_query, _ref_variables = _ref.variables, variables = _ref_variables === void 0 ? {} : _ref_variables, _ref_startAckTimeoutId = _ref.startAckTimeoutId, startAckTimeoutId = _ref_startAckTimeoutId === void 0 ? 0 : _ref_startAckTimeoutId, _ref_subscriptionReadyCallback = _ref.subscriptionReadyCallback, subscriptionReadyCallback = _ref_subscriptionReadyCallback === void 0 ? null : _ref_subscriptionReadyCallback, _ref_subscriptionFailedCallback = _ref.subscriptionFailedCallback, subscriptionFailedCallback = _ref_subscriptionFailedCallback === void 0 ? null : _ref_subscriptionFailedCallback;
3452
3470
  logger3({
3453
3471
  id: id,
package/lib/index.mjs CHANGED
@@ -2571,7 +2571,7 @@ function retry(functionToRetry, args, delayFn) {
2571
2571
  return _ts_generator(this, function(_state) {
2572
2572
  switch(_state.label){
2573
2573
  case 0:
2574
- logger2("Attempt #".concat(attempt, " for this vars: ").concat(JSON.stringify(args)));
2574
+ logger2("Attempt #".concat(attempt));
2575
2575
  _state.label = 1;
2576
2576
  case 1:
2577
2577
  _state.trys.push([
@@ -2684,6 +2684,12 @@ var AppSyncRealTimeSubscriptionHandshakeLink = /*#__PURE__*/ function(ApolloLink
2684
2684
  _this.auth = theAuth;
2685
2685
  _this.proxyUrl = proxy === null || proxy === void 0 ? void 0 : proxy.url;
2686
2686
  _this.keepAliveTimeout = keepAliveTimeoutMs;
2687
+ if (_this.url && !_this.url.startsWith("https://")) {
2688
+ logger3("WARNING: AppSync endpoint URL is not using HTTPS. Credentials may be sent unencrypted: ".concat(_this.url));
2689
+ }
2690
+ if (_this.proxyUrl && !_this.proxyUrl.startsWith("https://")) {
2691
+ logger3("WARNING: Proxy URL is not using HTTPS. Credentials may be sent unencrypted: ".concat(_this.proxyUrl));
2692
+ }
2687
2693
  if (_this.keepAliveTimeout < SERVER_KEEP_ALIVE_TIMEOUT) {
2688
2694
  var configName = "keepAliveTimeoutMs";
2689
2695
  throw new Error("".concat(configName, " must be greater than or equal to ").concat(SERVER_KEEP_ALIVE_TIMEOUT, " (").concat(_this.keepAliveTimeout, " used)."));
@@ -2981,7 +2987,7 @@ var AppSyncRealTimeSubscriptionHandshakeLink = /*#__PURE__*/ function(ApolloLink
2981
2987
  }
2982
2988
  return new Promise(function(res, rej) {
2983
2989
  return _async_to_generator(function() {
2984
- var payloadString, headerString, _, headerQs, payloadQs, discoverableEndpoint, awsRealTimeUrl, err;
2990
+ var payloadString, headerObj, headerString, headerBase64url, discoverableEndpoint, protocols, err;
2985
2991
  return _ts_generator(this, function(_state) {
2986
2992
  switch(_state.label){
2987
2993
  case 0:
@@ -3003,7 +3009,6 @@ var AppSyncRealTimeSubscriptionHandshakeLink = /*#__PURE__*/ function(ApolloLink
3003
3009
  ]);
3004
3010
  this.socketStatus = 2 /* CONNECTING */ ;
3005
3011
  payloadString = "{}";
3006
- _ = JSON.stringify;
3007
3012
  return [
3008
3013
  4,
3009
3014
  this._awsRealTimeHeaderBasedAuth({
@@ -3019,11 +3024,9 @@ var AppSyncRealTimeSubscriptionHandshakeLink = /*#__PURE__*/ function(ApolloLink
3019
3024
  })
3020
3025
  ];
3021
3026
  case 2:
3022
- headerString = _.apply(JSON, [
3023
- _state.sent()
3024
- ]);
3025
- headerQs = Buffer.from(headerString).toString("base64");
3026
- payloadQs = Buffer.from(payloadString).toString("base64");
3027
+ headerObj = _state.sent();
3028
+ headerString = JSON.stringify(headerObj);
3029
+ headerBase64url = Buffer.from(headerString).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
3027
3030
  if (this.proxyUrl) {
3028
3031
  discoverableEndpoint = this.proxyUrl.replace(/\/graphql$/, "").concat(customDomainPath).replace("https://", "wss://").replace("http://", "ws://");
3029
3032
  } else if (this.isCustomDomain(appSyncGraphqlEndpoint)) {
@@ -3033,11 +3036,15 @@ var AppSyncRealTimeSubscriptionHandshakeLink = /*#__PURE__*/ function(ApolloLink
3033
3036
  discoverableEndpoint = appSyncGraphqlEndpoint.replace("appsync-api", "appsync-realtime-api").replace("gogi-beta", "grt-beta");
3034
3037
  discoverableEndpoint = discoverableEndpoint.replace("https://", "wss://").replace("http://", "ws://");
3035
3038
  }
3036
- awsRealTimeUrl = "".concat(discoverableEndpoint, "?header=").concat(headerQs, "&payload=").concat(payloadQs);
3039
+ protocols = [
3040
+ "graphql-ws",
3041
+ "header-".concat(headerBase64url)
3042
+ ];
3037
3043
  return [
3038
3044
  4,
3039
3045
  this._initializeRetryableHandshake({
3040
- awsRealTimeUrl: awsRealTimeUrl
3046
+ awsRealTimeUrl: discoverableEndpoint,
3047
+ protocols: protocols
3041
3048
  })
3042
3049
  ];
3043
3050
  case 3:
@@ -3097,11 +3104,7 @@ var AppSyncRealTimeSubscriptionHandshakeLink = /*#__PURE__*/ function(ApolloLink
3097
3104
  };
3098
3105
  handler = headerHandler[authenticationType];
3099
3106
  if (typeof handler !== "function") {
3100
- logger3("Authentication type ".concat(authenticationType, " not supported"));
3101
- return [
3102
- 2,
3103
- {}
3104
- ];
3107
+ throw new NonRetryableError("Authentication type ".concat(authenticationType, " not supported"));
3105
3108
  }
3106
3109
  host = url.parse(this.url).host;
3107
3110
  return [
@@ -3275,17 +3278,18 @@ var AppSyncRealTimeSubscriptionHandshakeLink = /*#__PURE__*/ function(ApolloLink
3275
3278
  key: "_initializeRetryableHandshake",
3276
3279
  value: function _initializeRetryableHandshake(_0) {
3277
3280
  return _async_to_generator(function(param) {
3278
- var awsRealTimeUrl;
3281
+ var awsRealTimeUrl, protocols;
3279
3282
  return _ts_generator(this, function(_state) {
3280
3283
  switch(_state.label){
3281
3284
  case 0:
3282
- awsRealTimeUrl = param.awsRealTimeUrl;
3285
+ awsRealTimeUrl = param.awsRealTimeUrl, protocols = param.protocols;
3283
3286
  logger3("Initializaling retryable Handshake");
3284
3287
  return [
3285
3288
  4,
3286
3289
  jitteredExponentialRetry(this._initializeHandshake.bind(this), [
3287
3290
  {
3288
- awsRealTimeUrl: awsRealTimeUrl
3291
+ awsRealTimeUrl: awsRealTimeUrl,
3292
+ protocols: protocols
3289
3293
  }
3290
3294
  ])
3291
3295
  ];
@@ -3303,12 +3307,12 @@ var AppSyncRealTimeSubscriptionHandshakeLink = /*#__PURE__*/ function(ApolloLink
3303
3307
  key: "_initializeHandshake",
3304
3308
  value: function _initializeHandshake(_0) {
3305
3309
  return _async_to_generator(function(param) {
3306
- var _this, awsRealTimeUrl, err, errorType, errorCode;
3310
+ var _this, awsRealTimeUrl, protocols, err, errorType, errorCode;
3307
3311
  return _ts_generator(this, function(_state) {
3308
3312
  switch(_state.label){
3309
3313
  case 0:
3310
3314
  _this = this;
3311
- awsRealTimeUrl = param.awsRealTimeUrl;
3315
+ awsRealTimeUrl = param.awsRealTimeUrl, protocols = param.protocols;
3312
3316
  logger3("Initializing handshake ".concat(awsRealTimeUrl));
3313
3317
  _state.label = 1;
3314
3318
  case 1:
@@ -3322,7 +3326,7 @@ var AppSyncRealTimeSubscriptionHandshakeLink = /*#__PURE__*/ function(ApolloLink
3322
3326
  4,
3323
3327
  function() {
3324
3328
  return new Promise(function(res, rej) {
3325
- var newSocket = _AppSyncRealTimeSubscriptionHandshakeLink.createWebSocket(awsRealTimeUrl, "graphql-ws");
3329
+ var newSocket = _AppSyncRealTimeSubscriptionHandshakeLink.createWebSocket(awsRealTimeUrl, protocols);
3326
3330
  newSocket.onerror = function() {
3327
3331
  logger3("WebSocket connection error");
3328
3332
  };
@@ -3356,13 +3360,20 @@ var AppSyncRealTimeSubscriptionHandshakeLink = /*#__PURE__*/ function(ApolloLink
3356
3360
  rej(new Error(JSON.stringify(event)));
3357
3361
  };
3358
3362
  _this.awsRealTimeSocket.onmessage = function(message) {
3359
- logger3("subscription message from AWS AppSyncRealTime: ".concat(message.data, " "));
3360
- var data = JSON.parse(message.data);
3363
+ var data;
3364
+ try {
3365
+ data = JSON.parse(message.data);
3366
+ } catch (e) {
3367
+ logger3("Failed to parse WebSocket message");
3368
+ return;
3369
+ }
3361
3370
  var type = data.type, tmp = data.payload, _ref = tmp === void 0 ? {} : tmp, _ref_connectionTimeoutMs = _ref.connectionTimeoutMs, connectionTimeoutMs = _ref_connectionTimeoutMs === void 0 ? DEFAULT_KEEP_ALIVE_TIMEOUT : _ref_connectionTimeoutMs;
3371
+ logger3("subscription message from AWS AppSyncRealTime: ".concat(type));
3362
3372
  if (type === "connection_ack" /* GQL_CONNECTION_ACK */ ) {
3363
3373
  ackOk = true;
3374
+ var validTimeout = typeof connectionTimeoutMs === "number" && connectionTimeoutMs >= SERVER_KEEP_ALIVE_TIMEOUT ? connectionTimeoutMs : DEFAULT_KEEP_ALIVE_TIMEOUT;
3364
3375
  var _this_keepAliveTimeout;
3365
- _this.keepAliveTimeout = (_this_keepAliveTimeout = _this.keepAliveTimeout) !== null && _this_keepAliveTimeout !== void 0 ? _this_keepAliveTimeout : connectionTimeoutMs;
3376
+ _this.keepAliveTimeout = (_this_keepAliveTimeout = _this.keepAliveTimeout) !== null && _this_keepAliveTimeout !== void 0 ? _this_keepAliveTimeout : validTimeout;
3366
3377
  _this.awsRealTimeSocket.onmessage = _this._handleIncomingSubscriptionMessage.bind(_this);
3367
3378
  _this.awsRealTimeSocket.onerror = function(err) {
3368
3379
  logger3(err);
@@ -3423,8 +3434,15 @@ var AppSyncRealTimeSubscriptionHandshakeLink = /*#__PURE__*/ function(ApolloLink
3423
3434
  {
3424
3435
  key: "_handleIncomingSubscriptionMessage",
3425
3436
  value: function _handleIncomingSubscriptionMessage(message) {
3426
- logger3("subscription message from AWS AppSync RealTime: ".concat(message.data));
3427
- var _JSON_parse = JSON.parse(message.data), _JSON_parse_id = _JSON_parse.id, id = _JSON_parse_id === void 0 ? "" : _JSON_parse_id, payload = _JSON_parse.payload, type = _JSON_parse.type;
3437
+ var parsed;
3438
+ try {
3439
+ parsed = JSON.parse(message.data);
3440
+ } catch (e) {
3441
+ logger3("Failed to parse incoming subscription message");
3442
+ return;
3443
+ }
3444
+ var _parsed_id = parsed.id, id = _parsed_id === void 0 ? "" : _parsed_id, payload = parsed.payload, type = parsed.type;
3445
+ logger3("subscription message from AWS AppSync RealTime: ".concat(type, " id: ").concat(id));
3428
3446
  var _ref = this.subscriptionObserverMap.get(id) || {}, _ref_observer = _ref.observer, observer = _ref_observer === void 0 ? null : _ref_observer, _ref_query = _ref.query, query = _ref_query === void 0 ? "" : _ref_query, _ref_variables = _ref.variables, variables = _ref_variables === void 0 ? {} : _ref_variables, _ref_startAckTimeoutId = _ref.startAckTimeoutId, startAckTimeoutId = _ref_startAckTimeoutId === void 0 ? 0 : _ref_startAckTimeoutId, _ref_subscriptionReadyCallback = _ref.subscriptionReadyCallback, subscriptionReadyCallback = _ref_subscriptionReadyCallback === void 0 ? null : _ref_subscriptionReadyCallback, _ref_subscriptionFailedCallback = _ref.subscriptionFailedCallback, subscriptionFailedCallback = _ref_subscriptionFailedCallback === void 0 ? null : _ref_subscriptionFailedCallback;
3429
3447
  logger3({
3430
3448
  id: id,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aws-appsync-subscription-link",
3
- "version": "4.0.1",
3
+ "version": "4.0.2",
4
4
  "main": "lib/index.js",
5
5
  "module": "lib/index.mjs",
6
6
  "types": "lib/index.d.ts",