@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 +6 -0
- package/lib/Application.d.ts +22 -0
- package/lib/Application.js +68 -2
- package/lib/ApplicationEvents.d.ts +3 -0
- package/lib/ApplicationEvents.js +2 -0
- package/lib/Bridge.d.ts +3 -3
- package/lib/index.d.ts +1 -1
- package/lib/index.js +2 -1
- package/package.json +1 -1
package/.idea/misc.xml
ADDED
package/lib/Application.d.ts
CHANGED
|
@@ -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
|
}
|
package/lib/Application.js
CHANGED
|
@@ -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
|
}
|
package/lib/ApplicationEvents.js
CHANGED
|
@@ -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
|
-
|
|
13
|
-
|
|
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"));
|