@shopfront/bridge 1.14.0 → 1.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.idea/misc.xml ADDED
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="TaskProjectConfiguration">
4
+ <server type="JIRA" url="https://onshopfront.atlassian.net" />
5
+ </component>
6
+ </project>
@@ -7,6 +7,18 @@ import { ShopfrontSaleState } from "./APIs/CurrentSale/ShopfrontSaleState";
7
7
  import { Database } from "./APIs/Database/Database";
8
8
  import { MaybePromise } from "./Utilities/MiscTypes";
9
9
  import { BaseEvent } from "./Events/BaseEvent";
10
+ export interface ShopfrontEmbeddedVerificationToken {
11
+ auth: string;
12
+ id: string;
13
+ app: string;
14
+ user: string;
15
+ url: {
16
+ raw: string;
17
+ loaded: string;
18
+ };
19
+ }
20
+ export declare class ShopfrontTokenDecodingError extends Error {
21
+ }
10
22
  export declare class Application {
11
23
  protected bridge: Bridge;
12
24
  protected isReady: boolean;
@@ -14,6 +26,7 @@ export declare class Application {
14
26
  protected register: string | null;
15
27
  protected outlet: string | null;
16
28
  protected user: string | null;
29
+ protected signingKey: CryptoKeyPair | undefined;
17
30
  protected listeners: {
18
31
  [key in keyof Omit<FromShopfront, "CALLBACK">]: Map<(...args: Array<unknown>) => void, FromShopfront[key] & BaseEvent>;
19
32
  };
@@ -103,4 +116,13 @@ export declare class Application {
103
116
  value: TValueType | undefined;
104
117
  };
105
118
  getOption<TValueType>(option: string, defaultValue: TValueType): Promise<TValueType>;
119
+ protected dataIsEmbeddedToken(data: Record<string, unknown>): data is {
120
+ requestId: string;
121
+ data: BufferSource;
122
+ signature: BufferSource;
123
+ };
124
+ protected generateSigningKey(): Promise<void>;
125
+ protected decodeToken(signature: BufferSource, data: BufferSource, returnTokenObject?: boolean): Promise<ShopfrontEmbeddedVerificationToken | string>;
126
+ getToken(returnTokenObject: true): Promise<ShopfrontEmbeddedVerificationToken>;
127
+ getToken(returnTokenObject?: false): Promise<string>;
106
128
  }
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.Application = void 0;
6
+ exports.Application = exports.ShopfrontTokenDecodingError = void 0;
7
7
  const ApplicationEvents_1 = require("./ApplicationEvents");
8
8
  const Ready_1 = require("./Events/Ready");
9
9
  const Button_1 = require("./Actions/Button");
@@ -24,6 +24,9 @@ const SaleComplete_1 = require("./Events/SaleComplete");
24
24
  const UIPipeline_1 = require("./Events/UIPipeline");
25
25
  const PaymentMethodsEnabled_1 = require("./Events/PaymentMethodsEnabled");
26
26
  const AudioPermissionChange_1 = require("./Events/AudioPermissionChange");
27
+ class ShopfrontTokenDecodingError extends Error {
28
+ }
29
+ exports.ShopfrontTokenDecodingError = ShopfrontTokenDecodingError;
27
30
  // noinspection JSUnusedGlobalSymbols
28
31
  class Application {
29
32
  constructor(bridge) {
@@ -73,7 +76,8 @@ class Application {
73
76
  event === "RESPONSE_DATABASE_REQUEST" ||
74
77
  event === "RESPONSE_LOCATION" ||
75
78
  event === "RESPONSE_OPTION" ||
76
- event === "RESPONSE_AUDIO_REQUEST") {
79
+ event === "RESPONSE_AUDIO_REQUEST" ||
80
+ event === "RESPONSE_SECURE_KEY") {
77
81
  // Handled elsewhere
78
82
  return;
79
83
  }
@@ -469,5 +473,67 @@ class Application {
469
473
  }
470
474
  return optionValue;
471
475
  }
476
+ dataIsEmbeddedToken(data) {
477
+ return typeof data.requestId === "string" && typeof data.data === "object" && data.data !== null;
478
+ }
479
+ async generateSigningKey() {
480
+ if (typeof this.signingKey !== "undefined") {
481
+ return;
482
+ }
483
+ this.signingKey = await crypto.subtle.generateKey({
484
+ name: "ECDSA",
485
+ namedCurve: "P-384",
486
+ }, true, ["sign", "verify"]);
487
+ this.bridge.sendMessage(ApplicationEvents_1.ToShopfront.ROTATE_SIGNING_KEY, await crypto.subtle.exportKey("jwk", this.signingKey.privateKey));
488
+ }
489
+ async decodeToken(signature, data, returnTokenObject) {
490
+ if (typeof this.signingKey === "undefined") {
491
+ // Not possible to decode
492
+ throw new Error("Unable to decode token due to a missing signing key");
493
+ }
494
+ if (!(await crypto.subtle.verify({
495
+ name: "ECDSA",
496
+ hash: {
497
+ name: "SHA-384",
498
+ },
499
+ }, this.signingKey.publicKey, signature, data))) {
500
+ throw new ShopfrontTokenDecodingError();
501
+ }
502
+ const decoded = JSON.parse(new TextDecoder().decode(data));
503
+ if (decoded.app !== this.bridge.key) {
504
+ throw new ShopfrontTokenDecodingError();
505
+ }
506
+ if (decoded.url.loaded !== location.href) {
507
+ throw new ShopfrontTokenDecodingError();
508
+ }
509
+ if (returnTokenObject) {
510
+ return decoded;
511
+ }
512
+ return decoded.auth;
513
+ }
514
+ async getToken(returnTokenObject) {
515
+ await this.generateSigningKey();
516
+ const request = `TokenRequest-${Date.now().toString()}`;
517
+ const promise = new Promise(res => {
518
+ const listener = (event, data) => {
519
+ if (event !== "RESPONSE_SECURE_KEY") {
520
+ return;
521
+ }
522
+ if (!this.dataIsEmbeddedToken(data)) {
523
+ return;
524
+ }
525
+ if (data.requestId !== request) {
526
+ return;
527
+ }
528
+ this.bridge.removeEventListener(listener);
529
+ res([data.signature, data.data]);
530
+ };
531
+ this.bridge.addEventListener(listener);
532
+ });
533
+ this.bridge.sendMessage(ApplicationEvents_1.ToShopfront.REQUEST_SECURE_KEY, {
534
+ requestId: request,
535
+ });
536
+ return this.decodeToken(...(await promise), returnTokenObject);
537
+ }
472
538
  }
473
539
  exports.Application = Application;
@@ -39,6 +39,8 @@ export declare enum ToShopfront {
39
39
  REDIRECT = "REDIRECT",
40
40
  GET_OPTION = "GET_OPTION",
41
41
  RESPONSE_UI_PIPELINE = "RESPONSE_UI_PIPELINE",
42
+ REQUEST_SECURE_KEY = "REQUEST_SECURE_KEY",
43
+ ROTATE_SIGNING_KEY = "ROTATE_SIGNING_KEY",
42
44
  AUDIO_REQUEST_PERMISSION = "AUDIO_REQUEST_PERMISSION",
43
45
  AUDIO_PRELOAD = "AUDIO_PRELOAD",
44
46
  AUDIO_PLAY = "AUDIO_PLAY",
@@ -185,4 +187,5 @@ export interface FromShopfrontInternal {
185
187
  RESPONSE_LOCATION: "RESPONSE_LOCATION";
186
188
  RESPONSE_OPTION: "RESPONSE_OPTION";
187
189
  RESPONSE_AUDIO_REQUEST: "RESPONSE_AUDIO_REQUEST";
190
+ RESPONSE_SECURE_KEY: "RESPONSE_SECURE_KEY";
188
191
  }
@@ -23,6 +23,8 @@ var ToShopfront;
23
23
  ToShopfront["REDIRECT"] = "REDIRECT";
24
24
  ToShopfront["GET_OPTION"] = "GET_OPTION";
25
25
  ToShopfront["RESPONSE_UI_PIPELINE"] = "RESPONSE_UI_PIPELINE";
26
+ ToShopfront["REQUEST_SECURE_KEY"] = "REQUEST_SECURE_KEY";
27
+ ToShopfront["ROTATE_SIGNING_KEY"] = "ROTATE_SIGNING_KEY";
26
28
  // Audio Events
27
29
  ToShopfront["AUDIO_REQUEST_PERMISSION"] = "AUDIO_REQUEST_PERMISSION";
28
30
  ToShopfront["AUDIO_PRELOAD"] = "AUDIO_PRELOAD";
package/lib/Bridge.d.ts CHANGED
@@ -4,13 +4,13 @@ interface ApplicationOptions {
4
4
  id: string;
5
5
  vendor: string;
6
6
  }
7
- interface ApplicationEventListener {
7
+ export interface ApplicationEventListener {
8
8
  (event: keyof ApplicationEvents.FromShopfront | keyof ApplicationEvents.FromShopfrontInternal, data: Record<string, unknown>, id: string): void;
9
9
  }
10
10
  export declare class Bridge {
11
11
  static createApplication(options: ApplicationOptions): Application;
12
- protected key: string;
13
- protected url: URL;
12
+ key: string;
13
+ url: URL;
14
14
  protected listeners: Array<ApplicationEventListener>;
15
15
  protected hasListener: boolean;
16
16
  protected target: Window | null;
package/lib/index.d.ts CHANGED
@@ -8,5 +8,5 @@ export { SellScreenOption } from "./EmitableEvents/SellScreenOption";
8
8
  export { TableUpdate } from "./EmitableEvents/TableUpdate";
9
9
  export { SellScreenPromotionApplicable } from "./EmitableEvents/SellScreenPromotionApplicable";
10
10
  export { CompletedSale } from "./Events/SaleComplete";
11
- export { Application } from "./Application";
11
+ export { Application, ShopfrontEmbeddedVerificationToken, ShopfrontTokenDecodingError } from "./Application";
12
12
  export * as Sales from "./APIs/CurrentSale/index";
package/lib/index.js CHANGED
@@ -23,7 +23,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
23
23
  return result;
24
24
  };
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.Sales = exports.Application = exports.SellScreenPromotionApplicable = exports.TableUpdate = exports.SellScreenOption = exports.SaleKey = exports.Dialog = exports.Toast = exports.Redirect = exports.Button = exports.Bridge = void 0;
26
+ exports.Sales = exports.ShopfrontTokenDecodingError = exports.Application = exports.SellScreenPromotionApplicable = exports.TableUpdate = exports.SellScreenOption = exports.SaleKey = exports.Dialog = exports.Toast = exports.Redirect = exports.Button = exports.Bridge = void 0;
27
27
  var Bridge_1 = require("./Bridge");
28
28
  Object.defineProperty(exports, "Bridge", { enumerable: true, get: function () { return Bridge_1.Bridge; } });
29
29
  var Button_1 = require("./Actions/Button");
@@ -44,4 +44,5 @@ var SellScreenPromotionApplicable_1 = require("./EmitableEvents/SellScreenPromot
44
44
  Object.defineProperty(exports, "SellScreenPromotionApplicable", { enumerable: true, get: function () { return SellScreenPromotionApplicable_1.SellScreenPromotionApplicable; } });
45
45
  var Application_1 = require("./Application");
46
46
  Object.defineProperty(exports, "Application", { enumerable: true, get: function () { return Application_1.Application; } });
47
+ Object.defineProperty(exports, "ShopfrontTokenDecodingError", { enumerable: true, get: function () { return Application_1.ShopfrontTokenDecodingError; } });
47
48
  exports.Sales = __importStar(require("./APIs/CurrentSale/index"));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shopfront/bridge",
3
- "version": "1.14.0",
3
+ "version": "1.16.0",
4
4
  "main": "./lib/index.js",
5
5
  "license": "ISC",
6
6
  "description": "The bridge used to embed your application within Shopfront",