@itwin/core-electron 3.0.0-extension.1 → 3.1.0-dev.6
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 +76 -1
- package/LICENSE.md +1 -1
- package/lib/cjs/ElectronBackend.d.ts +0 -1
- package/lib/cjs/ElectronBackend.d.ts.map +1 -1
- package/lib/cjs/ElectronBackend.js +0 -1
- package/lib/cjs/ElectronBackend.js.map +1 -1
- package/lib/cjs/backend/ElectronHost.d.ts +1 -9
- package/lib/cjs/backend/ElectronHost.d.ts.map +1 -1
- package/lib/cjs/backend/ElectronHost.js +1 -9
- package/lib/cjs/backend/ElectronHost.js.map +1 -1
- package/package.json +15 -16
- package/lib/cjs/backend/ElectronAuthorizationBackend.d.ts +0 -71
- package/lib/cjs/backend/ElectronAuthorizationBackend.d.ts.map +0 -1
- package/lib/cjs/backend/ElectronAuthorizationBackend.js +0 -265
- package/lib/cjs/backend/ElectronAuthorizationBackend.js.map +0 -1
- package/lib/cjs/backend/ElectronAuthorizationEvents.d.ts +0 -20
- package/lib/cjs/backend/ElectronAuthorizationEvents.d.ts.map +0 -1
- package/lib/cjs/backend/ElectronAuthorizationEvents.js +0 -26
- package/lib/cjs/backend/ElectronAuthorizationEvents.js.map +0 -1
- package/lib/cjs/backend/ElectronAuthorizationRequestHandler.d.ts +0 -25
- package/lib/cjs/backend/ElectronAuthorizationRequestHandler.d.ts.map +0 -1
- package/lib/cjs/backend/ElectronAuthorizationRequestHandler.js +0 -63
- package/lib/cjs/backend/ElectronAuthorizationRequestHandler.js.map +0 -1
- package/lib/cjs/backend/ElectronTokenStore.d.ts +0 -18
- package/lib/cjs/backend/ElectronTokenStore.d.ts.map +0 -1
- package/lib/cjs/backend/ElectronTokenStore.js +0 -66
- package/lib/cjs/backend/ElectronTokenStore.js.map +0 -1
- package/lib/cjs/backend/LoopbackWebServer.d.ts +0 -19
- package/lib/cjs/backend/LoopbackWebServer.d.ts.map +0 -1
- package/lib/cjs/backend/LoopbackWebServer.js +0 -101
- package/lib/cjs/backend/LoopbackWebServer.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,81 @@
|
|
|
1
1
|
# Change Log - @itwin/core-electron
|
|
2
2
|
|
|
3
|
-
This log was last generated on
|
|
3
|
+
This log was last generated on Wed, 12 Jan 2022 14:52:38 GMT and should not be manually modified.
|
|
4
|
+
|
|
5
|
+
## 2.19.28
|
|
6
|
+
Wed, 12 Jan 2022 14:52:38 GMT
|
|
7
|
+
|
|
8
|
+
_Version update only_
|
|
9
|
+
|
|
10
|
+
## 2.19.27
|
|
11
|
+
Wed, 05 Jan 2022 20:07:20 GMT
|
|
12
|
+
|
|
13
|
+
_Version update only_
|
|
14
|
+
|
|
15
|
+
## 2.19.26
|
|
16
|
+
Wed, 08 Dec 2021 20:54:52 GMT
|
|
17
|
+
|
|
18
|
+
_Version update only_
|
|
19
|
+
|
|
20
|
+
## 2.19.25
|
|
21
|
+
Fri, 03 Dec 2021 20:05:49 GMT
|
|
22
|
+
|
|
23
|
+
_Version update only_
|
|
24
|
+
|
|
25
|
+
## 2.19.24
|
|
26
|
+
Mon, 29 Nov 2021 18:44:31 GMT
|
|
27
|
+
|
|
28
|
+
_Version update only_
|
|
29
|
+
|
|
30
|
+
## 2.19.23
|
|
31
|
+
Mon, 22 Nov 2021 20:41:39 GMT
|
|
32
|
+
|
|
33
|
+
_Version update only_
|
|
34
|
+
|
|
35
|
+
## 2.19.22
|
|
36
|
+
Wed, 17 Nov 2021 01:23:26 GMT
|
|
37
|
+
|
|
38
|
+
_Version update only_
|
|
39
|
+
|
|
40
|
+
## 2.19.21
|
|
41
|
+
Wed, 10 Nov 2021 10:58:24 GMT
|
|
42
|
+
|
|
43
|
+
_Version update only_
|
|
44
|
+
|
|
45
|
+
## 2.19.20
|
|
46
|
+
Fri, 29 Oct 2021 16:14:22 GMT
|
|
47
|
+
|
|
48
|
+
_Version update only_
|
|
49
|
+
|
|
50
|
+
## 2.19.19
|
|
51
|
+
Mon, 25 Oct 2021 16:16:25 GMT
|
|
52
|
+
|
|
53
|
+
_Version update only_
|
|
54
|
+
|
|
55
|
+
## 2.19.18
|
|
56
|
+
Thu, 21 Oct 2021 20:59:44 GMT
|
|
57
|
+
|
|
58
|
+
_Version update only_
|
|
59
|
+
|
|
60
|
+
## 2.19.17
|
|
61
|
+
Thu, 14 Oct 2021 21:19:43 GMT
|
|
62
|
+
|
|
63
|
+
_Version update only_
|
|
64
|
+
|
|
65
|
+
## 2.19.16
|
|
66
|
+
Mon, 11 Oct 2021 17:37:46 GMT
|
|
67
|
+
|
|
68
|
+
_Version update only_
|
|
69
|
+
|
|
70
|
+
## 2.19.15
|
|
71
|
+
Fri, 08 Oct 2021 16:44:23 GMT
|
|
72
|
+
|
|
73
|
+
_Version update only_
|
|
74
|
+
|
|
75
|
+
## 2.19.14
|
|
76
|
+
Fri, 01 Oct 2021 13:07:03 GMT
|
|
77
|
+
|
|
78
|
+
_Version update only_
|
|
4
79
|
|
|
5
80
|
## 2.19.13
|
|
6
81
|
Tue, 21 Sep 2021 21:06:40 GMT
|
package/LICENSE.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# MIT License
|
|
2
2
|
|
|
3
|
-
Copyright © 2017-
|
|
3
|
+
Copyright © 2017-2022 Bentley Systems, Incorporated. All rights reserved.
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
6
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ElectronBackend.d.ts","sourceRoot":"","sources":["../../src/ElectronBackend.ts"],"names":[],"mappings":"AAKA,cAAc,wBAAwB,CAAC
|
|
1
|
+
{"version":3,"file":"ElectronBackend.d.ts","sourceRoot":"","sources":["../../src/ElectronBackend.ts"],"names":[],"mappings":"AAKA,cAAc,wBAAwB,CAAC"}
|
|
@@ -15,5 +15,4 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./backend/ElectronHost"), exports);
|
|
18
|
-
__exportStar(require("./backend/ElectronAuthorizationBackend"), exports);
|
|
19
18
|
//# sourceMappingURL=ElectronBackend.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ElectronBackend.js","sourceRoot":"","sources":["../../src/ElectronBackend.ts"],"names":[],"mappings":";AAAA;;;+FAG+F;;;;;;;;;;;;AAE/F,yDAAuC
|
|
1
|
+
{"version":3,"file":"ElectronBackend.js","sourceRoot":"","sources":["../../src/ElectronBackend.ts"],"names":[],"mappings":";AAAA;;;+FAG+F;;;;;;;;;;;;AAE/F,yDAAuC","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n* See LICENSE.md in the project root for license terms and full copyright notice.\r\n*--------------------------------------------------------------------------------------------*/\r\n\r\nexport * from \"./backend/ElectronHost\";\r\n"]}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { BrowserWindow, BrowserWindowConstructorOptions } from "electron";
|
|
2
2
|
import { IpcHandler, NativeHostOpts } from "@itwin/core-backend";
|
|
3
|
-
import {
|
|
4
|
-
import { ElectronAuthorizationBackend } from "./ElectronAuthorizationBackend";
|
|
3
|
+
import { RpcConfiguration, RpcInterfaceDefinition } from "@itwin/core-common";
|
|
5
4
|
/**
|
|
6
5
|
* Options for [[ElectronHost.startup]]
|
|
7
6
|
* @beta
|
|
@@ -21,11 +20,6 @@ export interface ElectronHostOptions {
|
|
|
21
20
|
rpcInterfaces?: RpcInterfaceDefinition[];
|
|
22
21
|
/** list of [IpcHandler]($common) classes to register */
|
|
23
22
|
ipcHandlers?: (typeof IpcHandler)[];
|
|
24
|
-
/** if present, [[NativeHost.authorizationClient]] will be set to an instance of NativeAppAuthorizationBackend and will be initialized. */
|
|
25
|
-
authConfig?: NativeAppAuthorizationConfiguration;
|
|
26
|
-
/** if true, do not attempt to initialize AuthorizationClient on startup */
|
|
27
|
-
noInitializeAuthClient?: boolean;
|
|
28
|
-
applicationName?: never;
|
|
29
23
|
}
|
|
30
24
|
/** @beta */
|
|
31
25
|
export interface ElectronHostOpts extends NativeHostOpts {
|
|
@@ -63,8 +57,6 @@ export declare class ElectronHost {
|
|
|
63
57
|
static get ipcMain(): Electron.IpcMain;
|
|
64
58
|
static get app(): Electron.App;
|
|
65
59
|
static get electron(): typeof Electron;
|
|
66
|
-
/** @internal */
|
|
67
|
-
static get authorization(): ElectronAuthorizationBackend;
|
|
68
60
|
private constructor();
|
|
69
61
|
/**
|
|
70
62
|
* Converts an "electron://frontend/" URL to an absolute file path.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ElectronHost.d.ts","sourceRoot":"","sources":["../../../src/backend/ElectronHost.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ElectronHost.d.ts","sourceRoot":"","sources":["../../../src/backend/ElectronHost.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,aAAa,EAAE,+BAA+B,EAAE,MAAM,UAAU,CAAC;AAI1E,OAAO,EAAE,UAAU,EAAuB,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACtF,OAAO,EAA8D,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAwB1I;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,sCAAsC;IACtC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,oEAAoE;IACpE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qCAAqC;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2IAA2I;IAC3I,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,0DAA0D;IAC1D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,oDAAoD;IACpD,aAAa,CAAC,EAAE,sBAAsB,EAAE,CAAC;IACzC,wDAAwD;IACxD,WAAW,CAAC,EAAE,CAAC,OAAO,UAAU,CAAC,EAAE,CAAC;CACrC;AAED,YAAY;AACZ,MAAM,WAAW,gBAAiB,SAAQ,cAAc;IACtD,YAAY,CAAC,EAAE,mBAAmB,CAAC;CACpC;AAED,YAAY;AACZ,MAAM,WAAW,yBAA0B,SAAQ,+BAA+B;IAChF,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,2DAA2D;IAC3D,aAAa,CAAC,EAAE,CAAC,SAAS,GAAG,QAAQ,GAAG,aAAa,GAAG,sBAAsB,CAAC,CAAC;CACjF;AAED;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACzC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED;;;GAGG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAC,IAAI,CAAc;IACjC,OAAO,CAAC,MAAM,CAAC,kBAAkB,CAAU;IAC3C,OAAO,CAAC,MAAM,CAAC,SAAS,CAAkB;IAC1C,OAAO,CAAC,MAAM,CAAC,iBAAiB,CAA0B;IAC1D,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC,CAAgB;IAC3C,OAAc,gBAAgB,EAAE,MAAM,CAAC;IACvC,OAAc,WAAW,EAAE,MAAM,CAAC;IAClC,OAAc,WAAW,EAAE,MAAM,CAAC;IAClC,OAAc,SAAS,EAAE,gBAAgB,CAAC;IAC1C,WAAkB,OAAO,qBAAqC;IAC9D,WAAkB,GAAG,iBAAiC;IACtD,WAAkB,QAAQ,oBAA6B;IAEvD,OAAO;IAEP;;;;;;OAMG;IACH,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAmB/B,OAAO,CAAC,MAAM,CAAC,WAAW;IAqD1B,qDAAqD;IACrD,WAAkB,UAAU,8BAA+B;IAE3D,0FAA0F;WAC5E,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,0BAA0B,GAAG,SAAS;IAK9F,wFAAwF;WAC1E,yBAAyB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS;IAIhF;;;OAGG;WACiB,cAAc,CAAC,aAAa,CAAC,EAAE,yBAAyB,GAAG,OAAO,CAAC,IAAI,CAAC;IAsC5F,WAAkB,OAAO,YAAsC;IAE/D;;;;;;OAMG;WACiB,OAAO,CAAC,IAAI,CAAC,EAAE,gBAAgB;CA4BpD"}
|
|
@@ -11,7 +11,6 @@ const core_bentley_1 = require("@itwin/core-bentley");
|
|
|
11
11
|
const core_backend_1 = require("@itwin/core-backend");
|
|
12
12
|
const core_common_1 = require("@itwin/core-common");
|
|
13
13
|
const ElectronRpcManager_1 = require("../common/ElectronRpcManager");
|
|
14
|
-
const ElectronAuthorizationBackend_1 = require("./ElectronAuthorizationBackend");
|
|
15
14
|
// cSpell:ignore signin devserver webcontents copyfile unmaximize eopt
|
|
16
15
|
class ElectronIpc {
|
|
17
16
|
addListener(channel, listener) {
|
|
@@ -41,8 +40,6 @@ class ElectronHost {
|
|
|
41
40
|
static get ipcMain() { return this._electron.ipcMain; }
|
|
42
41
|
static get app() { return this._electron.app; }
|
|
43
42
|
static get electron() { return this._electron; }
|
|
44
|
-
/** @internal */
|
|
45
|
-
static get authorization() { return core_backend_1.IModelHost.authorizationClient; }
|
|
46
43
|
/**
|
|
47
44
|
* Converts an "electron://frontend/" URL to an absolute file path.
|
|
48
45
|
*
|
|
@@ -173,7 +170,7 @@ class ElectronHost {
|
|
|
173
170
|
* @note This method must only be called from the backend of an Electron app (i.e. when [ProcessDetector.isElectronAppBackend]($bentley) is `true`).
|
|
174
171
|
*/
|
|
175
172
|
static async startup(opts) {
|
|
176
|
-
var _a, _b, _c, _d, _f, _g, _h, _j
|
|
173
|
+
var _a, _b, _c, _d, _f, _g, _h, _j;
|
|
177
174
|
if (!core_bentley_1.ProcessDetector.isElectronAppBackend)
|
|
178
175
|
throw new Error("Not running under Electron");
|
|
179
176
|
if (!this.isValid) {
|
|
@@ -198,11 +195,6 @@ class ElectronHost {
|
|
|
198
195
|
ElectronAppHandler.register();
|
|
199
196
|
(_j = (_h = opts.electronHost) === null || _h === void 0 ? void 0 : _h.ipcHandlers) === null || _j === void 0 ? void 0 : _j.forEach((ipc) => ipc.register());
|
|
200
197
|
}
|
|
201
|
-
const authorizationBackend = new ElectronAuthorizationBackend_1.ElectronAuthorizationBackend((_k = opts.electronHost) === null || _k === void 0 ? void 0 : _k.authConfig);
|
|
202
|
-
const connectivityStatus = core_backend_1.NativeHost.checkInternetConnectivity();
|
|
203
|
-
if (((_l = opts.electronHost) === null || _l === void 0 ? void 0 : _l.authConfig) && true !== ((_m = opts.electronHost) === null || _m === void 0 ? void 0 : _m.noInitializeAuthClient) && connectivityStatus === core_common_1.InternetConnectivityStatus.Online)
|
|
204
|
-
await authorizationBackend.initialize((_o = opts.electronHost) === null || _o === void 0 ? void 0 : _o.authConfig);
|
|
205
|
-
core_backend_1.IModelHost.authorizationClient = authorizationBackend;
|
|
206
198
|
}
|
|
207
199
|
}
|
|
208
200
|
exports.ElectronHost = ElectronHost;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ElectronHost.js","sourceRoot":"","sources":["../../../src/backend/ElectronHost.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n* See LICENSE.md in the project root for license terms and full copyright notice.\r\n*--------------------------------------------------------------------------------------------*/\r\n\r\n// Note: only import types! Does not create a `require(\"electron\")` in JavaScript after transpiling. That's important so this file can\r\n// be imported by apps that sometimes use Electron and sometimes not. Call to `ElectronBackend.initialize`\r\n// will do the necessary `require(\"electron\")`\r\n// IMPORTANT: Do not call or construct any of these imports. Otherwise, a require(\"electron\") call will be emitted at top level.\r\n// Instead, use `ElectronHost.electron.<type>`\r\nimport { BrowserWindow, BrowserWindowConstructorOptions } from \"electron\";\r\nimport * as fs from \"fs\";\r\nimport * as path from \"path\";\r\nimport { BeDuration, IModelStatus, ProcessDetector } from \"@itwin/core-bentley\";\r\nimport { IModelHost, IpcHandler, IpcHost, NativeHost, NativeHostOpts } from \"@itwin/core-backend\";\r\nimport { IModelError, InternetConnectivityStatus, IpcListener, IpcSocketBackend, NativeAppAuthorizationConfiguration, RemoveFunction, RpcConfiguration, RpcInterfaceDefinition } from \"@itwin/core-common\";\r\nimport { ElectronRpcConfiguration, ElectronRpcManager } from \"../common/ElectronRpcManager\";\r\nimport { ElectronAuthorizationBackend } from \"./ElectronAuthorizationBackend\";\r\n\r\n// cSpell:ignore signin devserver webcontents copyfile unmaximize eopt\r\n\r\nclass ElectronIpc implements IpcSocketBackend {\r\n public addListener(channel: string, listener: IpcListener): RemoveFunction {\r\n ElectronHost.ipcMain.addListener(channel, listener);\r\n return () => ElectronHost.ipcMain.removeListener(channel, listener);\r\n }\r\n public removeListener(channel: string, listener: IpcListener) {\r\n ElectronHost.ipcMain.removeListener(channel, listener);\r\n }\r\n public send(channel: string, ...args: any[]): void {\r\n const window = ElectronHost.mainWindow ?? ElectronHost.electron.BrowserWindow.getAllWindows()[0];\r\n window?.webContents.send(channel, ...args);\r\n }\r\n public handle(channel: string, listener: (evt: any, ...args: any[]) => Promise<any>): RemoveFunction {\r\n ElectronHost.ipcMain.removeHandler(channel); // make sure there's not already a handler registered\r\n ElectronHost.ipcMain.handle(channel, listener);\r\n return () => ElectronHost.ipcMain.removeHandler(channel);\r\n }\r\n}\r\n\r\n/**\r\n * Options for [[ElectronHost.startup]]\r\n * @beta\r\n */\r\nexport interface ElectronHostOptions {\r\n /** the path to find web resources */\r\n webResourcesPath?: string;\r\n /** filename for the app's icon, relative to [[webResourcesPath]] */\r\n iconName?: string;\r\n /** name of frontend url to open. */\r\n frontendURL?: string;\r\n /** use a development server rather than the \"electron\" protocol for loading frontend (see https://www.electronjs.org/docs/api/protocol) */\r\n developmentServer?: boolean;\r\n /** port number for development server. Default is 3000 */\r\n frontendPort?: number;\r\n /** list of RPC interface definitions to register */\r\n rpcInterfaces?: RpcInterfaceDefinition[];\r\n /** list of [IpcHandler]($common) classes to register */\r\n ipcHandlers?: (typeof IpcHandler)[];\r\n /** if present, [[NativeHost.authorizationClient]] will be set to an instance of NativeAppAuthorizationBackend and will be initialized. */\r\n authConfig?: NativeAppAuthorizationConfiguration;\r\n /** if true, do not attempt to initialize AuthorizationClient on startup */\r\n noInitializeAuthClient?: boolean;\r\n applicationName?: never; // this should be supplied in NativeHostOpts\r\n}\r\n\r\n/** @beta */\r\nexport interface ElectronHostOpts extends NativeHostOpts {\r\n electronHost?: ElectronHostOptions;\r\n}\r\n\r\n/** @beta */\r\nexport interface ElectronHostWindowOptions extends BrowserWindowConstructorOptions {\r\n storeWindowName?: string;\r\n /** The style of window title bar. Default is `default`. */\r\n titleBarStyle?: (\"default\" | \"hidden\" | \"hiddenInset\" | \"customButtonsOnHover\");\r\n}\r\n\r\n/** the size and position of a window as stored in the settings file.\r\n * @beta\r\n */\r\nexport interface WindowSizeAndPositionProps {\r\n width: number;\r\n height: number;\r\n x: number;\r\n y: number;\r\n}\r\n\r\n/**\r\n * The backend for Electron-based desktop applications\r\n * @beta\r\n */\r\nexport class ElectronHost {\r\n private static _ipc: ElectronIpc;\r\n private static _developmentServer: boolean;\r\n private static _electron: typeof Electron;\r\n private static _electronFrontend = \"electron://frontend/\";\r\n private static _mainWindow?: BrowserWindow;\r\n public static webResourcesPath: string;\r\n public static appIconPath: string;\r\n public static frontendURL: string;\r\n public static rpcConfig: RpcConfiguration;\r\n public static get ipcMain() { return this._electron.ipcMain; }\r\n public static get app() { return this._electron.app; }\r\n public static get electron() { return this._electron; }\r\n\r\n /** @internal */\r\n public static get authorization() { return IModelHost.authorizationClient as ElectronAuthorizationBackend; }\r\n\r\n private constructor() { }\r\n\r\n /**\r\n * Converts an \"electron://frontend/\" URL to an absolute file path.\r\n *\r\n * We use this protocol in production builds because our frontend must be built with absolute URLs,\r\n * however, since we're loading everything directly from the install directory, we cannot know the\r\n * absolute path at build time.\r\n */\r\n private static parseElectronUrl(requestedUrl: string): string {\r\n // Note that the \"frontend/\" path is arbitrary - this is just so we can handle *some* relative URLs...\r\n let assetPath = requestedUrl.substr(this._electronFrontend.length);\r\n if (assetPath.length === 0)\r\n assetPath = \"index.html\";\r\n assetPath = path.normalize(`${this.webResourcesPath}/${assetPath}`);\r\n // File protocols don't follow symlinks, so we need to resolve this to a real path.\r\n // However, if the file doesn't exist, it's fine to return an invalid path here - the request will just fail with net::ERR_FILE_NOT_FOUND\r\n try {\r\n assetPath = fs.realpathSync(assetPath);\r\n } catch (error) {\r\n // eslint-disable-next-line no-console\r\n // console.warn(`WARNING: Frontend requested \"${requestedUrl}\", but ${assetPath} does not exist`);\r\n }\r\n if (!assetPath.startsWith(this.webResourcesPath))\r\n throw new Error(`Access to files outside installation directory (${this.webResourcesPath}) is prohibited`);\r\n return assetPath;\r\n }\r\n\r\n private static _openWindow(options?: ElectronHostWindowOptions) {\r\n const opts: BrowserWindowConstructorOptions = {\r\n ...options,\r\n autoHideMenuBar: true,\r\n icon: this.appIconPath,\r\n webPreferences: {\r\n ...options?.webPreferences,\r\n\r\n // These web preference variables should not be overriden by the ElectronHostWindowOptions\r\n preload: require.resolve(/* webpack: copyfile */\"./ElectronPreload.js\"),\r\n experimentalFeatures: false,\r\n nodeIntegration: false,\r\n contextIsolation: true,\r\n sandbox: true,\r\n nativeWindowOpen: true,\r\n nodeIntegrationInWorker: false,\r\n nodeIntegrationInSubFrames: false,\r\n },\r\n };\r\n\r\n this._mainWindow = new (this.electron.BrowserWindow)(opts);\r\n ElectronRpcConfiguration.targetWindowId = this._mainWindow.id;\r\n this._mainWindow.on(\"closed\", () => this._mainWindow = undefined);\r\n this._mainWindow.loadURL(this.frontendURL); // eslint-disable-line @typescript-eslint/no-floating-promises\r\n\r\n /** Monitors and saves main window size, position and maximized state */\r\n if (options?.storeWindowName) {\r\n const mainWindow = this._mainWindow;\r\n const name = options.storeWindowName;\r\n const saveWindowPosition = () => {\r\n const resolution = mainWindow.getSize();\r\n const position = mainWindow.getPosition();\r\n const pos: WindowSizeAndPositionProps = {\r\n width: resolution[0],\r\n height: resolution[1],\r\n x: position[0],\r\n y: position[1],\r\n };\r\n NativeHost.settingsStore.setData(`windowPos-${name}`, JSON.stringify(pos));\r\n };\r\n const saveMaximized = (maximized: boolean) => {\r\n if (!maximized)\r\n saveWindowPosition();\r\n NativeHost.settingsStore.setData(`windowMaximized-${name}`, maximized);\r\n };\r\n\r\n mainWindow.on(\"resized\", () => saveWindowPosition());\r\n mainWindow.on(\"moved\", () => saveWindowPosition());\r\n mainWindow.on(\"maximize\", () => saveMaximized(true));\r\n mainWindow.on(\"unmaximize\", () => saveMaximized(false));\r\n }\r\n }\r\n\r\n /** The \"main\" BrowserWindow for this application. */\r\n public static get mainWindow() { return this._mainWindow; }\r\n\r\n /** Gets window size and position for a window, by name, from settings file, if present */\r\n public static getWindowSizeSetting(windowName: string): WindowSizeAndPositionProps | undefined {\r\n const saved = NativeHost.settingsStore.getString(`windowPos-${windowName}`);\r\n return saved ? JSON.parse(saved) as WindowSizeAndPositionProps : undefined;\r\n }\r\n\r\n /** Gets \"window maximized\" flag for a window, by name, from settings file if present */\r\n public static getWindowMaximizedSetting(windowName: string): boolean | undefined {\r\n return NativeHost.settingsStore.getBoolean(`windowMaximized-${windowName}`);\r\n }\r\n\r\n /**\r\n * Open the main Window when the app is ready.\r\n * @param windowOptions Options for constructing the main BrowserWindow. See: https://electronjs.org/docs/api/browser-window#new-browserwindowoptions\r\n */\r\n public static async openMainWindow(windowOptions?: ElectronHostWindowOptions): Promise<void> {\r\n const app = this.app;\r\n // quit the application when all windows are closed (unless we're running on MacOS)\r\n app.on(\"window-all-closed\", () => {\r\n if (process.platform !== \"darwin\")\r\n app.quit();\r\n });\r\n\r\n // re-open the main window if it was closed and the app is re-activated (this is the normal MacOS behavior)\r\n app.on(\"activate\", () => {\r\n if (!this._mainWindow)\r\n this._openWindow(windowOptions);\r\n });\r\n\r\n if (this._developmentServer) {\r\n // Occasionally, the electron backend may start before the webpack devserver has even started.\r\n // If this happens, we'll just retry and keep reloading the page.\r\n app.on(\"web-contents-created\", (_e, webcontents) => {\r\n webcontents.on(\"did-fail-load\", async (_event, errorCode, _errorDescription, _validatedURL, isMainFrame) => {\r\n // errorCode -102 is CONNECTION_REFUSED - see https://cs.chromium.org/chromium/src/net/base/net_error_list.h\r\n if (isMainFrame && errorCode === -102) {\r\n await BeDuration.wait(100);\r\n webcontents.reload();\r\n }\r\n });\r\n });\r\n }\r\n\r\n await app.whenReady();\r\n\r\n if (!this._developmentServer) {\r\n // handle any \"electron://\" requests and redirect them to \"file://\" URLs\r\n this.electron.protocol.registerFileProtocol(\"electron\", (request, callback) => callback(this.parseElectronUrl(request.url))); // eslint-disable-line @typescript-eslint/no-var-requires\r\n }\r\n\r\n this._openWindow(windowOptions);\r\n }\r\n\r\n public static get isValid() { return this._ipc !== undefined; }\r\n\r\n /**\r\n * Initialize the backend of an Electron app.\r\n * This method configures the backend for all of the inter-process communication (RPC and IPC) for an\r\n * Electron app. It should be called from your Electron main function.\r\n * @param opts Options that control aspects of your backend.\r\n * @note This method must only be called from the backend of an Electron app (i.e. when [ProcessDetector.isElectronAppBackend]($bentley) is `true`).\r\n */\r\n public static async startup(opts?: ElectronHostOpts) {\r\n if (!ProcessDetector.isElectronAppBackend)\r\n throw new Error(\"Not running under Electron\");\r\n\r\n if (!this.isValid) {\r\n this._electron = require(\"electron\");\r\n this._ipc = new ElectronIpc();\r\n const app = this.app;\r\n if (!app.isReady())\r\n this.electron.protocol.registerSchemesAsPrivileged([{ scheme: \"electron\", privileges: { standard: true, secure: true } }]);\r\n const eopt = opts?.electronHost;\r\n this._developmentServer = eopt?.developmentServer ?? false;\r\n const frontendPort = eopt?.frontendPort ?? 3000;\r\n this.webResourcesPath = eopt?.webResourcesPath ?? \"\";\r\n this.frontendURL = eopt?.frontendURL ?? (this._developmentServer ? `http://localhost:${frontendPort}` : `${this._electronFrontend}index.html`);\r\n this.appIconPath = path.join(this.webResourcesPath, eopt?.iconName ?? \"appicon.ico\");\r\n this.rpcConfig = ElectronRpcManager.initializeBackend(this._ipc, eopt?.rpcInterfaces);\r\n }\r\n\r\n opts = opts ?? {};\r\n opts.ipcHost = opts.ipcHost ?? {};\r\n opts.ipcHost.socket = this._ipc;\r\n await NativeHost.startup(opts);\r\n if (IpcHost.isValid) {\r\n ElectronAppHandler.register();\r\n opts.electronHost?.ipcHandlers?.forEach((ipc) => ipc.register());\r\n }\r\n\r\n const authorizationBackend = new ElectronAuthorizationBackend(opts.electronHost?.authConfig);\r\n const connectivityStatus = NativeHost.checkInternetConnectivity();\r\n if (opts.electronHost?.authConfig && true !== opts.electronHost?.noInitializeAuthClient && connectivityStatus === InternetConnectivityStatus.Online)\r\n await authorizationBackend.initialize(opts.electronHost?.authConfig);\r\n\r\n IModelHost.authorizationClient = authorizationBackend;\r\n }\r\n}\r\n\r\nclass ElectronAppHandler extends IpcHandler {\r\n public get channelName() { return \"electron-safe\"; }\r\n public async callElectron(member: string, method: string, ...args: any) {\r\n const electronMember = (ElectronHost.electron as any)[member];\r\n const func = electronMember[method];\r\n if (typeof func !== \"function\")\r\n throw new IModelError(IModelStatus.FunctionNotFound, `Method ${method} not found electron.${member}`);\r\n\r\n return func.call(electronMember, ...args);\r\n }\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"file":"ElectronHost.js","sourceRoot":"","sources":["../../../src/backend/ElectronHost.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n* See LICENSE.md in the project root for license terms and full copyright notice.\r\n*--------------------------------------------------------------------------------------------*/\r\n\r\n// Note: only import types! Does not create a `require(\"electron\")` in JavaScript after transpiling. That's important so this file can\r\n// be imported by apps that sometimes use Electron and sometimes not. Call to `ElectronBackend.initialize`\r\n// will do the necessary `require(\"electron\")`\r\n// IMPORTANT: Do not call or construct any of these imports. Otherwise, a require(\"electron\") call will be emitted at top level.\r\n// Instead, use `ElectronHost.electron.<type>`\r\n\r\nimport { BrowserWindow, BrowserWindowConstructorOptions } from \"electron\";\r\nimport * as fs from \"fs\";\r\nimport * as path from \"path\";\r\nimport { BeDuration, IModelStatus, ProcessDetector } from \"@itwin/core-bentley\";\r\nimport { IpcHandler, IpcHost, NativeHost, NativeHostOpts } from \"@itwin/core-backend\";\r\nimport { IModelError, IpcListener, IpcSocketBackend, RemoveFunction, RpcConfiguration, RpcInterfaceDefinition } from \"@itwin/core-common\";\r\nimport { ElectronRpcConfiguration, ElectronRpcManager } from \"../common/ElectronRpcManager\";\r\n\r\n// cSpell:ignore signin devserver webcontents copyfile unmaximize eopt\r\n\r\nclass ElectronIpc implements IpcSocketBackend {\r\n public addListener(channel: string, listener: IpcListener): RemoveFunction {\r\n ElectronHost.ipcMain.addListener(channel, listener);\r\n return () => ElectronHost.ipcMain.removeListener(channel, listener);\r\n }\r\n public removeListener(channel: string, listener: IpcListener) {\r\n ElectronHost.ipcMain.removeListener(channel, listener);\r\n }\r\n public send(channel: string, ...args: any[]): void {\r\n const window = ElectronHost.mainWindow ?? ElectronHost.electron.BrowserWindow.getAllWindows()[0];\r\n window?.webContents.send(channel, ...args);\r\n }\r\n public handle(channel: string, listener: (evt: any, ...args: any[]) => Promise<any>): RemoveFunction {\r\n ElectronHost.ipcMain.removeHandler(channel); // make sure there's not already a handler registered\r\n ElectronHost.ipcMain.handle(channel, listener);\r\n return () => ElectronHost.ipcMain.removeHandler(channel);\r\n }\r\n}\r\n\r\n/**\r\n * Options for [[ElectronHost.startup]]\r\n * @beta\r\n */\r\nexport interface ElectronHostOptions {\r\n /** the path to find web resources */\r\n webResourcesPath?: string;\r\n /** filename for the app's icon, relative to [[webResourcesPath]] */\r\n iconName?: string;\r\n /** name of frontend url to open. */\r\n frontendURL?: string;\r\n /** use a development server rather than the \"electron\" protocol for loading frontend (see https://www.electronjs.org/docs/api/protocol) */\r\n developmentServer?: boolean;\r\n /** port number for development server. Default is 3000 */\r\n frontendPort?: number;\r\n /** list of RPC interface definitions to register */\r\n rpcInterfaces?: RpcInterfaceDefinition[];\r\n /** list of [IpcHandler]($common) classes to register */\r\n ipcHandlers?: (typeof IpcHandler)[];\r\n}\r\n\r\n/** @beta */\r\nexport interface ElectronHostOpts extends NativeHostOpts {\r\n electronHost?: ElectronHostOptions;\r\n}\r\n\r\n/** @beta */\r\nexport interface ElectronHostWindowOptions extends BrowserWindowConstructorOptions {\r\n storeWindowName?: string;\r\n /** The style of window title bar. Default is `default`. */\r\n titleBarStyle?: (\"default\" | \"hidden\" | \"hiddenInset\" | \"customButtonsOnHover\");\r\n}\r\n\r\n/** the size and position of a window as stored in the settings file.\r\n * @beta\r\n */\r\nexport interface WindowSizeAndPositionProps {\r\n width: number;\r\n height: number;\r\n x: number;\r\n y: number;\r\n}\r\n\r\n/**\r\n * The backend for Electron-based desktop applications\r\n * @beta\r\n */\r\nexport class ElectronHost {\r\n private static _ipc: ElectronIpc;\r\n private static _developmentServer: boolean;\r\n private static _electron: typeof Electron;\r\n private static _electronFrontend = \"electron://frontend/\";\r\n private static _mainWindow?: BrowserWindow;\r\n public static webResourcesPath: string;\r\n public static appIconPath: string;\r\n public static frontendURL: string;\r\n public static rpcConfig: RpcConfiguration;\r\n public static get ipcMain() { return this._electron.ipcMain; }\r\n public static get app() { return this._electron.app; }\r\n public static get electron() { return this._electron; }\r\n\r\n private constructor() { }\r\n\r\n /**\r\n * Converts an \"electron://frontend/\" URL to an absolute file path.\r\n *\r\n * We use this protocol in production builds because our frontend must be built with absolute URLs,\r\n * however, since we're loading everything directly from the install directory, we cannot know the\r\n * absolute path at build time.\r\n */\r\n private static parseElectronUrl(requestedUrl: string): string {\r\n // Note that the \"frontend/\" path is arbitrary - this is just so we can handle *some* relative URLs...\r\n let assetPath = requestedUrl.substr(this._electronFrontend.length);\r\n if (assetPath.length === 0)\r\n assetPath = \"index.html\";\r\n assetPath = path.normalize(`${this.webResourcesPath}/${assetPath}`);\r\n // File protocols don't follow symlinks, so we need to resolve this to a real path.\r\n // However, if the file doesn't exist, it's fine to return an invalid path here - the request will just fail with net::ERR_FILE_NOT_FOUND\r\n try {\r\n assetPath = fs.realpathSync(assetPath);\r\n } catch (error) {\r\n // eslint-disable-next-line no-console\r\n // console.warn(`WARNING: Frontend requested \"${requestedUrl}\", but ${assetPath} does not exist`);\r\n }\r\n if (!assetPath.startsWith(this.webResourcesPath))\r\n throw new Error(`Access to files outside installation directory (${this.webResourcesPath}) is prohibited`);\r\n return assetPath;\r\n }\r\n\r\n private static _openWindow(options?: ElectronHostWindowOptions) {\r\n const opts: BrowserWindowConstructorOptions = {\r\n ...options,\r\n autoHideMenuBar: true,\r\n icon: this.appIconPath,\r\n webPreferences: {\r\n ...options?.webPreferences,\r\n\r\n // These web preference variables should not be overriden by the ElectronHostWindowOptions\r\n preload: require.resolve(/* webpack: copyfile */\"./ElectronPreload.js\"),\r\n experimentalFeatures: false,\r\n nodeIntegration: false,\r\n contextIsolation: true,\r\n sandbox: true,\r\n nativeWindowOpen: true,\r\n nodeIntegrationInWorker: false,\r\n nodeIntegrationInSubFrames: false,\r\n },\r\n };\r\n\r\n this._mainWindow = new (this.electron.BrowserWindow)(opts);\r\n ElectronRpcConfiguration.targetWindowId = this._mainWindow.id;\r\n this._mainWindow.on(\"closed\", () => this._mainWindow = undefined);\r\n this._mainWindow.loadURL(this.frontendURL); // eslint-disable-line @typescript-eslint/no-floating-promises\r\n\r\n /** Monitors and saves main window size, position and maximized state */\r\n if (options?.storeWindowName) {\r\n const mainWindow = this._mainWindow;\r\n const name = options.storeWindowName;\r\n const saveWindowPosition = () => {\r\n const resolution = mainWindow.getSize();\r\n const position = mainWindow.getPosition();\r\n const pos: WindowSizeAndPositionProps = {\r\n width: resolution[0],\r\n height: resolution[1],\r\n x: position[0],\r\n y: position[1],\r\n };\r\n NativeHost.settingsStore.setData(`windowPos-${name}`, JSON.stringify(pos));\r\n };\r\n const saveMaximized = (maximized: boolean) => {\r\n if (!maximized)\r\n saveWindowPosition();\r\n NativeHost.settingsStore.setData(`windowMaximized-${name}`, maximized);\r\n };\r\n\r\n mainWindow.on(\"resized\", () => saveWindowPosition());\r\n mainWindow.on(\"moved\", () => saveWindowPosition());\r\n mainWindow.on(\"maximize\", () => saveMaximized(true));\r\n mainWindow.on(\"unmaximize\", () => saveMaximized(false));\r\n }\r\n }\r\n\r\n /** The \"main\" BrowserWindow for this application. */\r\n public static get mainWindow() { return this._mainWindow; }\r\n\r\n /** Gets window size and position for a window, by name, from settings file, if present */\r\n public static getWindowSizeSetting(windowName: string): WindowSizeAndPositionProps | undefined {\r\n const saved = NativeHost.settingsStore.getString(`windowPos-${windowName}`);\r\n return saved ? JSON.parse(saved) as WindowSizeAndPositionProps : undefined;\r\n }\r\n\r\n /** Gets \"window maximized\" flag for a window, by name, from settings file if present */\r\n public static getWindowMaximizedSetting(windowName: string): boolean | undefined {\r\n return NativeHost.settingsStore.getBoolean(`windowMaximized-${windowName}`);\r\n }\r\n\r\n /**\r\n * Open the main Window when the app is ready.\r\n * @param windowOptions Options for constructing the main BrowserWindow. See: https://electronjs.org/docs/api/browser-window#new-browserwindowoptions\r\n */\r\n public static async openMainWindow(windowOptions?: ElectronHostWindowOptions): Promise<void> {\r\n const app = this.app;\r\n // quit the application when all windows are closed (unless we're running on MacOS)\r\n app.on(\"window-all-closed\", () => {\r\n if (process.platform !== \"darwin\")\r\n app.quit();\r\n });\r\n\r\n // re-open the main window if it was closed and the app is re-activated (this is the normal MacOS behavior)\r\n app.on(\"activate\", () => {\r\n if (!this._mainWindow)\r\n this._openWindow(windowOptions);\r\n });\r\n\r\n if (this._developmentServer) {\r\n // Occasionally, the electron backend may start before the webpack devserver has even started.\r\n // If this happens, we'll just retry and keep reloading the page.\r\n app.on(\"web-contents-created\", (_e, webcontents) => {\r\n webcontents.on(\"did-fail-load\", async (_event, errorCode, _errorDescription, _validatedURL, isMainFrame) => {\r\n // errorCode -102 is CONNECTION_REFUSED - see https://cs.chromium.org/chromium/src/net/base/net_error_list.h\r\n if (isMainFrame && errorCode === -102) {\r\n await BeDuration.wait(100);\r\n webcontents.reload();\r\n }\r\n });\r\n });\r\n }\r\n\r\n await app.whenReady();\r\n\r\n if (!this._developmentServer) {\r\n // handle any \"electron://\" requests and redirect them to \"file://\" URLs\r\n this.electron.protocol.registerFileProtocol(\"electron\", (request, callback) => callback(this.parseElectronUrl(request.url))); // eslint-disable-line @typescript-eslint/no-var-requires\r\n }\r\n\r\n this._openWindow(windowOptions);\r\n }\r\n\r\n public static get isValid() { return this._ipc !== undefined; }\r\n\r\n /**\r\n * Initialize the backend of an Electron app.\r\n * This method configures the backend for all of the inter-process communication (RPC and IPC) for an\r\n * Electron app. It should be called from your Electron main function.\r\n * @param opts Options that control aspects of your backend.\r\n * @note This method must only be called from the backend of an Electron app (i.e. when [ProcessDetector.isElectronAppBackend]($bentley) is `true`).\r\n */\r\n public static async startup(opts?: ElectronHostOpts) {\r\n if (!ProcessDetector.isElectronAppBackend)\r\n throw new Error(\"Not running under Electron\");\r\n\r\n if (!this.isValid) {\r\n this._electron = require(\"electron\");\r\n this._ipc = new ElectronIpc();\r\n const app = this.app;\r\n if (!app.isReady())\r\n this.electron.protocol.registerSchemesAsPrivileged([{ scheme: \"electron\", privileges: { standard: true, secure: true } }]);\r\n const eopt = opts?.electronHost;\r\n this._developmentServer = eopt?.developmentServer ?? false;\r\n const frontendPort = eopt?.frontendPort ?? 3000;\r\n this.webResourcesPath = eopt?.webResourcesPath ?? \"\";\r\n this.frontendURL = eopt?.frontendURL ?? (this._developmentServer ? `http://localhost:${frontendPort}` : `${this._electronFrontend}index.html`);\r\n this.appIconPath = path.join(this.webResourcesPath, eopt?.iconName ?? \"appicon.ico\");\r\n this.rpcConfig = ElectronRpcManager.initializeBackend(this._ipc, eopt?.rpcInterfaces);\r\n }\r\n\r\n opts = opts ?? {};\r\n opts.ipcHost = opts.ipcHost ?? {};\r\n opts.ipcHost.socket = this._ipc;\r\n await NativeHost.startup(opts);\r\n if (IpcHost.isValid) {\r\n ElectronAppHandler.register();\r\n opts.electronHost?.ipcHandlers?.forEach((ipc) => ipc.register());\r\n }\r\n }\r\n}\r\n\r\nclass ElectronAppHandler extends IpcHandler {\r\n public get channelName() { return \"electron-safe\"; }\r\n public async callElectron(member: string, method: string, ...args: any) {\r\n const electronMember = (ElectronHost.electron as any)[member];\r\n const func = electronMember[method];\r\n if (typeof func !== \"function\")\r\n throw new IModelError(IModelStatus.FunctionNotFound, `Method ${method} not found electron.${member}`);\r\n\r\n return func.call(electronMember, ...args);\r\n }\r\n}\r\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@itwin/core-electron",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.0-dev.6",
|
|
4
4
|
"description": "iTwin.js ElectronHost and ElectronApp",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"engines": {
|
|
@@ -20,23 +20,21 @@
|
|
|
20
20
|
"url": "http://www.bentley.com"
|
|
21
21
|
},
|
|
22
22
|
"peerDependencies": {
|
|
23
|
-
"@itwin/core-
|
|
24
|
-
"@itwin/core-
|
|
25
|
-
"@itwin/core-common": "^3.
|
|
26
|
-
"@itwin/core-frontend": "^3.
|
|
27
|
-
"@
|
|
28
|
-
"@itwin/presentation-common": "^3.0.0-extension.1",
|
|
23
|
+
"@itwin/core-backend": "^3.1.0-dev.6",
|
|
24
|
+
"@itwin/core-bentley": "^3.1.0-dev.6",
|
|
25
|
+
"@itwin/core-common": "^3.1.0-dev.6",
|
|
26
|
+
"@itwin/core-frontend": "^3.1.0-dev.6",
|
|
27
|
+
"@itwin/presentation-common": "^3.1.0-dev.6",
|
|
29
28
|
"electron": "^14.0.0"
|
|
30
29
|
},
|
|
31
30
|
"devDependencies": {
|
|
32
|
-
"@itwin/
|
|
33
|
-
"@itwin/
|
|
34
|
-
"@itwin/
|
|
35
|
-
"@itwin/core-
|
|
36
|
-
"@itwin/core-
|
|
37
|
-
"@itwin/
|
|
38
|
-
"@
|
|
39
|
-
"@itwin/presentation-common": "3.0.0-extension.1",
|
|
31
|
+
"@itwin/build-tools": "3.1.0-dev.6",
|
|
32
|
+
"@itwin/core-backend": "3.1.0-dev.6",
|
|
33
|
+
"@itwin/core-bentley": "3.1.0-dev.6",
|
|
34
|
+
"@itwin/core-common": "3.1.0-dev.6",
|
|
35
|
+
"@itwin/core-frontend": "3.1.0-dev.6",
|
|
36
|
+
"@itwin/eslint-plugin": "3.1.0-dev.6",
|
|
37
|
+
"@itwin/presentation-common": "3.1.0-dev.6",
|
|
40
38
|
"@types/node": "14.14.31",
|
|
41
39
|
"electron": "^14.0.0",
|
|
42
40
|
"eslint": "^7.11.0",
|
|
@@ -64,5 +62,6 @@
|
|
|
64
62
|
"lint": "eslint -f visualstudio \"./src/**/*.ts\" 1>&2",
|
|
65
63
|
"test": "",
|
|
66
64
|
"cover": ""
|
|
67
|
-
}
|
|
65
|
+
},
|
|
66
|
+
"readme": "# @itwin/core-electron\r\n\r\nCopyright © Bentley Systems, Incorporated. All rights reserved. See LICENSE.md for license terms and full copyright notice.\r\n\r\n## Description\r\n\r\nThe __@itwin/core-electron__ package contains the electron utilities to write an iTwin.js application based on Electron.\r\n\r\n## Documentation\r\n\r\nSee the [iTwin.js](https://www.itwinjs.org) documentation for more information.\r\n"
|
|
68
67
|
}
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
/** @packageDocumentation
|
|
2
|
-
* @module Authentication
|
|
3
|
-
*/
|
|
4
|
-
import { AccessToken } from "@itwin/core-bentley";
|
|
5
|
-
import { NativeAppAuthorizationBackend } from "@itwin/core-backend";
|
|
6
|
-
import { NativeAppAuthorizationConfiguration } from "@itwin/core-common";
|
|
7
|
-
import { ElectronTokenStore } from "./ElectronTokenStore";
|
|
8
|
-
/**
|
|
9
|
-
* Utility to generate OIDC/OAuth tokens for Desktop Applications
|
|
10
|
-
* @beta
|
|
11
|
-
*/
|
|
12
|
-
export declare class ElectronAuthorizationBackend extends NativeAppAuthorizationBackend {
|
|
13
|
-
static defaultRedirectUri: string;
|
|
14
|
-
private _configuration;
|
|
15
|
-
private _tokenResponse;
|
|
16
|
-
private _tokenStore?;
|
|
17
|
-
private _expiresAt?;
|
|
18
|
-
get tokenStore(): ElectronTokenStore;
|
|
19
|
-
constructor(config?: NativeAppAuthorizationConfiguration);
|
|
20
|
-
get redirectUri(): string;
|
|
21
|
-
/**
|
|
22
|
-
* Used to initialize the client - must be awaited before any other methods are called.
|
|
23
|
-
* The call attempts a silent sign-if possible.
|
|
24
|
-
*/
|
|
25
|
-
initialize(config?: NativeAppAuthorizationConfiguration): Promise<void>;
|
|
26
|
-
refreshToken(): Promise<AccessToken>;
|
|
27
|
-
/** Loads the access token from the store, and refreshes it if necessary and possible
|
|
28
|
-
* @return AccessToken if it's possible to get a valid access token, and undefined otherwise.
|
|
29
|
-
*/
|
|
30
|
-
private loadAccessToken;
|
|
31
|
-
/**
|
|
32
|
-
* Sign-in completely.
|
|
33
|
-
* This is a wrapper around [[signIn]] - the only difference is that the promise resolves
|
|
34
|
-
* with the access token after sign in is complete and successful.
|
|
35
|
-
*/
|
|
36
|
-
signInComplete(): Promise<AccessToken>;
|
|
37
|
-
/**
|
|
38
|
-
* Start the sign-in process
|
|
39
|
-
* - calls the onAccessTokenChanged() call back after the authorization completes
|
|
40
|
-
* or if there is an error.
|
|
41
|
-
* - will attempt in order:
|
|
42
|
-
* (i) load any existing authorized user from storage,
|
|
43
|
-
* (ii) an interactive signin that requires user input.
|
|
44
|
-
*/
|
|
45
|
-
signIn(): Promise<void>;
|
|
46
|
-
private _onAuthorizationResponse;
|
|
47
|
-
/**
|
|
48
|
-
* Start the sign-out process
|
|
49
|
-
* - calls the onAccessTokenChanged() call back after the authorization completes
|
|
50
|
-
* or if there is an error.
|
|
51
|
-
* - redirects application to the postSignoutRedirectUri specified in the configuration when the sign out is
|
|
52
|
-
* complete
|
|
53
|
-
*/
|
|
54
|
-
signOut(): Promise<void>;
|
|
55
|
-
/**
|
|
56
|
-
* Sign out completely
|
|
57
|
-
* This is a wrapper around [[signOut]] - the only difference is that the promise resolves
|
|
58
|
-
* after the sign out is complete.
|
|
59
|
-
*/
|
|
60
|
-
signOutComplete(): Promise<void>;
|
|
61
|
-
private clearTokenResponse;
|
|
62
|
-
private setTokenResponse;
|
|
63
|
-
private get _hasExpired();
|
|
64
|
-
getAccessToken(): Promise<AccessToken>;
|
|
65
|
-
private refreshAccessToken;
|
|
66
|
-
/** Swap the authorization code for a refresh token and access token */
|
|
67
|
-
private swapAuthorizationCodeForTokens;
|
|
68
|
-
private makeRefreshAccessTokenRequest;
|
|
69
|
-
private makeRevokeTokenRequest;
|
|
70
|
-
}
|
|
71
|
-
//# sourceMappingURL=ElectronAuthorizationBackend.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ElectronAuthorizationBackend.d.ts","sourceRoot":"","sources":["../../../src/backend/ElectronAuthorizationBackend.ts"],"names":[],"mappings":"AAMA;;GAEG;AAKH,OAAO,EAAE,WAAW,EAA4C,MAAM,qBAAqB,CAAC;AAC5F,OAAO,EAAE,6BAA6B,EAAc,MAAM,qBAAqB,CAAC;AAChF,OAAO,EAAE,mCAAmC,EAAE,MAAM,oBAAoB,CAAC;AASzE,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAK1D;;;GAGG;AACH,qBAAa,4BAA6B,SAAQ,6BAA6B;IAC7E,OAAc,kBAAkB,SAA2C;IAC3E,OAAO,CAAC,cAAc,CAAgD;IACtE,OAAO,CAAC,cAAc,CAA4B;IAClD,OAAO,CAAC,WAAW,CAAC,CAAqB;IACzC,OAAO,CAAC,UAAU,CAAC,CAAO;IAC1B,IAAW,UAAU,uBAAgC;gBAElC,MAAM,CAAC,EAAE,mCAAmC;IAI/D,IAAW,WAAW,WAA0F;IAEhH;;;OAGG;IACmB,UAAU,CAAC,MAAM,CAAC,EAAE,mCAAmC,GAAG,OAAO,CAAC,IAAI,CAAC;IAchF,YAAY,IAAI,OAAO,CAAC,WAAW,CAAC;IAQjD;;OAEG;YACW,eAAe;IAW7B;;;;OAIG;IACU,cAAc,IAAI,OAAO,CAAC,WAAW,CAAC;IAanD;;;;;;;OAOG;IACU,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;YAqDtB,wBAAwB;IAsBtC;;;;;;OAMG;IACU,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAIrC;;;;OAIG;IACU,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;YAa/B,kBAAkB;YAMlB,gBAAgB;IAY9B,OAAO,KAAK,WAAW,GAKtB;IAEqB,cAAc,IAAI,OAAO,CAAC,WAAW,CAAC;YAM9C,kBAAkB;IAMhC,uEAAuE;YACzD,8BAA8B;YAqB9B,6BAA6B;YAkB7B,sBAAsB;CAqBrC"}
|
|
@@ -1,265 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/*---------------------------------------------------------------------------------------------
|
|
3
|
-
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
|
|
4
|
-
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
5
|
-
*--------------------------------------------------------------------------------------------*/
|
|
6
|
-
// Code based on the blog article @ https://authguidance.com
|
|
7
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
-
exports.ElectronAuthorizationBackend = void 0;
|
|
9
|
-
/** @packageDocumentation
|
|
10
|
-
* @module Authentication
|
|
11
|
-
*/
|
|
12
|
-
// cSpell:ignore openid appauth signin Pkce Signout
|
|
13
|
-
/* eslint-disable @typescript-eslint/naming-convention */
|
|
14
|
-
const core_bentley_1 = require("@itwin/core-bentley");
|
|
15
|
-
const core_backend_1 = require("@itwin/core-backend");
|
|
16
|
-
const appauth_1 = require("@openid/appauth");
|
|
17
|
-
const node_support_1 = require("@openid/appauth/built/node_support");
|
|
18
|
-
const ElectronAuthorizationEvents_1 = require("./ElectronAuthorizationEvents");
|
|
19
|
-
const ElectronAuthorizationRequestHandler_1 = require("./ElectronAuthorizationRequestHandler");
|
|
20
|
-
const ElectronTokenStore_1 = require("./ElectronTokenStore");
|
|
21
|
-
const LoopbackWebServer_1 = require("./LoopbackWebServer");
|
|
22
|
-
const loggerCategory = "electron-auth";
|
|
23
|
-
/**
|
|
24
|
-
* Utility to generate OIDC/OAuth tokens for Desktop Applications
|
|
25
|
-
* @beta
|
|
26
|
-
*/
|
|
27
|
-
class ElectronAuthorizationBackend extends core_backend_1.NativeAppAuthorizationBackend {
|
|
28
|
-
constructor(config) {
|
|
29
|
-
super(config);
|
|
30
|
-
}
|
|
31
|
-
get tokenStore() { return this._tokenStore; }
|
|
32
|
-
get redirectUri() { var _a, _b; return (_b = (_a = this.config) === null || _a === void 0 ? void 0 : _a.redirectUri) !== null && _b !== void 0 ? _b : ElectronAuthorizationBackend.defaultRedirectUri; }
|
|
33
|
-
/**
|
|
34
|
-
* Used to initialize the client - must be awaited before any other methods are called.
|
|
35
|
-
* The call attempts a silent sign-if possible.
|
|
36
|
-
*/
|
|
37
|
-
async initialize(config) {
|
|
38
|
-
await super.initialize(config);
|
|
39
|
-
(0, core_bentley_1.assert)(this.config !== undefined && this.issuerUrl !== undefined, "URL of authorization provider was not initialized");
|
|
40
|
-
this._tokenStore = new ElectronTokenStore_1.ElectronTokenStore(this.config.clientId);
|
|
41
|
-
const tokenRequestor = new node_support_1.NodeRequestor(); // the Node.js based HTTP client
|
|
42
|
-
this._configuration = await appauth_1.AuthorizationServiceConfiguration.fetchFromIssuer(this.issuerUrl, tokenRequestor);
|
|
43
|
-
core_bentley_1.Logger.logTrace(loggerCategory, "Initialized service configuration", () => ({ configuration: this._configuration }));
|
|
44
|
-
// Attempt to load the access token from store
|
|
45
|
-
await this.loadAccessToken();
|
|
46
|
-
}
|
|
47
|
-
async refreshToken() {
|
|
48
|
-
if (this._tokenResponse === undefined || this._tokenResponse.refreshToken === undefined)
|
|
49
|
-
return "";
|
|
50
|
-
const token = `Bearer ${this._tokenResponse.refreshToken}`;
|
|
51
|
-
return this.refreshAccessToken(token);
|
|
52
|
-
}
|
|
53
|
-
/** Loads the access token from the store, and refreshes it if necessary and possible
|
|
54
|
-
* @return AccessToken if it's possible to get a valid access token, and undefined otherwise.
|
|
55
|
-
*/
|
|
56
|
-
async loadAccessToken() {
|
|
57
|
-
const tokenResponse = await this.tokenStore.load();
|
|
58
|
-
if (tokenResponse === undefined || tokenResponse.refreshToken === undefined)
|
|
59
|
-
return "";
|
|
60
|
-
try {
|
|
61
|
-
return await this.refreshAccessToken(tokenResponse.refreshToken);
|
|
62
|
-
}
|
|
63
|
-
catch (err) {
|
|
64
|
-
return "";
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* Sign-in completely.
|
|
69
|
-
* This is a wrapper around [[signIn]] - the only difference is that the promise resolves
|
|
70
|
-
* with the access token after sign in is complete and successful.
|
|
71
|
-
*/
|
|
72
|
-
async signInComplete() {
|
|
73
|
-
return new Promise((resolve, reject) => {
|
|
74
|
-
core_backend_1.NativeHost.onAccessTokenChanged.addOnce((token) => {
|
|
75
|
-
if (token !== "") {
|
|
76
|
-
resolve(token);
|
|
77
|
-
}
|
|
78
|
-
else {
|
|
79
|
-
reject(new Error("Failed to sign in"));
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
this.signIn().catch((err) => reject(err));
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
/**
|
|
86
|
-
* Start the sign-in process
|
|
87
|
-
* - calls the onAccessTokenChanged() call back after the authorization completes
|
|
88
|
-
* or if there is an error.
|
|
89
|
-
* - will attempt in order:
|
|
90
|
-
* (i) load any existing authorized user from storage,
|
|
91
|
-
* (ii) an interactive signin that requires user input.
|
|
92
|
-
*/
|
|
93
|
-
async signIn() {
|
|
94
|
-
if (!this._configuration)
|
|
95
|
-
throw new core_bentley_1.BentleyError(core_bentley_1.AuthStatus.Error, "Not initialized. First call initialize()");
|
|
96
|
-
(0, core_bentley_1.assert)(this.config !== undefined);
|
|
97
|
-
// Attempt to load the access token from store
|
|
98
|
-
const token = await this.loadAccessToken();
|
|
99
|
-
if (token)
|
|
100
|
-
return this.setAccessToken(token);
|
|
101
|
-
// Create the authorization request
|
|
102
|
-
const nativeConfig = this.config;
|
|
103
|
-
const authReqJson = {
|
|
104
|
-
client_id: nativeConfig.clientId,
|
|
105
|
-
redirect_uri: this.redirectUri,
|
|
106
|
-
scope: nativeConfig.scope,
|
|
107
|
-
response_type: appauth_1.AuthorizationRequest.RESPONSE_TYPE_CODE,
|
|
108
|
-
extras: { prompt: "consent", access_type: "offline" },
|
|
109
|
-
};
|
|
110
|
-
const authorizationRequest = new appauth_1.AuthorizationRequest(authReqJson, new node_support_1.NodeCrypto(), true /* = usePkce */);
|
|
111
|
-
await authorizationRequest.setupCodeVerifier();
|
|
112
|
-
// Create events for this signin attempt
|
|
113
|
-
const authorizationEvents = new ElectronAuthorizationEvents_1.ElectronAuthorizationEvents();
|
|
114
|
-
// Ensure that completion callbacks are correlated to the correct authorization request
|
|
115
|
-
LoopbackWebServer_1.LoopbackWebServer.addCorrelationState(authorizationRequest.state, authorizationEvents);
|
|
116
|
-
// Start a web server to listen to the browser requests
|
|
117
|
-
LoopbackWebServer_1.LoopbackWebServer.start(nativeConfig);
|
|
118
|
-
const authorizationHandler = new ElectronAuthorizationRequestHandler_1.ElectronAuthorizationRequestHandler(authorizationEvents);
|
|
119
|
-
// Setup a notifier to obtain the result of authorization
|
|
120
|
-
const notifier = new appauth_1.AuthorizationNotifier();
|
|
121
|
-
authorizationHandler.setAuthorizationNotifier(notifier);
|
|
122
|
-
notifier.setAuthorizationListener(async (authRequest, authResponse, authError) => {
|
|
123
|
-
core_bentley_1.Logger.logTrace(loggerCategory, "Authorization listener invoked", () => ({ authRequest, authResponse, authError }));
|
|
124
|
-
const tokenResponse = await this._onAuthorizationResponse(authRequest, authResponse, authError);
|
|
125
|
-
authorizationEvents.onAuthorizationResponseCompleted.raiseEvent(authError ? authError : undefined);
|
|
126
|
-
if (!tokenResponse)
|
|
127
|
-
await this.clearTokenResponse();
|
|
128
|
-
else
|
|
129
|
-
await this.setTokenResponse(tokenResponse);
|
|
130
|
-
});
|
|
131
|
-
// Start the signin
|
|
132
|
-
await authorizationHandler.performAuthorizationRequest(this._configuration, authorizationRequest);
|
|
133
|
-
}
|
|
134
|
-
async _onAuthorizationResponse(authRequest, authResponse, authError) {
|
|
135
|
-
// Phase 1 of login has completed to fetch the authorization code - check for errors
|
|
136
|
-
if (authError) {
|
|
137
|
-
core_bentley_1.Logger.logError(loggerCategory, "Authorization error. Unable to get authorization code.", () => authError);
|
|
138
|
-
return undefined;
|
|
139
|
-
}
|
|
140
|
-
if (!authResponse || authResponse.state !== authRequest.state) {
|
|
141
|
-
core_bentley_1.Logger.logError(loggerCategory, "Authorization error. Unable to get authorization code", () => ({
|
|
142
|
-
error: "invalid_state",
|
|
143
|
-
errorDescription: "The login response state did not match the login request state.",
|
|
144
|
-
}));
|
|
145
|
-
return undefined;
|
|
146
|
-
}
|
|
147
|
-
// Phase 2: Swap the authorization code for the access token
|
|
148
|
-
const tokenResponse = await this.swapAuthorizationCodeForTokens(authResponse.code, authRequest.internal.code_verifier);
|
|
149
|
-
core_bentley_1.Logger.logTrace(loggerCategory, "Authorization completed, and issued access token");
|
|
150
|
-
return tokenResponse;
|
|
151
|
-
}
|
|
152
|
-
/**
|
|
153
|
-
* Start the sign-out process
|
|
154
|
-
* - calls the onAccessTokenChanged() call back after the authorization completes
|
|
155
|
-
* or if there is an error.
|
|
156
|
-
* - redirects application to the postSignoutRedirectUri specified in the configuration when the sign out is
|
|
157
|
-
* complete
|
|
158
|
-
*/
|
|
159
|
-
async signOut() {
|
|
160
|
-
await this.makeRevokeTokenRequest();
|
|
161
|
-
}
|
|
162
|
-
/**
|
|
163
|
-
* Sign out completely
|
|
164
|
-
* This is a wrapper around [[signOut]] - the only difference is that the promise resolves
|
|
165
|
-
* after the sign out is complete.
|
|
166
|
-
*/
|
|
167
|
-
async signOutComplete() {
|
|
168
|
-
return new Promise((resolve, reject) => {
|
|
169
|
-
core_backend_1.NativeHost.onAccessTokenChanged.addOnce((token) => {
|
|
170
|
-
if (token === "") {
|
|
171
|
-
resolve();
|
|
172
|
-
}
|
|
173
|
-
else {
|
|
174
|
-
reject(new Error("Failed to sign out"));
|
|
175
|
-
}
|
|
176
|
-
});
|
|
177
|
-
this.signOut().catch((err) => reject(err));
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
async clearTokenResponse() {
|
|
181
|
-
this._tokenResponse = undefined;
|
|
182
|
-
await this.tokenStore.delete();
|
|
183
|
-
this.setAccessToken("");
|
|
184
|
-
}
|
|
185
|
-
async setTokenResponse(tokenResponse) {
|
|
186
|
-
var _a;
|
|
187
|
-
const accessToken = tokenResponse.accessToken;
|
|
188
|
-
this._tokenResponse = tokenResponse;
|
|
189
|
-
const expiresAtMilliseconds = (tokenResponse.issuedAt + ((_a = tokenResponse.expiresIn) !== null && _a !== void 0 ? _a : 0)) * 1000;
|
|
190
|
-
this._expiresAt = new Date(expiresAtMilliseconds);
|
|
191
|
-
await this.tokenStore.save(this._tokenResponse);
|
|
192
|
-
const bearerToken = `${tokenResponse.tokenType} ${accessToken}`;
|
|
193
|
-
this.setAccessToken(bearerToken);
|
|
194
|
-
return bearerToken;
|
|
195
|
-
}
|
|
196
|
-
get _hasExpired() {
|
|
197
|
-
if (!this._expiresAt)
|
|
198
|
-
return false;
|
|
199
|
-
return this._expiresAt.getTime() - Date.now() <= 1 * 60 * 1000; // Consider 1 minute before expiry as expired
|
|
200
|
-
}
|
|
201
|
-
async getAccessToken() {
|
|
202
|
-
if (this._hasExpired || !this._accessToken)
|
|
203
|
-
this.setAccessToken(await this.refreshToken());
|
|
204
|
-
return this._accessToken;
|
|
205
|
-
}
|
|
206
|
-
async refreshAccessToken(refreshToken) {
|
|
207
|
-
const tokenResponse = await this.makeRefreshAccessTokenRequest(refreshToken);
|
|
208
|
-
core_bentley_1.Logger.logTrace(loggerCategory, "Refresh token completed, and issued access token");
|
|
209
|
-
return this.setTokenResponse(tokenResponse);
|
|
210
|
-
}
|
|
211
|
-
/** Swap the authorization code for a refresh token and access token */
|
|
212
|
-
async swapAuthorizationCodeForTokens(authCode, codeVerifier) {
|
|
213
|
-
if (!this._configuration)
|
|
214
|
-
throw new core_bentley_1.BentleyError(core_bentley_1.AuthStatus.Error, "Not initialized. First call initialize()");
|
|
215
|
-
(0, core_bentley_1.assert)(this.config !== undefined);
|
|
216
|
-
const nativeConfig = this.config;
|
|
217
|
-
const extras = { code_verifier: codeVerifier };
|
|
218
|
-
const tokenRequestJson = {
|
|
219
|
-
grant_type: appauth_1.GRANT_TYPE_AUTHORIZATION_CODE,
|
|
220
|
-
code: authCode,
|
|
221
|
-
redirect_uri: this.redirectUri,
|
|
222
|
-
client_id: nativeConfig.clientId,
|
|
223
|
-
extras,
|
|
224
|
-
};
|
|
225
|
-
const tokenRequest = new appauth_1.TokenRequest(tokenRequestJson);
|
|
226
|
-
const tokenRequestor = new node_support_1.NodeRequestor();
|
|
227
|
-
const tokenHandler = new appauth_1.BaseTokenRequestHandler(tokenRequestor);
|
|
228
|
-
return tokenHandler.performTokenRequest(this._configuration, tokenRequest);
|
|
229
|
-
}
|
|
230
|
-
async makeRefreshAccessTokenRequest(refreshToken) {
|
|
231
|
-
if (!this._configuration)
|
|
232
|
-
throw new core_bentley_1.BentleyError(core_bentley_1.AuthStatus.Error, "Not initialized. First call initialize()");
|
|
233
|
-
(0, core_bentley_1.assert)(this.config !== undefined);
|
|
234
|
-
const tokenRequestJson = {
|
|
235
|
-
grant_type: appauth_1.GRANT_TYPE_REFRESH_TOKEN,
|
|
236
|
-
refresh_token: refreshToken,
|
|
237
|
-
redirect_uri: this.redirectUri,
|
|
238
|
-
client_id: this.config.clientId,
|
|
239
|
-
};
|
|
240
|
-
const tokenRequest = new appauth_1.TokenRequest(tokenRequestJson);
|
|
241
|
-
const tokenRequestor = new node_support_1.NodeRequestor();
|
|
242
|
-
const tokenHandler = new appauth_1.BaseTokenRequestHandler(tokenRequestor);
|
|
243
|
-
return tokenHandler.performTokenRequest(this._configuration, tokenRequest);
|
|
244
|
-
}
|
|
245
|
-
async makeRevokeTokenRequest() {
|
|
246
|
-
if (!this._tokenResponse)
|
|
247
|
-
throw new core_bentley_1.BentleyError(core_bentley_1.AuthStatus.Error, "Missing refresh token. First call signIn() and ensure it's successful");
|
|
248
|
-
(0, core_bentley_1.assert)(this.config !== undefined);
|
|
249
|
-
const refreshToken = this._tokenResponse.refreshToken;
|
|
250
|
-
const revokeTokenRequestJson = {
|
|
251
|
-
token: refreshToken,
|
|
252
|
-
token_type_hint: "refresh_token",
|
|
253
|
-
client_id: this.config.clientId,
|
|
254
|
-
};
|
|
255
|
-
const revokeTokenRequest = new appauth_1.RevokeTokenRequest(revokeTokenRequestJson);
|
|
256
|
-
const tokenRequestor = new node_support_1.NodeRequestor();
|
|
257
|
-
const tokenHandler = new appauth_1.BaseTokenRequestHandler(tokenRequestor);
|
|
258
|
-
await tokenHandler.performRevokeTokenRequest(this._configuration, revokeTokenRequest);
|
|
259
|
-
core_bentley_1.Logger.logTrace(loggerCategory, "Authorization revoked, and removed access token");
|
|
260
|
-
await this.clearTokenResponse();
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
exports.ElectronAuthorizationBackend = ElectronAuthorizationBackend;
|
|
264
|
-
ElectronAuthorizationBackend.defaultRedirectUri = "http://localhost:3000/signin-callback";
|
|
265
|
-
//# sourceMappingURL=ElectronAuthorizationBackend.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ElectronAuthorizationBackend.js","sourceRoot":"","sources":["../../../src/backend/ElectronAuthorizationBackend.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n* See LICENSE.md in the project root for license terms and full copyright notice.\r\n*--------------------------------------------------------------------------------------------*/\r\n// Code based on the blog article @ https://authguidance.com\r\n\r\n/** @packageDocumentation\r\n * @module Authentication\r\n */\r\n\r\n// cSpell:ignore openid appauth signin Pkce Signout\r\n/* eslint-disable @typescript-eslint/naming-convention */\r\n\r\nimport { AccessToken, assert, AuthStatus, BentleyError, Logger } from \"@itwin/core-bentley\";\r\nimport { NativeAppAuthorizationBackend, NativeHost } from \"@itwin/core-backend\";\r\nimport { NativeAppAuthorizationConfiguration } from \"@itwin/core-common\";\r\nimport {\r\n AuthorizationError, AuthorizationNotifier, AuthorizationRequest, AuthorizationRequestJson, AuthorizationResponse, AuthorizationServiceConfiguration,\r\n BaseTokenRequestHandler, GRANT_TYPE_AUTHORIZATION_CODE, GRANT_TYPE_REFRESH_TOKEN, RevokeTokenRequest, RevokeTokenRequestJson, StringMap,\r\n TokenRequest, TokenRequestHandler, TokenRequestJson, TokenResponse,\r\n} from \"@openid/appauth\";\r\nimport { NodeCrypto, NodeRequestor } from \"@openid/appauth/built/node_support\";\r\nimport { ElectronAuthorizationEvents } from \"./ElectronAuthorizationEvents\";\r\nimport { ElectronAuthorizationRequestHandler } from \"./ElectronAuthorizationRequestHandler\";\r\nimport { ElectronTokenStore } from \"./ElectronTokenStore\";\r\nimport { LoopbackWebServer } from \"./LoopbackWebServer\";\r\n\r\nconst loggerCategory = \"electron-auth\";\r\n\r\n/**\r\n * Utility to generate OIDC/OAuth tokens for Desktop Applications\r\n * @beta\r\n */\r\nexport class ElectronAuthorizationBackend extends NativeAppAuthorizationBackend {\r\n public static defaultRedirectUri = \"http://localhost:3000/signin-callback\";\r\n private _configuration: AuthorizationServiceConfiguration | undefined;\r\n private _tokenResponse: TokenResponse | undefined;\r\n private _tokenStore?: ElectronTokenStore;\r\n private _expiresAt?: Date;\r\n public get tokenStore() { return this._tokenStore!; }\r\n\r\n public constructor(config?: NativeAppAuthorizationConfiguration) {\r\n super(config);\r\n }\r\n\r\n public get redirectUri() { return this.config?.redirectUri ?? ElectronAuthorizationBackend.defaultRedirectUri; }\r\n\r\n /**\r\n * Used to initialize the client - must be awaited before any other methods are called.\r\n * The call attempts a silent sign-if possible.\r\n */\r\n public override async initialize(config?: NativeAppAuthorizationConfiguration): Promise<void> {\r\n await super.initialize(config);\r\n assert(this.config !== undefined && this.issuerUrl !== undefined, \"URL of authorization provider was not initialized\");\r\n\r\n this._tokenStore = new ElectronTokenStore(this.config.clientId);\r\n\r\n const tokenRequestor = new NodeRequestor(); // the Node.js based HTTP client\r\n this._configuration = await AuthorizationServiceConfiguration.fetchFromIssuer(this.issuerUrl, tokenRequestor);\r\n Logger.logTrace(loggerCategory, \"Initialized service configuration\", () => ({ configuration: this._configuration }));\r\n\r\n // Attempt to load the access token from store\r\n await this.loadAccessToken();\r\n }\r\n\r\n public async refreshToken(): Promise<AccessToken> {\r\n if (this._tokenResponse === undefined || this._tokenResponse.refreshToken === undefined)\r\n return \"\";\r\n\r\n const token = `Bearer ${this._tokenResponse.refreshToken}`;\r\n return this.refreshAccessToken(token);\r\n }\r\n\r\n /** Loads the access token from the store, and refreshes it if necessary and possible\r\n * @return AccessToken if it's possible to get a valid access token, and undefined otherwise.\r\n */\r\n private async loadAccessToken(): Promise<AccessToken> {\r\n const tokenResponse = await this.tokenStore.load();\r\n if (tokenResponse === undefined || tokenResponse.refreshToken === undefined)\r\n return \"\";\r\n try {\r\n return await this.refreshAccessToken(tokenResponse.refreshToken);\r\n } catch (err) {\r\n return \"\";\r\n }\r\n }\r\n\r\n /**\r\n * Sign-in completely.\r\n * This is a wrapper around [[signIn]] - the only difference is that the promise resolves\r\n * with the access token after sign in is complete and successful.\r\n */\r\n public async signInComplete(): Promise<AccessToken> {\r\n return new Promise<AccessToken>((resolve, reject) => {\r\n NativeHost.onAccessTokenChanged.addOnce((token) => {\r\n if (token !== \"\") {\r\n resolve(token);\r\n } else {\r\n reject(new Error(\"Failed to sign in\"));\r\n }\r\n });\r\n this.signIn().catch((err) => reject(err));\r\n });\r\n }\r\n\r\n /**\r\n * Start the sign-in process\r\n * - calls the onAccessTokenChanged() call back after the authorization completes\r\n * or if there is an error.\r\n * - will attempt in order:\r\n * (i) load any existing authorized user from storage,\r\n * (ii) an interactive signin that requires user input.\r\n */\r\n public async signIn(): Promise<void> {\r\n if (!this._configuration)\r\n throw new BentleyError(AuthStatus.Error, \"Not initialized. First call initialize()\");\r\n assert(this.config !== undefined);\r\n\r\n // Attempt to load the access token from store\r\n const token = await this.loadAccessToken();\r\n if (token)\r\n return this.setAccessToken(token);\r\n\r\n // Create the authorization request\r\n const nativeConfig = this.config;\r\n const authReqJson: AuthorizationRequestJson = {\r\n client_id: nativeConfig.clientId,\r\n redirect_uri: this.redirectUri,\r\n scope: nativeConfig.scope,\r\n response_type: AuthorizationRequest.RESPONSE_TYPE_CODE,\r\n extras: { prompt: \"consent\", access_type: \"offline\" },\r\n };\r\n const authorizationRequest = new AuthorizationRequest(authReqJson, new NodeCrypto(), true /* = usePkce */);\r\n await authorizationRequest.setupCodeVerifier();\r\n\r\n // Create events for this signin attempt\r\n const authorizationEvents = new ElectronAuthorizationEvents();\r\n\r\n // Ensure that completion callbacks are correlated to the correct authorization request\r\n LoopbackWebServer.addCorrelationState(authorizationRequest.state, authorizationEvents);\r\n\r\n // Start a web server to listen to the browser requests\r\n LoopbackWebServer.start(nativeConfig);\r\n\r\n const authorizationHandler = new ElectronAuthorizationRequestHandler(authorizationEvents);\r\n\r\n // Setup a notifier to obtain the result of authorization\r\n const notifier = new AuthorizationNotifier();\r\n authorizationHandler.setAuthorizationNotifier(notifier);\r\n notifier.setAuthorizationListener(async (authRequest: AuthorizationRequest, authResponse: AuthorizationResponse | null, authError: AuthorizationError | null) => {\r\n Logger.logTrace(loggerCategory, \"Authorization listener invoked\", () => ({ authRequest, authResponse, authError }));\r\n\r\n const tokenResponse = await this._onAuthorizationResponse(authRequest, authResponse, authError);\r\n\r\n authorizationEvents.onAuthorizationResponseCompleted.raiseEvent(authError ? authError : undefined);\r\n\r\n if (!tokenResponse)\r\n await this.clearTokenResponse();\r\n else\r\n await this.setTokenResponse(tokenResponse);\r\n });\r\n\r\n // Start the signin\r\n await authorizationHandler.performAuthorizationRequest(this._configuration, authorizationRequest);\r\n }\r\n\r\n private async _onAuthorizationResponse(authRequest: AuthorizationRequest, authResponse: AuthorizationResponse | null, authError: AuthorizationError | null): Promise<TokenResponse | undefined> {\r\n\r\n // Phase 1 of login has completed to fetch the authorization code - check for errors\r\n if (authError) {\r\n Logger.logError(loggerCategory, \"Authorization error. Unable to get authorization code.\", () => authError);\r\n return undefined;\r\n }\r\n\r\n if (!authResponse || authResponse.state !== authRequest.state) {\r\n Logger.logError(loggerCategory, \"Authorization error. Unable to get authorization code\", () => ({\r\n error: \"invalid_state\",\r\n errorDescription: \"The login response state did not match the login request state.\",\r\n }));\r\n return undefined;\r\n }\r\n\r\n // Phase 2: Swap the authorization code for the access token\r\n const tokenResponse = await this.swapAuthorizationCodeForTokens(authResponse.code, authRequest.internal!.code_verifier);\r\n Logger.logTrace(loggerCategory, \"Authorization completed, and issued access token\");\r\n return tokenResponse;\r\n }\r\n\r\n /**\r\n * Start the sign-out process\r\n * - calls the onAccessTokenChanged() call back after the authorization completes\r\n * or if there is an error.\r\n * - redirects application to the postSignoutRedirectUri specified in the configuration when the sign out is\r\n * complete\r\n */\r\n public async signOut(): Promise<void> {\r\n await this.makeRevokeTokenRequest();\r\n }\r\n\r\n /**\r\n * Sign out completely\r\n * This is a wrapper around [[signOut]] - the only difference is that the promise resolves\r\n * after the sign out is complete.\r\n */\r\n public async signOutComplete(): Promise<void> {\r\n return new Promise<void>((resolve, reject) => {\r\n NativeHost.onAccessTokenChanged.addOnce((token) => {\r\n if (token === \"\") {\r\n resolve();\r\n } else {\r\n reject(new Error(\"Failed to sign out\"));\r\n }\r\n });\r\n this.signOut().catch((err) => reject(err));\r\n });\r\n }\r\n\r\n private async clearTokenResponse() {\r\n this._tokenResponse = undefined;\r\n await this.tokenStore.delete();\r\n this.setAccessToken(\"\");\r\n }\r\n\r\n private async setTokenResponse(tokenResponse: TokenResponse): Promise<AccessToken> {\r\n const accessToken = tokenResponse.accessToken;\r\n this._tokenResponse = tokenResponse;\r\n const expiresAtMilliseconds = (tokenResponse.issuedAt + (tokenResponse.expiresIn ?? 0)) * 1000;\r\n this._expiresAt = new Date(expiresAtMilliseconds);\r\n\r\n await this.tokenStore.save(this._tokenResponse);\r\n const bearerToken = `${tokenResponse.tokenType} ${accessToken}`;\r\n this.setAccessToken(bearerToken);\r\n return bearerToken;\r\n }\r\n\r\n private get _hasExpired(): boolean {\r\n if (!this._expiresAt)\r\n return false;\r\n\r\n return this._expiresAt.getTime() - Date.now() <= 1 * 60 * 1000; // Consider 1 minute before expiry as expired\r\n }\r\n\r\n public override async getAccessToken(): Promise<AccessToken> {\r\n if (this._hasExpired || !this._accessToken)\r\n this.setAccessToken(await this.refreshToken());\r\n return this._accessToken;\r\n }\r\n\r\n private async refreshAccessToken(refreshToken: string): Promise<AccessToken> {\r\n const tokenResponse = await this.makeRefreshAccessTokenRequest(refreshToken);\r\n Logger.logTrace(loggerCategory, \"Refresh token completed, and issued access token\");\r\n return this.setTokenResponse(tokenResponse);\r\n }\r\n\r\n /** Swap the authorization code for a refresh token and access token */\r\n private async swapAuthorizationCodeForTokens(authCode: string, codeVerifier: string): Promise<TokenResponse> {\r\n if (!this._configuration)\r\n throw new BentleyError(AuthStatus.Error, \"Not initialized. First call initialize()\");\r\n assert(this.config !== undefined);\r\n\r\n const nativeConfig = this.config;\r\n const extras: StringMap = { code_verifier: codeVerifier };\r\n const tokenRequestJson: TokenRequestJson = {\r\n grant_type: GRANT_TYPE_AUTHORIZATION_CODE,\r\n code: authCode,\r\n redirect_uri: this.redirectUri,\r\n client_id: nativeConfig.clientId,\r\n extras,\r\n };\r\n\r\n const tokenRequest = new TokenRequest(tokenRequestJson);\r\n const tokenRequestor = new NodeRequestor();\r\n const tokenHandler: TokenRequestHandler = new BaseTokenRequestHandler(tokenRequestor);\r\n return tokenHandler.performTokenRequest(this._configuration, tokenRequest);\r\n }\r\n\r\n private async makeRefreshAccessTokenRequest(refreshToken: string): Promise<TokenResponse> {\r\n if (!this._configuration)\r\n throw new BentleyError(AuthStatus.Error, \"Not initialized. First call initialize()\");\r\n assert(this.config !== undefined);\r\n\r\n const tokenRequestJson: TokenRequestJson = {\r\n grant_type: GRANT_TYPE_REFRESH_TOKEN,\r\n refresh_token: refreshToken,\r\n redirect_uri: this.redirectUri,\r\n client_id: this.config.clientId,\r\n };\r\n\r\n const tokenRequest = new TokenRequest(tokenRequestJson);\r\n const tokenRequestor = new NodeRequestor();\r\n const tokenHandler: TokenRequestHandler = new BaseTokenRequestHandler(tokenRequestor);\r\n return tokenHandler.performTokenRequest(this._configuration, tokenRequest);\r\n }\r\n\r\n private async makeRevokeTokenRequest(): Promise<void> {\r\n if (!this._tokenResponse)\r\n throw new BentleyError(AuthStatus.Error, \"Missing refresh token. First call signIn() and ensure it's successful\");\r\n assert(this.config !== undefined);\r\n\r\n const refreshToken = this._tokenResponse.refreshToken!;\r\n\r\n const revokeTokenRequestJson: RevokeTokenRequestJson = {\r\n token: refreshToken,\r\n token_type_hint: \"refresh_token\",\r\n client_id: this.config.clientId,\r\n };\r\n\r\n const revokeTokenRequest = new RevokeTokenRequest(revokeTokenRequestJson);\r\n const tokenRequestor = new NodeRequestor();\r\n const tokenHandler: TokenRequestHandler = new BaseTokenRequestHandler(tokenRequestor);\r\n await tokenHandler.performRevokeTokenRequest(this._configuration!, revokeTokenRequest);\r\n\r\n Logger.logTrace(loggerCategory, \"Authorization revoked, and removed access token\");\r\n await this.clearTokenResponse();\r\n }\r\n}\r\n"]}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/** @packageDocumentation
|
|
2
|
-
* @module Authentication
|
|
3
|
-
*/
|
|
4
|
-
import { BeEvent } from "@itwin/core-bentley";
|
|
5
|
-
import { AuthorizationErrorJson, AuthorizationResponseJson } from "@openid/appauth";
|
|
6
|
-
/** @internal */
|
|
7
|
-
export declare type AuthorizationResponseCompletedListener = (error?: AuthorizationErrorJson) => void;
|
|
8
|
-
/** @internal */
|
|
9
|
-
export declare type AuthorizationResponseListener = (error: AuthorizationErrorJson | null, response: AuthorizationResponseJson | null) => void;
|
|
10
|
-
/**
|
|
11
|
-
* Internal events used by authorization by DesktopAuthorizationClient and related classes
|
|
12
|
-
* @internal
|
|
13
|
-
*/
|
|
14
|
-
export declare class ElectronAuthorizationEvents {
|
|
15
|
-
/** Event raised when the authorization is completed */
|
|
16
|
-
readonly onAuthorizationResponseCompleted: BeEvent<AuthorizationResponseCompletedListener>;
|
|
17
|
-
/** Event raised when a response is received from the authorization server with the authorization code */
|
|
18
|
-
readonly onAuthorizationResponse: BeEvent<AuthorizationResponseListener>;
|
|
19
|
-
}
|
|
20
|
-
//# sourceMappingURL=ElectronAuthorizationEvents.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ElectronAuthorizationEvents.d.ts","sourceRoot":"","sources":["../../../src/backend/ElectronAuthorizationEvents.ts"],"names":[],"mappings":"AAMA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAC9C,OAAO,EAAE,sBAAsB,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAC;AAEpF,gBAAgB;AAChB,oBAAY,sCAAsC,GAAG,CAAC,KAAK,CAAC,EAAE,sBAAsB,KAAK,IAAI,CAAC;AAE9F,gBAAgB;AAChB,oBAAY,6BAA6B,GAAG,CAAC,KAAK,EAAE,sBAAsB,GAAG,IAAI,EAAE,QAAQ,EAAE,yBAAyB,GAAG,IAAI,KAAK,IAAI,CAAC;AAEvI;;;GAGG;AACH,qBAAa,2BAA2B;IACtC,uDAAuD;IACvD,SAAgB,gCAAgC,kDAAyD;IAEzG,yGAAyG;IACzG,SAAgB,uBAAuB,yCAAgD;CACxF"}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/*---------------------------------------------------------------------------------------------
|
|
3
|
-
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
|
|
4
|
-
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
5
|
-
*--------------------------------------------------------------------------------------------*/
|
|
6
|
-
// Code based on the blog article @ https://authguidance.com
|
|
7
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
-
exports.ElectronAuthorizationEvents = void 0;
|
|
9
|
-
/** @packageDocumentation
|
|
10
|
-
* @module Authentication
|
|
11
|
-
*/
|
|
12
|
-
const core_bentley_1 = require("@itwin/core-bentley");
|
|
13
|
-
/**
|
|
14
|
-
* Internal events used by authorization by DesktopAuthorizationClient and related classes
|
|
15
|
-
* @internal
|
|
16
|
-
*/
|
|
17
|
-
class ElectronAuthorizationEvents {
|
|
18
|
-
constructor() {
|
|
19
|
-
/** Event raised when the authorization is completed */
|
|
20
|
-
this.onAuthorizationResponseCompleted = new core_bentley_1.BeEvent();
|
|
21
|
-
/** Event raised when a response is received from the authorization server with the authorization code */
|
|
22
|
-
this.onAuthorizationResponse = new core_bentley_1.BeEvent();
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
exports.ElectronAuthorizationEvents = ElectronAuthorizationEvents;
|
|
26
|
-
//# sourceMappingURL=ElectronAuthorizationEvents.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ElectronAuthorizationEvents.js","sourceRoot":"","sources":["../../../src/backend/ElectronAuthorizationEvents.ts"],"names":[],"mappings":";AAAA;;;+FAG+F;AAC/F,4DAA4D;;;AAE5D;;GAEG;AAEH,sDAA8C;AAS9C;;;GAGG;AACH,MAAa,2BAA2B;IAAxC;QACE,uDAAuD;QACvC,qCAAgC,GAAG,IAAI,sBAAO,EAA0C,CAAC;QAEzG,yGAAyG;QACzF,4BAAuB,GAAG,IAAI,sBAAO,EAAiC,CAAC;IACzF,CAAC;CAAA;AAND,kEAMC","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n* See LICENSE.md in the project root for license terms and full copyright notice.\r\n*--------------------------------------------------------------------------------------------*/\r\n// Code based on the blog article @ https://authguidance.com\r\n\r\n/** @packageDocumentation\r\n * @module Authentication\r\n */\r\n\r\nimport { BeEvent } from \"@itwin/core-bentley\";\r\nimport { AuthorizationErrorJson, AuthorizationResponseJson } from \"@openid/appauth\";\r\n\r\n/** @internal */\r\nexport type AuthorizationResponseCompletedListener = (error?: AuthorizationErrorJson) => void;\r\n\r\n/** @internal */\r\nexport type AuthorizationResponseListener = (error: AuthorizationErrorJson | null, response: AuthorizationResponseJson | null) => void;\r\n\r\n/**\r\n * Internal events used by authorization by DesktopAuthorizationClient and related classes\r\n * @internal\r\n */\r\nexport class ElectronAuthorizationEvents {\r\n /** Event raised when the authorization is completed */\r\n public readonly onAuthorizationResponseCompleted = new BeEvent<AuthorizationResponseCompletedListener>();\r\n\r\n /** Event raised when a response is received from the authorization server with the authorization code */\r\n public readonly onAuthorizationResponse = new BeEvent<AuthorizationResponseListener>();\r\n}\r\n"]}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { AuthorizationRequest, AuthorizationRequestHandler, AuthorizationRequestResponse, AuthorizationServiceConfiguration } from "@openid/appauth";
|
|
2
|
-
import { ElectronAuthorizationEvents } from "./ElectronAuthorizationEvents";
|
|
3
|
-
/**
|
|
4
|
-
* Utility to setup a local web server that listens to authorization responses to the browser and make the necessary redirections
|
|
5
|
-
* @internal
|
|
6
|
-
*/
|
|
7
|
-
export declare class ElectronAuthorizationRequestHandler extends AuthorizationRequestHandler {
|
|
8
|
-
private _authorizationPromise;
|
|
9
|
-
private _authorizationEvents;
|
|
10
|
-
/**
|
|
11
|
-
* Constructor
|
|
12
|
-
*/
|
|
13
|
-
constructor(authorizationEvents: ElectronAuthorizationEvents);
|
|
14
|
-
/**
|
|
15
|
-
* Makes an authorization request on the system browser
|
|
16
|
-
*/
|
|
17
|
-
performAuthorizationRequest(serviceConfiguration: AuthorizationServiceConfiguration, authRequest: AuthorizationRequest): Promise<void>;
|
|
18
|
-
/**
|
|
19
|
-
* Checks if an authorization flow can be completed, and completes it.
|
|
20
|
-
* The handler returns a `Promise<AuthorizationRequestResponse>` if ready, or a `Promise<null>`
|
|
21
|
-
* if not ready.
|
|
22
|
-
*/
|
|
23
|
-
protected completeAuthorizationRequest(): Promise<AuthorizationRequestResponse | null>;
|
|
24
|
-
}
|
|
25
|
-
//# sourceMappingURL=ElectronAuthorizationRequestHandler.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ElectronAuthorizationRequestHandler.d.ts","sourceRoot":"","sources":["../../../src/backend/ElectronAuthorizationRequestHandler.ts"],"names":[],"mappings":"AAYA,OAAO,EACuC,oBAAoB,EAAE,2BAA2B,EAAE,4BAA4B,EAChG,iCAAiC,EAC7D,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,2BAA2B,EAAE,MAAM,+BAA+B,CAAC;AAE5E;;;GAGG;AACH,qBAAa,mCAAoC,SAAQ,2BAA2B;IAClF,OAAO,CAAC,qBAAqB,CAAsD;IACnF,OAAO,CAAC,oBAAoB,CAA8B;IAE1D;;OAEG;gBACgB,mBAAmB,EAAE,2BAA2B;IAKnE;;OAEG;IACU,2BAA2B,CAAC,oBAAoB,EAAE,iCAAiC,EAAE,WAAW,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;IA2BnJ;;;;OAIG;cACa,4BAA4B,IAAI,OAAO,CAAC,4BAA4B,GAAG,IAAI,CAAC;CAG7F"}
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/*---------------------------------------------------------------------------------------------
|
|
3
|
-
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
|
|
4
|
-
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
5
|
-
*--------------------------------------------------------------------------------------------*/
|
|
6
|
-
// Code based on the blog article @ https://authguidance.com
|
|
7
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
-
exports.ElectronAuthorizationRequestHandler = void 0;
|
|
9
|
-
/** @packageDocumentation
|
|
10
|
-
* @module Authentication
|
|
11
|
-
*/
|
|
12
|
-
const open = require("open");
|
|
13
|
-
const core_bentley_1 = require("@itwin/core-bentley");
|
|
14
|
-
const appauth_1 = require("@openid/appauth");
|
|
15
|
-
const node_support_1 = require("@openid/appauth/built/node_support");
|
|
16
|
-
/**
|
|
17
|
-
* Utility to setup a local web server that listens to authorization responses to the browser and make the necessary redirections
|
|
18
|
-
* @internal
|
|
19
|
-
*/
|
|
20
|
-
class ElectronAuthorizationRequestHandler extends appauth_1.AuthorizationRequestHandler {
|
|
21
|
-
/**
|
|
22
|
-
* Constructor
|
|
23
|
-
*/
|
|
24
|
-
constructor(authorizationEvents) {
|
|
25
|
-
super(new appauth_1.BasicQueryStringUtils(), new node_support_1.NodeCrypto());
|
|
26
|
-
this._authorizationPromise = null;
|
|
27
|
-
this._authorizationEvents = authorizationEvents;
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Makes an authorization request on the system browser
|
|
31
|
-
*/
|
|
32
|
-
async performAuthorizationRequest(serviceConfiguration, authRequest) {
|
|
33
|
-
core_bentley_1.Logger.logTrace("electron-auth", "Making authorization request", () => ({ serviceConfiguration, authRequest }));
|
|
34
|
-
// Setup a promise to process the authorization response
|
|
35
|
-
this._authorizationPromise = new Promise((resolve, _reject) => {
|
|
36
|
-
// Wrap the response from the web browser (with the authorization code)
|
|
37
|
-
this._authorizationEvents.onAuthorizationResponse.addOnce((authErrorJson, authResponseJson) => {
|
|
38
|
-
// Resolve the full response including the request
|
|
39
|
-
const authRequestResponse = {
|
|
40
|
-
request: authRequest,
|
|
41
|
-
error: authErrorJson ? new appauth_1.AuthorizationError(authErrorJson) : null,
|
|
42
|
-
response: authResponseJson ? new appauth_1.AuthorizationResponse(authResponseJson) : null,
|
|
43
|
-
};
|
|
44
|
-
resolve(authRequestResponse);
|
|
45
|
-
// Ask the base class to call our completeAuthorizationRequest - this calls the registered notifier to broadcast the event outside of the client
|
|
46
|
-
this.completeAuthorizationRequestIfPossible(); // eslint-disable-line @typescript-eslint/no-floating-promises
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
// Compose the request and invoke in the browser
|
|
50
|
-
const authUrl = this.buildRequestUrl(serviceConfiguration, authRequest);
|
|
51
|
-
await open(authUrl);
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* Checks if an authorization flow can be completed, and completes it.
|
|
55
|
-
* The handler returns a `Promise<AuthorizationRequestResponse>` if ready, or a `Promise<null>`
|
|
56
|
-
* if not ready.
|
|
57
|
-
*/
|
|
58
|
-
async completeAuthorizationRequest() {
|
|
59
|
-
return this._authorizationPromise;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
exports.ElectronAuthorizationRequestHandler = ElectronAuthorizationRequestHandler;
|
|
63
|
-
//# sourceMappingURL=ElectronAuthorizationRequestHandler.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ElectronAuthorizationRequestHandler.js","sourceRoot":"","sources":["../../../src/backend/ElectronAuthorizationRequestHandler.ts"],"names":[],"mappings":";AAAA;;;+FAG+F;AAC/F,4DAA4D;;;AAE5D;;GAEG;AAEH,6BAA6B;AAC7B,sDAA6C;AAC7C,6CAGyB;AACzB,qEAAgE;AAGhE;;;GAGG;AACH,MAAa,mCAAoC,SAAQ,qCAA2B;IAIlF;;OAEG;IACH,YAAmB,mBAAgD;QACjE,KAAK,CAAC,IAAI,+BAAqB,EAAE,EAAE,IAAI,yBAAU,EAAE,CAAC,CAAC;QAP/C,0BAAqB,GAAiD,IAAI,CAAC;QAQjF,IAAI,CAAC,oBAAoB,GAAG,mBAAmB,CAAC;IAClD,CAAC;IAED;;OAEG;IACI,KAAK,CAAC,2BAA2B,CAAC,oBAAuD,EAAE,WAAiC;QACjI,qBAAM,CAAC,QAAQ,CAAC,eAAe,EAAE,8BAA8B,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,oBAAoB,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC;QAEhH,wDAAwD;QACxD,IAAI,CAAC,qBAAqB,GAAG,IAAI,OAAO,CAA+B,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE;YAE1F,uEAAuE;YACvE,IAAI,CAAC,oBAAoB,CAAC,uBAAuB,CAAC,OAAO,CAAC,CAAC,aAA4C,EAAE,gBAAkD,EAAE,EAAE;gBAE7J,kDAAkD;gBAClD,MAAM,mBAAmB,GAAiC;oBACxD,OAAO,EAAE,WAAW;oBACpB,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,IAAI,4BAAkB,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,IAAI;oBACnE,QAAQ,EAAE,gBAAgB,CAAC,CAAC,CAAC,IAAI,+BAAqB,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,IAAI;iBAChF,CAAC;gBACF,OAAO,CAAC,mBAAmB,CAAC,CAAC;gBAE7B,gJAAgJ;gBAChJ,IAAI,CAAC,sCAAsC,EAAE,CAAC,CAAC,8DAA8D;YAC/G,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,gDAAgD;QAChD,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,oBAAoB,EAAE,WAAW,CAAC,CAAC;QACxE,MAAM,IAAI,CAAC,OAAO,CAAC,CAAC;IACtB,CAAC;IAED;;;;OAIG;IACO,KAAK,CAAC,4BAA4B;QAC1C,OAAO,IAAI,CAAC,qBAAqB,CAAC;IACpC,CAAC;CACF;AAlDD,kFAkDC","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n* See LICENSE.md in the project root for license terms and full copyright notice.\r\n*--------------------------------------------------------------------------------------------*/\r\n// Code based on the blog article @ https://authguidance.com\r\n\r\n/** @packageDocumentation\r\n * @module Authentication\r\n */\r\n\r\nimport * as open from \"open\";\r\nimport { Logger } from \"@itwin/core-bentley\";\r\nimport {\r\n AuthorizationError, AuthorizationErrorJson, AuthorizationRequest, AuthorizationRequestHandler, AuthorizationRequestResponse, AuthorizationResponse,\r\n AuthorizationResponseJson, AuthorizationServiceConfiguration, BasicQueryStringUtils,\r\n} from \"@openid/appauth\";\r\nimport { NodeCrypto } from \"@openid/appauth/built/node_support\";\r\nimport { ElectronAuthorizationEvents } from \"./ElectronAuthorizationEvents\";\r\n\r\n/**\r\n * Utility to setup a local web server that listens to authorization responses to the browser and make the necessary redirections\r\n * @internal\r\n */\r\nexport class ElectronAuthorizationRequestHandler extends AuthorizationRequestHandler {\r\n private _authorizationPromise: Promise<AuthorizationRequestResponse> | null = null;\r\n private _authorizationEvents: ElectronAuthorizationEvents;\r\n\r\n /**\r\n * Constructor\r\n */\r\n public constructor(authorizationEvents: ElectronAuthorizationEvents) {\r\n super(new BasicQueryStringUtils(), new NodeCrypto());\r\n this._authorizationEvents = authorizationEvents;\r\n }\r\n\r\n /**\r\n * Makes an authorization request on the system browser\r\n */\r\n public async performAuthorizationRequest(serviceConfiguration: AuthorizationServiceConfiguration, authRequest: AuthorizationRequest): Promise<void> {\r\n Logger.logTrace(\"electron-auth\", \"Making authorization request\", () => ({ serviceConfiguration, authRequest }));\r\n\r\n // Setup a promise to process the authorization response\r\n this._authorizationPromise = new Promise<AuthorizationRequestResponse>((resolve, _reject) => {\r\n\r\n // Wrap the response from the web browser (with the authorization code)\r\n this._authorizationEvents.onAuthorizationResponse.addOnce((authErrorJson: AuthorizationErrorJson | null, authResponseJson: AuthorizationResponseJson | null) => {\r\n\r\n // Resolve the full response including the request\r\n const authRequestResponse: AuthorizationRequestResponse = {\r\n request: authRequest,\r\n error: authErrorJson ? new AuthorizationError(authErrorJson) : null,\r\n response: authResponseJson ? new AuthorizationResponse(authResponseJson) : null,\r\n };\r\n resolve(authRequestResponse);\r\n\r\n // Ask the base class to call our completeAuthorizationRequest - this calls the registered notifier to broadcast the event outside of the client\r\n this.completeAuthorizationRequestIfPossible(); // eslint-disable-line @typescript-eslint/no-floating-promises\r\n });\r\n });\r\n\r\n // Compose the request and invoke in the browser\r\n const authUrl = this.buildRequestUrl(serviceConfiguration, authRequest);\r\n await open(authUrl);\r\n }\r\n\r\n /**\r\n * Checks if an authorization flow can be completed, and completes it.\r\n * The handler returns a `Promise<AuthorizationRequestResponse>` if ready, or a `Promise<null>`\r\n * if not ready.\r\n */\r\n protected async completeAuthorizationRequest(): Promise<AuthorizationRequestResponse | null> {\r\n return this._authorizationPromise;\r\n }\r\n}\r\n"]}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { TokenResponse } from "@openid/appauth";
|
|
2
|
-
/**
|
|
3
|
-
* Utility to store OIDC AppAuth in secure storage
|
|
4
|
-
* @internal
|
|
5
|
-
*/
|
|
6
|
-
export declare class ElectronTokenStore {
|
|
7
|
-
private _appStorageKey;
|
|
8
|
-
constructor(clientId: string);
|
|
9
|
-
private _userName?;
|
|
10
|
-
private getUserName;
|
|
11
|
-
/** Load token if available */
|
|
12
|
-
load(): Promise<TokenResponse | undefined>;
|
|
13
|
-
/** Save token after signin */
|
|
14
|
-
save(tokenResponse: TokenResponse): Promise<void>;
|
|
15
|
-
/** Delete token after signout */
|
|
16
|
-
delete(): Promise<void>;
|
|
17
|
-
}
|
|
18
|
-
//# sourceMappingURL=ElectronTokenStore.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ElectronTokenStore.d.ts","sourceRoot":"","sources":["../../../src/backend/ElectronTokenStore.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,aAAa,EAAqB,MAAM,iBAAiB,CAAC;AAGnE;;;GAGG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,cAAc,CAAS;gBAEZ,QAAQ,EAAE,MAAM;IAInC,OAAO,CAAC,SAAS,CAAC,CAAS;YACb,WAAW;IAMzB,8BAA8B;IACjB,IAAI,IAAI,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC;IAiBvD,8BAA8B;IACjB,IAAI,CAAC,aAAa,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAgB9D,iCAAiC;IACpB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;CAUrC"}
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/*---------------------------------------------------------------------------------------------
|
|
3
|
-
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
|
|
4
|
-
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
5
|
-
*--------------------------------------------------------------------------------------------*/
|
|
6
|
-
// Code based on the blog article @ https://authguidance.com
|
|
7
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
-
exports.ElectronTokenStore = void 0;
|
|
9
|
-
/** @packageDocumentation
|
|
10
|
-
* @module Authentication
|
|
11
|
-
*/
|
|
12
|
-
const OperatingSystemUserName = require("username");
|
|
13
|
-
const appauth_1 = require("@openid/appauth");
|
|
14
|
-
const core_backend_1 = require("@itwin/core-backend");
|
|
15
|
-
/**
|
|
16
|
-
* Utility to store OIDC AppAuth in secure storage
|
|
17
|
-
* @internal
|
|
18
|
-
*/
|
|
19
|
-
class ElectronTokenStore {
|
|
20
|
-
constructor(clientId) {
|
|
21
|
-
this._appStorageKey = `Bentley.iModelJs.OidcTokenStore.${clientId}`;
|
|
22
|
-
}
|
|
23
|
-
async getUserName() {
|
|
24
|
-
if (!this._userName)
|
|
25
|
-
this._userName = await OperatingSystemUserName();
|
|
26
|
-
return this._userName;
|
|
27
|
-
}
|
|
28
|
-
/** Load token if available */
|
|
29
|
-
async load() {
|
|
30
|
-
if (undefined === core_backend_1.IModelHost.platform.KeyTar) // no keytar on Linux yet
|
|
31
|
-
return undefined;
|
|
32
|
-
const userName = await this.getUserName();
|
|
33
|
-
if (!userName)
|
|
34
|
-
return;
|
|
35
|
-
const tokenResponseStr = await core_backend_1.IModelHost.platform.KeyTar.getPassword(this._appStorageKey, userName);
|
|
36
|
-
if (!tokenResponseStr) {
|
|
37
|
-
return undefined;
|
|
38
|
-
}
|
|
39
|
-
const tokenResponseJson = JSON.parse(tokenResponseStr);
|
|
40
|
-
return new appauth_1.TokenResponse(tokenResponseJson);
|
|
41
|
-
}
|
|
42
|
-
/** Save token after signin */
|
|
43
|
-
async save(tokenResponse) {
|
|
44
|
-
if (undefined === core_backend_1.IModelHost.platform.KeyTar) // no keytar on Linux yet
|
|
45
|
-
return;
|
|
46
|
-
const userName = await this.getUserName();
|
|
47
|
-
if (!userName)
|
|
48
|
-
return;
|
|
49
|
-
const tokenResponseObj = new appauth_1.TokenResponse(tokenResponse.toJson()); // Workaround for 'stub received bad data' error on windows - see https://github.com/atom/node-keytar/issues/112
|
|
50
|
-
tokenResponseObj.accessToken = "";
|
|
51
|
-
tokenResponseObj.idToken = "";
|
|
52
|
-
const tokenResponseStr = JSON.stringify(tokenResponseObj.toJson());
|
|
53
|
-
await core_backend_1.IModelHost.platform.KeyTar.setPassword(this._appStorageKey, userName, tokenResponseStr);
|
|
54
|
-
}
|
|
55
|
-
/** Delete token after signout */
|
|
56
|
-
async delete() {
|
|
57
|
-
if (undefined === core_backend_1.IModelHost.platform.KeyTar) // no keytar on Linux yet
|
|
58
|
-
return;
|
|
59
|
-
const userName = await this.getUserName();
|
|
60
|
-
if (!userName)
|
|
61
|
-
return;
|
|
62
|
-
await core_backend_1.IModelHost.platform.KeyTar.deletePassword(this._appStorageKey, userName);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
exports.ElectronTokenStore = ElectronTokenStore;
|
|
66
|
-
//# sourceMappingURL=ElectronTokenStore.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ElectronTokenStore.js","sourceRoot":"","sources":["../../../src/backend/ElectronTokenStore.ts"],"names":[],"mappings":";AAAA;;;+FAG+F;AAC/F,4DAA4D;;;AAE5D;;GAEG;AAEH,oDAAoD;AACpD,6CAAmE;AACnE,sDAAiD;AAEjD;;;GAGG;AACH,MAAa,kBAAkB;IAG7B,YAAmB,QAAgB;QACjC,IAAI,CAAC,cAAc,GAAG,mCAAmC,QAAQ,EAAE,CAAC;IACtE,CAAC;IAGO,KAAK,CAAC,WAAW;QACvB,IAAI,CAAC,IAAI,CAAC,SAAS;YACjB,IAAI,CAAC,SAAS,GAAG,MAAM,uBAAuB,EAAE,CAAC;QACnD,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;IAED,8BAA8B;IACvB,KAAK,CAAC,IAAI;QACf,IAAI,SAAS,KAAK,yBAAU,CAAC,QAAQ,CAAC,MAAM,EAAE,yBAAyB;YACrE,OAAO,SAAS,CAAC;QAEnB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC;QAC1C,IAAI,CAAC,QAAQ;YACX,OAAO;QAET,MAAM,gBAAgB,GAAG,MAAM,yBAAU,CAAC,QAAQ,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAC;QACrG,IAAI,CAAC,gBAAgB,EAAE;YACrB,OAAO,SAAS,CAAC;SAClB;QAED,MAAM,iBAAiB,GAAG,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAsB,CAAC;QAC5E,OAAO,IAAI,uBAAa,CAAC,iBAAiB,CAAC,CAAC;IAC9C,CAAC;IAED,8BAA8B;IACvB,KAAK,CAAC,IAAI,CAAC,aAA4B;QAC5C,IAAI,SAAS,KAAK,yBAAU,CAAC,QAAQ,CAAC,MAAM,EAAE,yBAAyB;YACrE,OAAO;QAET,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC;QAC1C,IAAI,CAAC,QAAQ;YACX,OAAO;QAET,MAAM,gBAAgB,GAAG,IAAI,uBAAa,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,gHAAgH;QACpL,gBAAgB,CAAC,WAAW,GAAG,EAAE,CAAC;QAClC,gBAAgB,CAAC,OAAO,GAAG,EAAE,CAAC;QAE9B,MAAM,gBAAgB,GAAG,IAAI,CAAC,SAAS,CAAC,gBAAgB,CAAC,MAAM,EAAE,CAAC,CAAC;QACnE,MAAM,yBAAU,CAAC,QAAQ,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,cAAc,EAAE,QAAQ,EAAE,gBAAgB,CAAC,CAAC;IAChG,CAAC;IAED,iCAAiC;IAC1B,KAAK,CAAC,MAAM;QACjB,IAAI,SAAS,KAAK,yBAAU,CAAC,QAAQ,CAAC,MAAM,EAAE,yBAAyB;YACrE,OAAO;QAET,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC;QAC1C,IAAI,CAAC,QAAQ;YACX,OAAO;QAET,MAAM,yBAAU,CAAC,QAAQ,CAAC,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAC;IACjF,CAAC;CACF;AA5DD,gDA4DC","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n* See LICENSE.md in the project root for license terms and full copyright notice.\r\n*--------------------------------------------------------------------------------------------*/\r\n// Code based on the blog article @ https://authguidance.com\r\n\r\n/** @packageDocumentation\r\n * @module Authentication\r\n */\r\n\r\nimport * as OperatingSystemUserName from \"username\";\r\nimport { TokenResponse, TokenResponseJson } from \"@openid/appauth\";\r\nimport { IModelHost } from \"@itwin/core-backend\";\r\n\r\n/**\r\n * Utility to store OIDC AppAuth in secure storage\r\n * @internal\r\n */\r\nexport class ElectronTokenStore {\r\n private _appStorageKey: string;\r\n\r\n public constructor(clientId: string) {\r\n this._appStorageKey = `Bentley.iModelJs.OidcTokenStore.${clientId}`;\r\n }\r\n\r\n private _userName?: string; // Cached user name\r\n private async getUserName(): Promise<string | undefined> {\r\n if (!this._userName)\r\n this._userName = await OperatingSystemUserName();\r\n return this._userName;\r\n }\r\n\r\n /** Load token if available */\r\n public async load(): Promise<TokenResponse | undefined> {\r\n if (undefined === IModelHost.platform.KeyTar) // no keytar on Linux yet\r\n return undefined;\r\n\r\n const userName = await this.getUserName();\r\n if (!userName)\r\n return;\r\n\r\n const tokenResponseStr = await IModelHost.platform.KeyTar.getPassword(this._appStorageKey, userName);\r\n if (!tokenResponseStr) {\r\n return undefined;\r\n }\r\n\r\n const tokenResponseJson = JSON.parse(tokenResponseStr) as TokenResponseJson;\r\n return new TokenResponse(tokenResponseJson);\r\n }\r\n\r\n /** Save token after signin */\r\n public async save(tokenResponse: TokenResponse): Promise<void> {\r\n if (undefined === IModelHost.platform.KeyTar) // no keytar on Linux yet\r\n return;\r\n\r\n const userName = await this.getUserName();\r\n if (!userName)\r\n return;\r\n\r\n const tokenResponseObj = new TokenResponse(tokenResponse.toJson()); // Workaround for 'stub received bad data' error on windows - see https://github.com/atom/node-keytar/issues/112\r\n tokenResponseObj.accessToken = \"\";\r\n tokenResponseObj.idToken = \"\";\r\n\r\n const tokenResponseStr = JSON.stringify(tokenResponseObj.toJson());\r\n await IModelHost.platform.KeyTar.setPassword(this._appStorageKey, userName, tokenResponseStr);\r\n }\r\n\r\n /** Delete token after signout */\r\n public async delete(): Promise<void> {\r\n if (undefined === IModelHost.platform.KeyTar) // no keytar on Linux yet\r\n return;\r\n\r\n const userName = await this.getUserName();\r\n if (!userName)\r\n return;\r\n\r\n await IModelHost.platform.KeyTar.deletePassword(this._appStorageKey, userName);\r\n }\r\n}\r\n"]}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { NativeAppAuthorizationConfiguration } from "@itwin/core-common";
|
|
2
|
-
import { ElectronAuthorizationEvents } from "./ElectronAuthorizationEvents";
|
|
3
|
-
/**
|
|
4
|
-
* Web server to listen to authorization requests/responses for the DesktopAuthorizationClient
|
|
5
|
-
* @internal
|
|
6
|
-
*/
|
|
7
|
-
export declare class LoopbackWebServer {
|
|
8
|
-
private static _httpServer?;
|
|
9
|
-
private static _authState;
|
|
10
|
-
/** Start a web server to listen to the browser requests */
|
|
11
|
-
static start(clientConfiguration: NativeAppAuthorizationConfiguration): void;
|
|
12
|
-
/** Add to the authorization state so that the correct response data is used for each request */
|
|
13
|
-
static addCorrelationState(state: string, authEvents: ElectronAuthorizationEvents): void;
|
|
14
|
-
/** Stop the web server after the authorization was completed */
|
|
15
|
-
private static stop;
|
|
16
|
-
/** Listen/Handle browser events */
|
|
17
|
-
private static onBrowserRequest;
|
|
18
|
-
}
|
|
19
|
-
//# sourceMappingURL=LoopbackWebServer.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"LoopbackWebServer.d.ts","sourceRoot":"","sources":["../../../src/backend/LoopbackWebServer.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,mCAAmC,EAAE,MAAM,oBAAoB,CAAC;AAEzE,OAAO,EAAE,2BAA2B,EAAE,MAAM,+BAA+B,CAAC;AA2B5E;;;GAGG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC,CAAc;IACzC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAgD;IAEzE,2DAA2D;WAC7C,KAAK,CAAC,mBAAmB,EAAE,mCAAmC;IAS5E,gGAAgG;WAClF,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,2BAA2B,GAAG,IAAI;IAI/F,gEAAgE;IAChE,OAAO,CAAC,MAAM,CAAC,IAAI;IAOnB,mCAAmC;IACnC,OAAO,CAAC,MAAM,CAAC,gBAAgB;CA4ChC"}
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/*---------------------------------------------------------------------------------------------
|
|
3
|
-
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
|
|
4
|
-
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
5
|
-
*--------------------------------------------------------------------------------------------*/
|
|
6
|
-
// Code based on the blog article @ https://authguidance.com
|
|
7
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
-
exports.LoopbackWebServer = void 0;
|
|
9
|
-
/** @packageDocumentation
|
|
10
|
-
* @module Authentication
|
|
11
|
-
*/
|
|
12
|
-
const Http = require("http");
|
|
13
|
-
const ElectronAuthorizationBackend_1 = require("./ElectronAuthorizationBackend");
|
|
14
|
-
/** Utility to manage re-entrancy if there are multiple login attempts */
|
|
15
|
-
class AuthorizationState {
|
|
16
|
-
addState(state, authEvents) {
|
|
17
|
-
AuthorizationState._stateEventsMap.push([state, authEvents]);
|
|
18
|
-
}
|
|
19
|
-
removeState(state) {
|
|
20
|
-
AuthorizationState._stateEventsMap = AuthorizationState._stateEventsMap.filter((se) => se[0] !== state);
|
|
21
|
-
}
|
|
22
|
-
// Get events for a received login response
|
|
23
|
-
getEvents(state) {
|
|
24
|
-
const stateEventsPair = AuthorizationState._stateEventsMap.find((se) => se[0] === state);
|
|
25
|
-
if (stateEventsPair) {
|
|
26
|
-
return stateEventsPair[1];
|
|
27
|
-
}
|
|
28
|
-
return null;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
AuthorizationState._stateEventsMap = [];
|
|
32
|
-
/**
|
|
33
|
-
* Web server to listen to authorization requests/responses for the DesktopAuthorizationClient
|
|
34
|
-
* @internal
|
|
35
|
-
*/
|
|
36
|
-
class LoopbackWebServer {
|
|
37
|
-
/** Start a web server to listen to the browser requests */
|
|
38
|
-
static start(clientConfiguration) {
|
|
39
|
-
var _a;
|
|
40
|
-
if (LoopbackWebServer._httpServer)
|
|
41
|
-
return;
|
|
42
|
-
LoopbackWebServer._httpServer = Http.createServer(LoopbackWebServer.onBrowserRequest);
|
|
43
|
-
const urlParts = new URL((_a = clientConfiguration.redirectUri) !== null && _a !== void 0 ? _a : ElectronAuthorizationBackend_1.ElectronAuthorizationBackend.defaultRedirectUri);
|
|
44
|
-
LoopbackWebServer._httpServer.listen(urlParts.port);
|
|
45
|
-
}
|
|
46
|
-
/** Add to the authorization state so that the correct response data is used for each request */
|
|
47
|
-
static addCorrelationState(state, authEvents) {
|
|
48
|
-
return LoopbackWebServer._authState.addState(state, authEvents);
|
|
49
|
-
}
|
|
50
|
-
/** Stop the web server after the authorization was completed */
|
|
51
|
-
static stop() {
|
|
52
|
-
if (!LoopbackWebServer._httpServer)
|
|
53
|
-
return;
|
|
54
|
-
LoopbackWebServer._httpServer.close();
|
|
55
|
-
LoopbackWebServer._httpServer = undefined;
|
|
56
|
-
}
|
|
57
|
-
/** Listen/Handle browser events */
|
|
58
|
-
static onBrowserRequest(httpRequest, httpResponse) {
|
|
59
|
-
if (!httpRequest.url)
|
|
60
|
-
return;
|
|
61
|
-
// Parse the request URL to determine the authorization code, state and errors if any
|
|
62
|
-
const redirectedUrl = new URL(httpRequest.url, ElectronAuthorizationBackend_1.ElectronAuthorizationBackend.defaultRedirectUri);
|
|
63
|
-
const searchParams = redirectedUrl.searchParams;
|
|
64
|
-
const state = searchParams.get("state") || undefined;
|
|
65
|
-
const code = searchParams.get("code");
|
|
66
|
-
const error = searchParams.get("error");
|
|
67
|
-
if (!state) {
|
|
68
|
-
// ignore irrelevant requests (e.g. favicon.ico)
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
// Look up context for the corresponding outgoing request
|
|
72
|
-
const authorizationEvents = LoopbackWebServer._authState.getEvents(state);
|
|
73
|
-
if (!authorizationEvents)
|
|
74
|
-
return;
|
|
75
|
-
// Notify listeners of the code response or error
|
|
76
|
-
let authorizationResponse = null;
|
|
77
|
-
let authorizationError = null;
|
|
78
|
-
if (error) {
|
|
79
|
-
const errorUri = searchParams.get("error_uri") || undefined;
|
|
80
|
-
const errorDescription = searchParams.get("error_description") || undefined;
|
|
81
|
-
authorizationError = { error, error_description: errorDescription, error_uri: errorUri, state }; // eslint-disable-line @typescript-eslint/naming-convention
|
|
82
|
-
httpResponse.write("<h1>Sign in error!</h1>"); // TODO: Needs localization
|
|
83
|
-
httpResponse.end();
|
|
84
|
-
}
|
|
85
|
-
else {
|
|
86
|
-
authorizationResponse = { code: code, state };
|
|
87
|
-
httpResponse.writeHead(200, { "Content-Type": "text/html" }); // eslint-disable-line @typescript-eslint/naming-convention
|
|
88
|
-
httpResponse.write("<h1>Sign in was successful!</h1>You can close this browser window and return to the application"); // TODO: Needs localization
|
|
89
|
-
httpResponse.end();
|
|
90
|
-
}
|
|
91
|
-
authorizationEvents.onAuthorizationResponse.raiseEvent(authorizationError, authorizationResponse);
|
|
92
|
-
// Handle the authorization completed event
|
|
93
|
-
authorizationEvents.onAuthorizationResponseCompleted.addOnce((_authCompletedError) => {
|
|
94
|
-
// Stop the web server now that the signin attempt has finished
|
|
95
|
-
LoopbackWebServer.stop();
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
exports.LoopbackWebServer = LoopbackWebServer;
|
|
100
|
-
LoopbackWebServer._authState = new AuthorizationState();
|
|
101
|
-
//# sourceMappingURL=LoopbackWebServer.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"LoopbackWebServer.js","sourceRoot":"","sources":["../../../src/backend/LoopbackWebServer.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n* See LICENSE.md in the project root for license terms and full copyright notice.\r\n*--------------------------------------------------------------------------------------------*/\r\n// Code based on the blog article @ https://authguidance.com\r\n\r\n/** @packageDocumentation\r\n * @module Authentication\r\n */\r\n\r\nimport * as Http from \"http\";\r\nimport { NativeAppAuthorizationConfiguration } from \"@itwin/core-common\";\r\nimport { AuthorizationErrorJson, AuthorizationResponseJson } from \"@openid/appauth\";\r\nimport { ElectronAuthorizationEvents } from \"./ElectronAuthorizationEvents\";\r\nimport { ElectronAuthorizationBackend } from \"./ElectronAuthorizationBackend\";\r\n\r\ntype StateEventsPair = [string, ElectronAuthorizationEvents];\r\n\r\n/** Utility to manage re-entrancy if there are multiple login attempts */\r\nclass AuthorizationState {\r\n private static _stateEventsMap = [] as StateEventsPair[];\r\n\r\n public addState(state: string, authEvents: ElectronAuthorizationEvents): void {\r\n AuthorizationState._stateEventsMap.push([state, authEvents]);\r\n }\r\n\r\n public removeState(state: string): void {\r\n AuthorizationState._stateEventsMap = AuthorizationState._stateEventsMap.filter((se) => se[0] !== state);\r\n }\r\n\r\n // Get events for a received login response\r\n public getEvents(state: string): ElectronAuthorizationEvents | null {\r\n const stateEventsPair = AuthorizationState._stateEventsMap.find((se) => se[0] === state);\r\n if (stateEventsPair) {\r\n return stateEventsPair[1];\r\n }\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Web server to listen to authorization requests/responses for the DesktopAuthorizationClient\r\n * @internal\r\n */\r\nexport class LoopbackWebServer {\r\n private static _httpServer?: Http.Server;\r\n private static _authState: AuthorizationState = new AuthorizationState();\r\n\r\n /** Start a web server to listen to the browser requests */\r\n public static start(clientConfiguration: NativeAppAuthorizationConfiguration) {\r\n if (LoopbackWebServer._httpServer)\r\n return;\r\n\r\n LoopbackWebServer._httpServer = Http.createServer(LoopbackWebServer.onBrowserRequest);\r\n const urlParts: URL = new URL(clientConfiguration.redirectUri ?? ElectronAuthorizationBackend.defaultRedirectUri);\r\n LoopbackWebServer._httpServer.listen(urlParts.port);\r\n }\r\n\r\n /** Add to the authorization state so that the correct response data is used for each request */\r\n public static addCorrelationState(state: string, authEvents: ElectronAuthorizationEvents): void {\r\n return LoopbackWebServer._authState.addState(state, authEvents);\r\n }\r\n\r\n /** Stop the web server after the authorization was completed */\r\n private static stop() {\r\n if (!LoopbackWebServer._httpServer)\r\n return;\r\n LoopbackWebServer._httpServer.close();\r\n LoopbackWebServer._httpServer = undefined;\r\n }\r\n\r\n /** Listen/Handle browser events */\r\n private static onBrowserRequest(httpRequest: Http.IncomingMessage, httpResponse: Http.ServerResponse): void {\r\n if (!httpRequest.url)\r\n return;\r\n\r\n // Parse the request URL to determine the authorization code, state and errors if any\r\n const redirectedUrl = new URL(httpRequest.url, ElectronAuthorizationBackend.defaultRedirectUri);\r\n const searchParams = redirectedUrl.searchParams;\r\n\r\n const state = searchParams.get(\"state\") || undefined;\r\n const code = searchParams.get(\"code\");\r\n const error = searchParams.get(\"error\");\r\n if (!state) {\r\n // ignore irrelevant requests (e.g. favicon.ico)\r\n return;\r\n }\r\n\r\n // Look up context for the corresponding outgoing request\r\n const authorizationEvents = LoopbackWebServer._authState.getEvents(state);\r\n if (!authorizationEvents)\r\n return;\r\n\r\n // Notify listeners of the code response or error\r\n let authorizationResponse: AuthorizationResponseJson | null = null;\r\n let authorizationError: AuthorizationErrorJson | null = null;\r\n if (error) {\r\n const errorUri = searchParams.get(\"error_uri\") || undefined;\r\n const errorDescription = searchParams.get(\"error_description\") || undefined;\r\n authorizationError = { error, error_description: errorDescription, error_uri: errorUri, state }; // eslint-disable-line @typescript-eslint/naming-convention\r\n httpResponse.write(\"<h1>Sign in error!</h1>\"); // TODO: Needs localization\r\n httpResponse.end();\r\n } else {\r\n authorizationResponse = { code: code!, state };\r\n httpResponse.writeHead(200, { \"Content-Type\": \"text/html\" }); // eslint-disable-line @typescript-eslint/naming-convention\r\n httpResponse.write(\"<h1>Sign in was successful!</h1>You can close this browser window and return to the application\"); // TODO: Needs localization\r\n httpResponse.end();\r\n }\r\n authorizationEvents.onAuthorizationResponse.raiseEvent(authorizationError, authorizationResponse);\r\n\r\n // Handle the authorization completed event\r\n authorizationEvents.onAuthorizationResponseCompleted.addOnce((_authCompletedError?: AuthorizationErrorJson) => {\r\n // Stop the web server now that the signin attempt has finished\r\n LoopbackWebServer.stop();\r\n });\r\n }\r\n}\r\n"]}
|