@agnostack/verifyd 2.1.2 → 2.2.0-beta.1

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 (83) hide show
  1. package/CHANGELOG.md +10 -74
  2. package/dist/lib/utils/rawbody.js +17 -7
  3. package/dist/lib/utils/rawbody.js.map +1 -1
  4. package/dist/lib/verification.d.ts +1 -1
  5. package/dist/lib/verification.d.ts.map +1 -1
  6. package/dist/lib/verification.js +1 -1
  7. package/dist/lib/verification.js.map +1 -1
  8. package/dist/react/hooks/useVerification.d.ts +1 -4
  9. package/dist/react/hooks/useVerification.d.ts.map +1 -1
  10. package/dist/react/hooks/useVerification.js.map +1 -1
  11. package/dist/shared/WebCrypto.d.ts +1 -4
  12. package/dist/shared/WebCrypto.d.ts.map +1 -1
  13. package/dist/shared/WebCrypto.js +26 -16
  14. package/dist/shared/WebCrypto.js.map +1 -1
  15. package/dist/shared/authorization.d.ts +1 -4
  16. package/dist/shared/authorization.d.ts.map +1 -1
  17. package/dist/shared/authorization.js +1 -1
  18. package/dist/shared/authorization.js.map +1 -1
  19. package/dist/shared/display.d.ts +8 -16
  20. package/dist/shared/display.d.ts.map +1 -1
  21. package/dist/shared/display.js +15 -51
  22. package/dist/shared/display.js.map +1 -1
  23. package/dist/shared/index.js +1 -1
  24. package/dist/shared/index.js.map +1 -1
  25. package/dist/shared/request.d.ts +2 -6
  26. package/dist/shared/request.d.ts.map +1 -1
  27. package/dist/shared/request.js.map +1 -1
  28. package/dist/shared/verification.d.ts +3 -20
  29. package/dist/shared/verification.d.ts.map +1 -1
  30. package/dist/shared/verification.js +2 -2
  31. package/dist/shared/verification.js.map +1 -1
  32. package/dist/umd/index.js +1001 -0
  33. package/dist/umd/index.js.map +1 -0
  34. package/dist/umd/lib/index.d.ts.map +1 -0
  35. package/dist/umd/lib/types.d.ts.map +1 -0
  36. package/dist/umd/lib/utils/errors.d.ts.map +1 -0
  37. package/dist/umd/lib/{lib/utils → utils}/index.d.ts.map +1 -1
  38. package/dist/umd/lib/utils/rawbody.d.ts.map +1 -0
  39. package/dist/umd/lib/verification.d.ts.map +1 -0
  40. package/dist/umd/{lib/react → react}/hooks/index.d.ts.map +1 -1
  41. package/dist/umd/react/hooks/useVerification.d.ts.map +1 -0
  42. package/dist/umd/react/index.d.ts.map +1 -0
  43. package/dist/umd/{lib/react → react}/types.d.ts.map +1 -1
  44. package/dist/umd/shared/WebCrypto.d.ts.map +1 -0
  45. package/dist/umd/shared/authorization.d.ts.map +1 -0
  46. package/dist/umd/{lib/shared → shared}/display.d.ts +6 -14
  47. package/dist/umd/shared/display.d.ts.map +1 -0
  48. package/dist/umd/shared/index.d.ts.map +1 -0
  49. package/dist/umd/{lib/shared → shared}/request.d.ts +1 -1
  50. package/dist/umd/shared/request.d.ts.map +1 -0
  51. package/dist/umd/{lib/shared → shared}/types.d.ts.map +1 -1
  52. package/dist/umd/shared/verification.d.ts.map +1 -0
  53. package/package.json +31 -27
  54. package/dist/umd/lib/index.js +0 -26
  55. package/dist/umd/lib/index.js.map +0 -1
  56. package/dist/umd/lib/lib/index.d.ts.map +0 -1
  57. package/dist/umd/lib/lib/types.d.ts.map +0 -1
  58. package/dist/umd/lib/lib/utils/errors.d.ts.map +0 -1
  59. package/dist/umd/lib/lib/utils/rawbody.d.ts.map +0 -1
  60. package/dist/umd/lib/lib/verification.d.ts.map +0 -1
  61. package/dist/umd/lib/react/hooks/useVerification.d.ts.map +0 -1
  62. package/dist/umd/lib/react/index.d.ts.map +0 -1
  63. package/dist/umd/lib/shared/WebCrypto.d.ts.map +0 -1
  64. package/dist/umd/lib/shared/authorization.d.ts.map +0 -1
  65. package/dist/umd/lib/shared/display.d.ts.map +0 -1
  66. package/dist/umd/lib/shared/index.d.ts.map +0 -1
  67. package/dist/umd/lib/shared/request.d.ts.map +0 -1
  68. package/dist/umd/lib/shared/verification.d.ts.map +0 -1
  69. /package/dist/umd/lib/{lib/index.d.ts → index.d.ts} +0 -0
  70. /package/dist/umd/lib/{lib/types.d.ts → types.d.ts} +0 -0
  71. /package/dist/umd/lib/{lib/utils → utils}/errors.d.ts +0 -0
  72. /package/dist/umd/lib/{lib/utils → utils}/index.d.ts +0 -0
  73. /package/dist/umd/lib/{lib/utils → utils}/rawbody.d.ts +0 -0
  74. /package/dist/umd/lib/{lib/verification.d.ts → verification.d.ts} +0 -0
  75. /package/dist/umd/{lib/react → react}/hooks/index.d.ts +0 -0
  76. /package/dist/umd/{lib/react → react}/hooks/useVerification.d.ts +0 -0
  77. /package/dist/umd/{lib/react → react}/index.d.ts +0 -0
  78. /package/dist/umd/{lib/react → react}/types.d.ts +0 -0
  79. /package/dist/umd/{lib/shared → shared}/WebCrypto.d.ts +0 -0
  80. /package/dist/umd/{lib/shared → shared}/authorization.d.ts +0 -0
  81. /package/dist/umd/{lib/shared → shared}/index.d.ts +0 -0
  82. /package/dist/umd/{lib/shared → shared}/types.d.ts +0 -0
  83. /package/dist/umd/{lib/shared → shared}/verification.d.ts +0 -0
@@ -0,0 +1,1001 @@
1
+ /**
2
+ * MIT License
3
+ *
4
+ * Copyright (c) 2021-present agnoStack, Inc. and Adam Grohs
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in all
14
+ * copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ * SOFTWARE.
23
+ */
24
+
25
+ (function (global, factory) {
26
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
27
+ typeof define === 'function' && define.amd ? define(['exports'], factory) :
28
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["@agnostack/verifyd/external"] = {}));
29
+ })(this, (function (exports) { 'use strict';
30
+
31
+ /* eslint-disable no-use-before-define */
32
+
33
+ // #region lib-core
34
+ // TODO!!!: keep in sync between next-shopify and lib-core (AND lib-utils-js at bottom)
35
+
36
+ const REACT_SYMBOL = Symbol.for('react.element');
37
+
38
+ const isArray = (value) => (
39
+ // eslint-disable-next-line eqeqeq
40
+ (value != undefined) && Array.isArray(value)
41
+ );
42
+
43
+ const isSet = (value) => (
44
+ // eslint-disable-next-line eqeqeq
45
+ (value != undefined) && (value instanceof Set)
46
+ );
47
+
48
+ const isType = (value, type) => (
49
+ // eslint-disable-next-line eqeqeq, valid-typeof
50
+ (value != undefined) && (typeof value === type)
51
+ );
52
+
53
+ const isString = (value) => (
54
+ isType(value, 'string') ||
55
+ (value instanceof String)
56
+ );
57
+
58
+ const isReact = (value) => (
59
+ isType(value, 'object') &&
60
+ (value?.$$typeof === REACT_SYMBOL)
61
+ );
62
+
63
+ const isClass = (value) => (
64
+ isType(value, 'object') &&
65
+ (Object.getPrototypeOf(value) !== Object.prototype) &&
66
+ !isReact(value)
67
+ );
68
+
69
+ const isTypeEnhanced = (value, type) => {
70
+ switch (true) {
71
+ case (type === 'boolean'): {
72
+ return isType(value, type) || ['true', 'false'].includes(value)
73
+ }
74
+
75
+ case (type === 'string'): {
76
+ return isString(value)
77
+ }
78
+
79
+ case (type === 'set'): {
80
+ return isSet(value)
81
+ }
82
+
83
+ case (type === 'react'): {
84
+ return isReact(value)
85
+ }
86
+
87
+ case (type === 'class'): {
88
+ return isClass(value)
89
+ }
90
+
91
+ case (type === 'array'): {
92
+ return (
93
+ isArray(value) &&
94
+ !isSet(value)
95
+ )
96
+ }
97
+
98
+ case (type === 'object'): {
99
+ return (
100
+ isType(value, type) &&
101
+ !isArray(value) &&
102
+ !isClass(value) &&
103
+ !isString(value) &&
104
+ !isReact(value)
105
+ )
106
+ }
107
+
108
+ default: {
109
+ return isType(value, type)
110
+ }
111
+ }
112
+ };
113
+
114
+ const safeTrim = (value) => {
115
+ if (isTypeEnhanced(value, 'string')) {
116
+ // eslint-disable-next-line no-param-reassign
117
+ value = value.trim();
118
+ }
119
+
120
+ return value
121
+ };
122
+
123
+ const isParseable = (value, trim, matchQuoted) => {
124
+ if (!isTypeEnhanced(value, 'string')) {
125
+ return false
126
+ }
127
+
128
+ if (trim) {
129
+ // eslint-disable-next-line no-param-reassign
130
+ value = safeTrim(value);
131
+ }
132
+
133
+ return isTrue(value.startsWith('{') || value.startsWith('[') || (matchQuoted && value.startsWith('"')))
134
+ };
135
+
136
+ const safeParse = (value, trim) => {
137
+ switch (true) {
138
+ case isTypeEnhanced(value, 'object'):
139
+ case isTypeEnhanced(value, 'array'): {
140
+ return value
141
+ }
142
+
143
+ case isParseable(value, trim, true): {
144
+ if (trim) {
145
+ // eslint-disable-next-line no-param-reassign
146
+ value = safeTrim(value);
147
+ }
148
+
149
+ try {
150
+ return JSON.parse(value)
151
+ } catch (_ignore) {
152
+ console.info('Ignoring error parsing', _ignore);
153
+ // TODO: should this be value ?? {}
154
+ return value
155
+ }
156
+ }
157
+
158
+ default: {
159
+ // TODO: should this be value ?? {}
160
+ return value
161
+ }
162
+ }
163
+ };
164
+
165
+ const ensureString = (string) => (
166
+ string ? `${string}` : ''
167
+ );
168
+
169
+ const ensureArray = (array = []) => (
170
+ // eslint-disable-next-line no-nested-ternary
171
+ !array ? [] : Array.isArray(array) ? array : [array]
172
+ );
173
+
174
+ const ensureObject = (object) => (
175
+ object ?? {}
176
+ );
177
+
178
+ // NOTE: this does not ensure !isType(string)
179
+ const objectEmpty = (object) => (
180
+ !object || !Object.keys(object).length
181
+ );
182
+
183
+ const stringNotEmpty = (stringable) => {
184
+ const string = ensureString(stringable);
185
+ return ((string.length > 0) && !['null', 'undefined'].includes(string))
186
+ };
187
+
188
+ const stringEmpty = (stringable) => (
189
+ !stringNotEmpty(stringable)
190
+ );
191
+
192
+ const compareString = (string1, string2) => {
193
+ if (stringEmpty(string1)) {
194
+ return 1
195
+ }
196
+
197
+ if (stringEmpty(string2)) {
198
+ return -1
199
+ }
200
+
201
+ return ensureString(string1).localeCompare(string2)
202
+ };
203
+
204
+ const compareEntryKeys = ([string1], [string2]) => (
205
+ compareString(string1, string2)
206
+ );
207
+
208
+ const objectToSortedString = (object, separator = '') => (
209
+ Object.entries(ensureObject(object))
210
+ .sort(compareEntryKeys)
211
+ .map(([key, value]) => `${key}=${value}`)
212
+ .join(separator)
213
+ );
214
+
215
+ const uppercase = (string) => (
216
+ ensureString(string).toUpperCase()
217
+ );
218
+
219
+ const lowercase = (string, defaultValue = '') => (
220
+ !stringNotEmpty(string)
221
+ ? defaultValue
222
+ : ensureString(string).toLowerCase()
223
+ );
224
+
225
+ const removeLeadingSlash = (string) => (
226
+ ensureString(string).replace(/^\//, '')
227
+ );
228
+
229
+ const ensureLeadingSlash = (string) => (
230
+ `/${removeLeadingSlash(string)}`
231
+ );
232
+
233
+ const isTrue = (value, falsy) => {
234
+ const string = ensureString(value);
235
+ return ![...ensureArray(falsy), '', 'false'].includes(string)
236
+ };
237
+ // #endregion lib-utils-js
238
+
239
+ var n,r=((n=function(){return r}).toString=n.toLocaleString=n[Symbol.toPrimitive]=function(){return ""},n.valueOf=function(){return !1},new Proxy(Object.freeze(n),{get:function(n,t){return n.hasOwnProperty(t)?n[t]:r}})),u=function(n){return n===r};
240
+
241
+ let win = global.window;
242
+ let exists = (variable) => !u(variable);
243
+ let window = typeof win !== "undefined" ? win : r;
244
+
245
+ class WebCrypto {
246
+ constructor({ crypto: _crypto, util: _util } = {}) {
247
+ this._crypto = _crypto ?? {};
248
+ this._util = _util ?? {};
249
+ }
250
+
251
+ get subtle() {
252
+ return this._crypto?.subtle
253
+ }
254
+
255
+ async getWebCrypto() {
256
+ if (!this._crypto?.subtle) {
257
+ try {
258
+ this._crypto = (await import('isomorphic-webcrypto')).default;
259
+ } catch (_ignore) {
260
+ console.info('Failed to import isomorphic-webcrypto, retrying w/ node crypto');
261
+ try {
262
+ this._crypto = (await import('crypto')).default;
263
+ } catch (error) {
264
+ // eslint-disable-next-line max-len
265
+ console.error(`Failed to import node crypto, ensure 'isomorphic-webcrypto' (or node 'crypto') is installed and/or pass in implementation via 'new WebCrypto({ crypto })'`);
266
+ throw error
267
+ }
268
+ }
269
+ }
270
+
271
+ if (!this._crypto?.subtle) {
272
+ throw new Error('Invalid crypto, missing subtle')
273
+ }
274
+
275
+ return this._crypto
276
+ }
277
+
278
+ async getTextDecoder() {
279
+ if (this._util?.TextDecoder) {
280
+ return this._util.TextDecoder
281
+ }
282
+
283
+ if (exists(window) && typeof window?.TextDecoder === 'function') {
284
+ return window.TextDecoder
285
+ }
286
+
287
+ try {
288
+ const TextDecoder = (await import('util')).TextDecoder;
289
+ this._util.TextDecoder = TextDecoder;
290
+
291
+ return TextDecoder
292
+ } catch (error) {
293
+ console.error(`Failed to import 'utils.TextDecoder', ensure 'util' is available and/or pass in implementation via 'new WebCrypto({ util })'`);
294
+ throw error
295
+ }
296
+ }
297
+
298
+ async getTextEncoder() {
299
+ if (this._util?.TextEncoder) {
300
+ return this._util.TextEncoder
301
+ }
302
+
303
+ if (exists(window) && typeof window?.TextEncoder === 'function') {
304
+ return window.TextEncoder
305
+ }
306
+
307
+ try {
308
+ const TextEncoder = (await import('util')).TextEncoder;
309
+ this._util.TextEncoder = TextEncoder;
310
+
311
+ return TextEncoder
312
+ } catch (error) {
313
+ console.error(`Failed to import 'utils.TextEncoder', ensure 'util' is available and/or pass in implementation via 'new WebCrypto({ util })'`);
314
+ throw error
315
+ }
316
+ }
317
+
318
+ timingSafeEqual(value1, value2) {
319
+ if (
320
+ (value1 == undefined) ||
321
+ (value2 == undefined) ||
322
+ (value1.length !== value2.length)
323
+ ) {
324
+ return false
325
+ }
326
+
327
+ let result = 0;
328
+ // eslint-disable-next-line no-plusplus
329
+ for (let i = 0; i < value1.length; i++) {
330
+ // eslint-disable-next-line no-bitwise
331
+ result |= value1[i] ^ value2[i];
332
+ }
333
+
334
+ return (result === 0)
335
+ }
336
+
337
+ stringToHex(stringValue) {
338
+ return (
339
+ Array.from(ensureString(stringValue), (char) => (
340
+ char.charCodeAt(0).toString(16).padStart(2, '0')
341
+ )).join('')
342
+ )
343
+ }
344
+
345
+ hexToString(hexValue) {
346
+ return (
347
+ ensureArray(
348
+ ensureString(hexValue).match(/.{1,2}/g)
349
+ )
350
+ .map((byte) => String.fromCharCode(parseInt(byte, 16)))
351
+ .join('')
352
+ )
353
+ }
354
+
355
+ async arrayBufferToString(arrayBuffer) {
356
+ const uint8Array = new Uint8Array(arrayBuffer);
357
+ const Decoder = await this.getTextDecoder();
358
+ return new Decoder().decode(uint8Array)
359
+ }
360
+
361
+ arrayToArrayBuffer(array) {
362
+ return (
363
+ (ArrayBuffer.from != undefined)
364
+ ? ArrayBuffer.from(array)
365
+ : new Uint8Array(array).buffer
366
+ )
367
+ }
368
+
369
+ ensureArrayBuffer(arrayOrArrayBuffer) {
370
+ return (
371
+ (arrayOrArrayBuffer instanceof ArrayBuffer)
372
+ ? arrayOrArrayBuffer
373
+ : this.arrayToArrayBuffer(arrayOrArrayBuffer)
374
+ )
375
+ }
376
+
377
+ getKeyOperations(keyType) {
378
+ switch (keyType) {
379
+ case 'paired':
380
+ case 'private':
381
+ case 'privateKey': {
382
+ return ['deriveKey']
383
+ }
384
+
385
+ case 'secret':
386
+ case 'secretKey':
387
+ case 'sharedSecret': {
388
+ return ['encrypt', 'decrypt']
389
+ }
390
+
391
+ default: {
392
+ return []
393
+ }
394
+ }
395
+ }
396
+
397
+ getKeyAlgorythm(keyType) {
398
+ switch (keyType) {
399
+ case 'derivedKey':
400
+ case 'derived':
401
+ case 'secret':
402
+ case 'secretKey':
403
+ case 'sharedSecret': {
404
+ return {
405
+ name: 'AES-GCM',
406
+ }
407
+ }
408
+
409
+ default: {
410
+ return {
411
+ name: 'ECDH',
412
+ namedCurve: 'P-256',
413
+ }
414
+ }
415
+ }
416
+ }
417
+
418
+ async generateKeyPair() {
419
+ const crypto = await this.getWebCrypto();
420
+ const keyPair = await crypto.subtle.generateKey(
421
+ this.getKeyAlgorythm('paired'),
422
+ true,
423
+ this.getKeyOperations('paired')
424
+ );
425
+
426
+ return keyPair
427
+ }
428
+
429
+ async generateSharedSecret() {
430
+ const crypto = await this.getWebCrypto();
431
+ const keyPair = await crypto.subtle.generateKey(
432
+ this.getKeyAlgorythm('sharedSecret'),
433
+ true,
434
+ this.getKeyOperations('sharedSecret')
435
+ );
436
+
437
+ return keyPair
438
+ }
439
+
440
+ async generateHMAC(message, derivedKey) {
441
+ if (!message || !derivedKey) {
442
+ return undefined
443
+ }
444
+
445
+ const crypto = await this.getWebCrypto();
446
+ const Encoder = await this.getTextEncoder();
447
+
448
+ const signature = await crypto.subtle.sign(
449
+ 'HMAC',
450
+ derivedKey,
451
+ new Encoder().encode(message)
452
+ );
453
+
454
+ return this.stringToHex(
455
+ this.arrayBufferToString(signature)
456
+ )
457
+ }
458
+
459
+ async verifyHMAC(message, derivedKey, verifiableHMAC) {
460
+ const calculatedHMAC = await this.generateHMAC(message, derivedKey);
461
+
462
+ return this.timingSafeEqual(calculatedHMAC, verifiableHMAC)
463
+ }
464
+
465
+ async getStorableKey(key) {
466
+ const crypto = await this.getWebCrypto();
467
+
468
+ const exportedJWK = await crypto.subtle.exportKey('jwk', key);
469
+ return this.stringToHex(JSON.stringify(exportedJWK))
470
+ }
471
+
472
+ async restoreStorableKey(keyType, storableHex) {
473
+ if (!storableHex) {
474
+ return undefined
475
+ }
476
+ const crypto = await this.getWebCrypto();
477
+
478
+ const exportedJWK = JSON.parse(this.hexToString(storableHex) || '{}');
479
+ if (objectEmpty(exportedJWK)) {
480
+ return undefined
481
+ }
482
+
483
+ return crypto.subtle.importKey(
484
+ 'jwk',
485
+ exportedJWK,
486
+ this.getKeyAlgorythm(keyType),
487
+ true,
488
+ this.getKeyOperations(keyType)
489
+ )
490
+ }
491
+
492
+ async getStorableKeyPair(keyPair) {
493
+ const storableKeys = {};
494
+
495
+ // eslint-disable-next-line no-restricted-syntax
496
+ for (const [keyType, key] of Object.entries(keyPair)) {
497
+ // eslint-disable-next-line no-await-in-loop
498
+ storableKeys[keyType] = await this.getStorableKey(key);
499
+ }
500
+
501
+ return storableKeys
502
+ }
503
+
504
+ async restoreStorableKeyPair(keyPair) {
505
+ const restoredKeys = {};
506
+
507
+ // eslint-disable-next-line no-restricted-syntax
508
+ for (const [keyType, key] of Object.entries(keyPair)) {
509
+ // eslint-disable-next-line no-await-in-loop
510
+ restoredKeys[keyType] = await this.restoreStorableKey(keyType, key);
511
+ }
512
+
513
+ return restoredKeys
514
+ }
515
+
516
+ async deriveSharedKey({ publicKey, privateKey }) {
517
+ if (!publicKey || !privateKey) {
518
+ return undefined
519
+ }
520
+
521
+ const crypto = await this.getWebCrypto();
522
+ const derivedKey = await crypto.subtle.deriveKey(
523
+ {
524
+ name: 'ECDH',
525
+ public: publicKey,
526
+ },
527
+ privateKey,
528
+ {
529
+ name: 'AES-GCM',
530
+ length: 256,
531
+ },
532
+ true,
533
+ ['encrypt', 'decrypt']
534
+ );
535
+ return derivedKey
536
+ }
537
+
538
+ async deriveHMACKey({ publicKey, privateKey }) {
539
+ if (!publicKey || !privateKey) {
540
+ return undefined
541
+ }
542
+
543
+ const crypto = await this.getWebCrypto();
544
+ const derivedKey = await crypto.subtle.deriveKey(
545
+ {
546
+ name: 'ECDH',
547
+ public: publicKey,
548
+ },
549
+ privateKey,
550
+ {
551
+ name: 'HMAC',
552
+ hash: { name: 'SHA-256' },
553
+ length: 256, // Adjusted key length, e.g., 128 bits
554
+ },
555
+ true,
556
+ ['sign', 'verify']
557
+ );
558
+ return derivedKey
559
+ }
560
+
561
+ async getVerificationKeys({ publicKey, privateKey }) {
562
+ if (!publicKey || !privateKey) {
563
+ return {}
564
+ }
565
+
566
+ const sharedKeyPair = await this.restoreStorableKeyPair({ publicKey, privateKey });
567
+ const derivedHMACKey = await this.deriveHMACKey(sharedKeyPair);
568
+ const derivedSecretKey = await this.deriveSharedKey(sharedKeyPair);
569
+
570
+ return {
571
+ derivedSecretKey,
572
+ derivedHMACKey,
573
+ }
574
+ }
575
+
576
+ async encryptMessage(decryptedMessage, derivedKey) {
577
+ if (!decryptedMessage || !derivedKey) {
578
+ return undefined
579
+ }
580
+
581
+ const crypto = await this.getWebCrypto();
582
+ const iv = crypto.getRandomValues(new Uint8Array(12));
583
+ const Encoder = await this.getTextEncoder();
584
+ const encodedMessage = new Encoder().encode(decryptedMessage);
585
+ const ciphertext = await crypto.subtle.encrypt(
586
+ {
587
+ name: 'AES-GCM',
588
+ iv,
589
+ },
590
+ derivedKey,
591
+ encodedMessage
592
+ );
593
+
594
+ const encryptedMessage = new Uint8Array([
595
+ ...iv,
596
+ ...new Uint8Array(ciphertext)
597
+ ]);
598
+ return Array.from(encryptedMessage)
599
+ }
600
+
601
+ async decryptMessage(encryptedMessage, derivedKey) {
602
+ if (!encryptedMessage || !derivedKey) {
603
+ return undefined
604
+ }
605
+
606
+ const crypto = await this.getWebCrypto();
607
+ // NOTE: this presumed an array or arrayBuffer coming in as encryptedMessage (will fail w/ IV error if its a string)
608
+ const encryptedArrayBuffer = this.ensureArrayBuffer(encryptedMessage);
609
+ const iv = encryptedArrayBuffer.slice(0, 12);
610
+ const ciphertext = encryptedArrayBuffer.slice(12);
611
+
612
+ const decryptedArrayBuffer = await crypto.subtle.decrypt(
613
+ {
614
+ name: 'AES-GCM',
615
+ iv,
616
+ },
617
+ derivedKey,
618
+ ciphertext
619
+ );
620
+
621
+ const Decoder = await this.getTextDecoder();
622
+ const decryptedMessage = new Decoder().decode(decryptedArrayBuffer);
623
+ return decryptedMessage
624
+ }
625
+ }
626
+
627
+ const getAuthorizationHelpers = async (sharedSecret, { crypto: _crypto, util: _util } = {}) => {
628
+ if (stringEmpty(sharedSecret)) {
629
+ return undefined
630
+ }
631
+
632
+ const webCrypto = new WebCrypto({ crypto: _crypto, util: _util });
633
+ const sharedKey = await webCrypto.restoreStorableKey(
634
+ 'sharedSecret',
635
+ sharedSecret
636
+ );
637
+
638
+ return {
639
+ encrypt: async (decryptedMessage) => (
640
+ webCrypto.encryptMessage(decryptedMessage, sharedKey)
641
+ ),
642
+ decrypt: async (encryptedMessage) => (
643
+ webCrypto.decryptMessage(encryptedMessage, sharedKey)
644
+ ),
645
+ }
646
+ };
647
+
648
+ const TEMP_HOSTNAME = 'xyz.com';
649
+ const REMOVABLE_KEYS = ['shop', 'host'];
650
+
651
+ const HEADER_CONTENT_TYPE = 'Content-Type';
652
+ const HEADER_PUBLIC_KEY = 'X-Public-Key';
653
+ const HEADER_EPHEMERAL_KEY = 'X-Ephemeral-Key';
654
+ const HEADER_AUTHORIZATION_CUSTOM = 'X-Authorization';
655
+ const HEADER_AUTHORIZATION_TIMESTAMP = 'X-Authorization-Timestamp';
656
+
657
+ const CONTENT_TYPES = {
658
+ APPLICATION_JSON: 'application/json',
659
+ };
660
+
661
+ const getRequestMethod = (body, _method) => {
662
+ const method = _method ?? (body ? 'POST' : 'GET');
663
+
664
+ return uppercase(method)
665
+ };
666
+
667
+ const prepareRequestOptions = ({ method: _method, body: _body, headers: _headers, ...requestOptions } = {}) => {
668
+ const method = getRequestMethod(_body, _method);
669
+
670
+ const _contentType = _headers?.[HEADER_CONTENT_TYPE.toLowerCase()];
671
+ const headers = {
672
+ [HEADER_CONTENT_TYPE]: _contentType || CONTENT_TYPES.APPLICATION_JSON,
673
+ ..._headers,
674
+ };
675
+
676
+ let body = _body;
677
+ if (body && headers[HEADER_CONTENT_TYPE].startsWith(CONTENT_TYPES.APPLICATION_JSON)) {
678
+ body = JSON.stringify(safeParse(body));
679
+ }
680
+
681
+ return {
682
+ method,
683
+ headers,
684
+ ...(body && (method !== 'GET')) && { body },
685
+ ...requestOptions,
686
+ }
687
+ };
688
+
689
+ const convertToURL = (uri) => {
690
+ if (stringEmpty(uri)) {
691
+ return undefined
692
+ }
693
+
694
+ const protocolRegex = /^(https?:\/\/).+/i;
695
+
696
+ if (!protocolRegex.test(uri)) {
697
+ return new URL(`https://${TEMP_HOSTNAME}${ensureLeadingSlash(uri)}`)
698
+ }
699
+
700
+ return new URL(uri)
701
+ };
702
+
703
+ const normalizeURIParts = (uri) => {
704
+ const urlObject = convertToURL(uri);
705
+ if (!urlObject) {
706
+ return undefined
707
+ }
708
+
709
+ REMOVABLE_KEYS.forEach((key) => urlObject.searchParams.delete(key));
710
+
711
+ return {
712
+ ...(urlObject.hostname !== TEMP_HOSTNAME) && {
713
+ origin: urlObject.origin,
714
+ },
715
+ pathname: urlObject.pathname,
716
+ search: urlObject.search,
717
+ }
718
+ };
719
+
720
+ const getUnixString = () => {
721
+ const currentDate = new Date();
722
+ const unixTimestamp = currentDate.getTime();
723
+ return Math.floor(unixTimestamp / 1000).toString()
724
+ };
725
+
726
+ const getVerificationKeysData = async (publicKey, { crypto: _crypto, util: _util } = {}) => {
727
+ if (stringEmpty(publicKey)) {
728
+ return {}
729
+ }
730
+
731
+ const webCrypto = new WebCrypto({ crypto: _crypto, util: _util });
732
+
733
+ const _ephemeralStoreableKeyPair = await webCrypto.getStorableKeyPair(
734
+ await webCrypto.generateKeyPair()
735
+ );
736
+
737
+ const _verificationKeyPair = await webCrypto.getVerificationKeys({
738
+ publicKey,
739
+ privateKey: _ephemeralStoreableKeyPair.privateKey,
740
+ });
741
+
742
+ return {
743
+ publicKey,
744
+ ephemeral: _ephemeralStoreableKeyPair,
745
+ verification: _verificationKeyPair,
746
+ }
747
+ };
748
+
749
+ // eslint-disable-next-line arrow-body-style
750
+ const prepareVerificationRequest = ({ keysData: _keysData, disableRecryption: _disableRecryption, crypto: _crypto, util: _util } = {}) => {
751
+ const webCrypto = new WebCrypto({ crypto: _crypto, util: _util });
752
+ const disableRecryption = isTrue(_disableRecryption);
753
+
754
+ return async (requestPath, { method: rawMethod, body: rawBody, headers: rawHeaders, ...requestOptions } = {}) => {
755
+ let parsedBody = safeParse(rawBody);
756
+ const method = getRequestMethod(parsedBody, rawMethod);
757
+
758
+ if (disableRecryption || stringEmpty(_keysData?.publicKey)) {
759
+ return [
760
+ requestPath,
761
+ prepareRequestOptions({
762
+ method,
763
+ body: parsedBody,
764
+ headers: rawHeaders,
765
+ ...requestOptions,
766
+ })
767
+ ]
768
+ }
769
+
770
+ const {
771
+ verification: {
772
+ derivedHMACKey,
773
+ derivedSecretKey,
774
+ } = {},
775
+ ephemeral: {
776
+ publicKey: ephemeralPublicKey,
777
+ } = {},
778
+ } = _keysData ?? {};
779
+
780
+ if (!derivedHMACKey || !ephemeralPublicKey) {
781
+ return undefined
782
+ }
783
+
784
+ if (parsedBody && derivedSecretKey) {
785
+ parsedBody = await webCrypto.encryptMessage(JSON.stringify(parsedBody), derivedSecretKey);
786
+ }
787
+
788
+ const timestamp = getUnixString();
789
+ const computedHMAC = await webCrypto.generateHMAC(
790
+ objectToSortedString({
791
+ body: parsedBody,
792
+ method,
793
+ timestamp,
794
+ ...normalizeURIParts(requestPath),
795
+ }),
796
+ derivedHMACKey
797
+ );
798
+
799
+ return [
800
+ requestPath,
801
+ prepareRequestOptions({
802
+ method,
803
+ body: parsedBody,
804
+ headers: {
805
+ 'X-Authorization': `HMAC-SHA256 ${computedHMAC}`,
806
+ 'X-Authorization-Timestamp': timestamp,
807
+ 'X-Ephemeral-Key': ephemeralPublicKey,
808
+ 'X-Public-Key': _keysData.publicKey,
809
+ ...rawHeaders,
810
+ },
811
+ ...requestOptions,
812
+ }),
813
+ derivedSecretKey
814
+ ]
815
+ }
816
+ };
817
+
818
+ const processVerificationResponse = ({ keysData, disableRecryption: _disableRecryption, crypto: _crypto, util: _util } = {}) => {
819
+ const webCrypto = new WebCrypto({ crypto: _crypto, util: _util });
820
+ const disableRecryption = isTrue(_disableRecryption);
821
+
822
+ return async (encryptedResponse, _derivedSecretKey) => {
823
+ const derivedSecretKey = _derivedSecretKey ?? keysData.verification?.derivedSecretKey;
824
+ if (disableRecryption || !encryptedResponse || !derivedSecretKey) {
825
+ return encryptedResponse
826
+ }
827
+
828
+ const decryptedMessage = await webCrypto.decryptMessage(encryptedResponse, derivedSecretKey);
829
+ return safeParse(decryptedMessage)
830
+ }
831
+ };
832
+
833
+ class VerificationError extends Error {
834
+ constructor(message, _data) {
835
+ super(message);
836
+ const { code = 500, ...data } = _data ?? {};
837
+ this.code = code;
838
+ this.data = data;
839
+ this.name = 'VerificationError';
840
+ Object.setPrototypeOf(this, VerificationError.prototype);
841
+ }
842
+ }
843
+
844
+ const getChunkedRawBody = async (req) => {
845
+ if (req?.rawBody) {
846
+ return req.rawBody
847
+ }
848
+
849
+ // TODO: move to req.text() after next 13.5: https://github.com/vercel/next.js/discussions/13405
850
+ try {
851
+ const _getRawBody = (await import('raw-body')).default;
852
+
853
+ if (!req.method || (lowercase(req.method) === 'get')) {
854
+ return undefined
855
+ }
856
+
857
+ return _getRawBody(req).then((_rawBody) => _rawBody?.toString())
858
+ } catch (error) {
859
+ console.error(`Failed to import 'raw-body', please ensure the dependency is installed`);
860
+ throw error
861
+ }
862
+ };
863
+
864
+ // TODO: explore returning mutated request object adding on req.rawBody??
865
+ const ensureRawBody = async (req) => (
866
+ getChunkedRawBody(req)
867
+ .catch((error) => {
868
+ console.error(`Error getting raw body for '${req?.url}'`, error);
869
+ throw error
870
+ })
871
+ );
872
+
873
+ /******************************************************************************
874
+ Copyright (c) Microsoft Corporation.
875
+
876
+ Permission to use, copy, modify, and/or distribute this software for any
877
+ purpose with or without fee is hereby granted.
878
+
879
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
880
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
881
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
882
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
883
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
884
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
885
+ PERFORMANCE OF THIS SOFTWARE.
886
+ ***************************************************************************** */
887
+
888
+ function __awaiter(thisArg, _arguments, P, generator) {
889
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
890
+ return new (P || (P = Promise))(function (resolve, reject) {
891
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
892
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
893
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
894
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
895
+ });
896
+ }
897
+
898
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
899
+ var e = new Error(message);
900
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
901
+ };
902
+
903
+ const generateStorableKeyPairs = (...args_1) => __awaiter(void 0, [...args_1], void 0, function* ({ crypto: _crypto, util: _util } = {}) {
904
+ const webCrypto = new WebCrypto({ crypto: _crypto, util: _util });
905
+ const sharedKeyPair = yield webCrypto.generateKeyPair();
906
+ return webCrypto.getStorableKeyPair({
907
+ publicKey: sharedKeyPair.publicKey,
908
+ privateKey: sharedKeyPair.privateKey,
909
+ });
910
+ });
911
+ const getVerificationHelpers = ({ keyPairs, util: _util, crypto: _crypto, DISABLE_RECRYPTION, } = { keyPairs: {} }) => {
912
+ const webCrypto = new WebCrypto({ crypto: _crypto, util: _util });
913
+ return (req, params) => __awaiter(void 0, void 0, void 0, function* () {
914
+ var _a;
915
+ const { [HEADER_PUBLIC_KEY]: _apiKey, [HEADER_PUBLIC_KEY.toLowerCase()]: apiKey = _apiKey, [HEADER_EPHEMERAL_KEY]: _ephemeralPublicKey, [HEADER_EPHEMERAL_KEY.toLowerCase()]: ephemeralPublicKey = _ephemeralPublicKey, [HEADER_AUTHORIZATION_TIMESTAMP]: _customAuthTimestamp, [HEADER_AUTHORIZATION_TIMESTAMP.toLowerCase()]: customAuthTimestamp = _customAuthTimestamp, [HEADER_AUTHORIZATION_CUSTOM]: _customAuth, [HEADER_AUTHORIZATION_CUSTOM.toLowerCase()]: customAuth = _customAuth, } = (_a = req.headers) !== null && _a !== void 0 ? _a : {};
916
+ const { uri: _uri, disableRecryption: _disableRecryption } = params !== null && params !== void 0 ? params : {};
917
+ const uri = _uri !== null && _uri !== void 0 ? _uri : req.url;
918
+ const disableRecryption = isTrue(DISABLE_RECRYPTION) || isTrue(_disableRecryption);
919
+ let isVerifiable = false;
920
+ try {
921
+ const [authProtocol, authSignature] = ensureString(customAuth).split(' ');
922
+ isVerifiable = isTrue(apiKey &&
923
+ ephemeralPublicKey &&
924
+ customAuthTimestamp &&
925
+ authSignature &&
926
+ (authProtocol === 'HMAC-SHA256') &&
927
+ (keyPairs === null || keyPairs === void 0 ? void 0 : keyPairs.shared));
928
+ let verificationKeys;
929
+ const rawBody = yield ensureRawBody(req);
930
+ // NOTE: requestBody should be wind up decrypted when isVerifiable (unless disableRecryption, then will pass through)
931
+ let requestBody = safeParse(rawBody);
932
+ // TEMP!!! remove isVerifiable check once web widget moved to react
933
+ if (isVerifiable) {
934
+ if (!apiKey ||
935
+ !ephemeralPublicKey ||
936
+ !customAuthTimestamp ||
937
+ !authSignature ||
938
+ (authProtocol !== 'HMAC-SHA256') ||
939
+ !(keyPairs === null || keyPairs === void 0 ? void 0 : keyPairs.shared) ||
940
+ (apiKey !== keyPairs.shared.publicKey)) {
941
+ throw new VerificationError('Invalid or missing authorization', { code: 401 });
942
+ }
943
+ verificationKeys = yield webCrypto.getVerificationKeys({
944
+ publicKey: ephemeralPublicKey,
945
+ privateKey: keyPairs.shared.privateKey,
946
+ });
947
+ if (!verificationKeys) {
948
+ throw new VerificationError('Invalid or missing verification', { code: 412 });
949
+ }
950
+ const verificationPayload = objectToSortedString(Object.assign({ method: getRequestMethod(rawBody, req.method), timestamp: customAuthTimestamp, body: requestBody }, normalizeURIParts(uri)));
951
+ const isValid = yield webCrypto.verifyHMAC(verificationPayload, verificationKeys.derivedHMACKey, authSignature);
952
+ if (!isValid) {
953
+ throw new VerificationError('Invalid or missing verification', { code: 403 });
954
+ }
955
+ if (!disableRecryption && requestBody) {
956
+ const decryptedMessage = yield webCrypto.decryptMessage(requestBody, verificationKeys.derivedSecretKey);
957
+ requestBody = safeParse(decryptedMessage);
958
+ }
959
+ }
960
+ const processResponse = (response) => __awaiter(void 0, void 0, void 0, function* () {
961
+ if (disableRecryption || !response || !isVerifiable || !(verificationKeys === null || verificationKeys === void 0 ? void 0 : verificationKeys.derivedSecretKey)) {
962
+ return response;
963
+ }
964
+ return webCrypto.encryptMessage(JSON.stringify(response), verificationKeys.derivedSecretKey);
965
+ });
966
+ return { rawBody, requestBody, processResponse };
967
+ }
968
+ catch (error) {
969
+ console.error(`Error handling request verification for '${uri}'`, {
970
+ error,
971
+ isVerifiable,
972
+ disableRecryption,
973
+ });
974
+ throw error;
975
+ }
976
+ });
977
+ };
978
+
979
+ exports.CONTENT_TYPES = CONTENT_TYPES;
980
+ exports.HEADER_AUTHORIZATION_CUSTOM = HEADER_AUTHORIZATION_CUSTOM;
981
+ exports.HEADER_AUTHORIZATION_TIMESTAMP = HEADER_AUTHORIZATION_TIMESTAMP;
982
+ exports.HEADER_CONTENT_TYPE = HEADER_CONTENT_TYPE;
983
+ exports.HEADER_EPHEMERAL_KEY = HEADER_EPHEMERAL_KEY;
984
+ exports.HEADER_PUBLIC_KEY = HEADER_PUBLIC_KEY;
985
+ exports.VerificationError = VerificationError;
986
+ exports.WebCrypto = WebCrypto;
987
+ exports.ensureRawBody = ensureRawBody;
988
+ exports.generateStorableKeyPairs = generateStorableKeyPairs;
989
+ exports.getAuthorizationHelpers = getAuthorizationHelpers;
990
+ exports.getRequestMethod = getRequestMethod;
991
+ exports.getVerificationHelpers = getVerificationHelpers;
992
+ exports.getVerificationKeysData = getVerificationKeysData;
993
+ exports.normalizeURIParts = normalizeURIParts;
994
+ exports.prepareRequestOptions = prepareRequestOptions;
995
+ exports.prepareVerificationRequest = prepareVerificationRequest;
996
+ exports.processVerificationResponse = processVerificationResponse;
997
+
998
+ Object.defineProperty(exports, '__esModule', { value: true });
999
+
1000
+ }));
1001
+ //# sourceMappingURL=index.js.map