@xh/hoist 64.0.0 → 64.0.3
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 +24 -0
- package/admin/tabs/monitor/MonitorTab.ts +6 -0
- package/admin/tabs/monitor/MonitorTabModel.ts +14 -1
- package/appcontainer/AppContainerModel.ts +2 -2
- package/build/types/admin/tabs/monitor/MonitorTabModel.d.ts +4 -0
- package/build/types/core/types/Types.d.ts +1 -0
- package/build/types/security/BaseOAuthClient.d.ts +72 -48
- package/build/types/security/Token.d.ts +12 -0
- package/build/types/security/authzero/AuthZeroClient.d.ts +24 -10
- package/build/types/security/authzero/index.d.ts +1 -0
- package/build/types/security/msal/MsalClient.d.ts +69 -12
- package/build/types/security/msal/index.d.ts +1 -0
- package/build/types/svc/FetchService.d.ts +5 -5
- package/core/types/Types.ts +1 -0
- package/kit/blueprint/styles.scss +31 -18
- package/package.json +1 -1
- package/security/BaseOAuthClient.ts +177 -165
- package/security/Token.ts +48 -0
- package/security/authzero/AuthZeroClient.ts +109 -88
- package/security/authzero/index.ts +7 -0
- package/security/msal/MsalClient.ts +213 -98
- package/security/msal/index.ts +7 -0
- package/svc/FetchService.ts +14 -11
- package/svc/IdentityService.ts +1 -1
- package/svc/LocalStorageService.ts +1 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/build/types/security/TokenInfo.d.ts +0 -9
- package/security/TokenInfo.ts +0 -42
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 64.0.3 - 2024-05-31
|
|
4
|
+
|
|
5
|
+
### 🐞 Bug Fixes
|
|
6
|
+
|
|
7
|
+
* Restore disabling of Blueprint animations on popovers and tooltips (corrects regression in v63.0.0)
|
|
8
|
+
|
|
9
|
+
### ⚙️ Technical
|
|
10
|
+
|
|
11
|
+
* Adjustments to API of (beta) `BaseOAuthClient`, `MsaClient`, and `AuthZeroClient`.
|
|
12
|
+
|
|
13
|
+
## 64.0.2 - 2024-05-23
|
|
14
|
+
|
|
15
|
+
### ⚙️ Technical
|
|
16
|
+
|
|
17
|
+
* Adjustments to API of (beta) `BaseOAuthClient`.
|
|
18
|
+
* `FetchService.addDefaultHeaders()` now supports async functions.
|
|
19
|
+
|
|
20
|
+
## 64.0.1 - 2024-05-19
|
|
21
|
+
|
|
22
|
+
### ⚙️ Technical
|
|
23
|
+
|
|
24
|
+
* Adjustments to loading of ID Tokens and API of `BaseOAuthClient`. (Note that
|
|
25
|
+
this package remains in Beta for v64 and is subject to change.)
|
|
26
|
+
|
|
3
27
|
## 64.0.0 - 2024-05-17
|
|
4
28
|
|
|
5
29
|
### 💥 Breaking Changes (upgrade difficulty: 🟠 MEDIUM - major Hoist Core = AG Grid updates)
|
|
@@ -13,6 +13,7 @@ import {relativeTimestamp} from '@xh/hoist/cmp/relativetimestamp';
|
|
|
13
13
|
import {creates, hoistCmp, XH} from '@xh/hoist/core';
|
|
14
14
|
import {button} from '@xh/hoist/desktop/cmp/button';
|
|
15
15
|
import {errorMessage} from '@xh/hoist/desktop/cmp/error';
|
|
16
|
+
import {switchInput} from '@xh/hoist/desktop/cmp/input';
|
|
16
17
|
import {panel} from '@xh/hoist/desktop/cmp/panel';
|
|
17
18
|
import {toolbar, toolbarSep} from '@xh/hoist/desktop/cmp/toolbar';
|
|
18
19
|
import {Icon} from '@xh/hoist/icon';
|
|
@@ -75,6 +76,11 @@ const tbar = hoistCmp.factory<MonitorTabModel>(({model}) => {
|
|
|
75
76
|
icon: Icon.gear(),
|
|
76
77
|
onClick: () => (model.showEditorDialog = true)
|
|
77
78
|
}),
|
|
79
|
+
'-',
|
|
80
|
+
switchInput({
|
|
81
|
+
label: 'Show inactive',
|
|
82
|
+
bind: 'showInactive'
|
|
83
|
+
}),
|
|
78
84
|
hbox({
|
|
79
85
|
className: getClassName(!failed),
|
|
80
86
|
items: [
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Copyright © 2024 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
7
|
import {MonitorResults, MonitorStatus} from '@xh/hoist/admin/tabs/monitor/Types';
|
|
8
|
-
import {HoistModel, LoadSpec, managed, XH} from '@xh/hoist/core';
|
|
8
|
+
import {HoistModel, LoadSpec, managed, persist, XH} from '@xh/hoist/core';
|
|
9
9
|
import {Icon} from '@xh/hoist/icon';
|
|
10
10
|
import {action, bindable, computed, makeObservable, observable} from '@xh/hoist/mobx';
|
|
11
11
|
import {Timer} from '@xh/hoist/utils/async';
|
|
@@ -14,10 +14,13 @@ import {pluralize} from '@xh/hoist/utils/js';
|
|
|
14
14
|
import {filter, isEqual, minBy, sortBy} from 'lodash';
|
|
15
15
|
|
|
16
16
|
export class MonitorTabModel extends HoistModel {
|
|
17
|
+
override persistWith = {localStorageKey: 'xhAdminClientMonitorState'};
|
|
18
|
+
|
|
17
19
|
@observable.ref results: MonitorResults[] = [];
|
|
18
20
|
@observable lastRun: number = null;
|
|
19
21
|
@managed timer: Timer = null;
|
|
20
22
|
|
|
23
|
+
@bindable @persist showInactive = false;
|
|
21
24
|
@bindable showEditorDialog = false;
|
|
22
25
|
|
|
23
26
|
@computed
|
|
@@ -55,11 +58,17 @@ export class MonitorTabModel extends HoistModel {
|
|
|
55
58
|
constructor() {
|
|
56
59
|
super();
|
|
57
60
|
makeObservable(this);
|
|
61
|
+
|
|
58
62
|
this.timer = Timer.create({
|
|
59
63
|
runFn: () => this.autoRefreshAsync(),
|
|
60
64
|
interval: 5 * SECONDS,
|
|
61
65
|
delay: true
|
|
62
66
|
});
|
|
67
|
+
|
|
68
|
+
this.addReaction({
|
|
69
|
+
track: () => this.showInactive,
|
|
70
|
+
run: () => this.refreshAsync()
|
|
71
|
+
});
|
|
63
72
|
}
|
|
64
73
|
|
|
65
74
|
override async doLoadAsync(loadSpec: LoadSpec) {
|
|
@@ -88,6 +97,10 @@ export class MonitorTabModel extends HoistModel {
|
|
|
88
97
|
//-------------------
|
|
89
98
|
@action
|
|
90
99
|
private installResults(results: MonitorResults[]) {
|
|
100
|
+
if (!this.showInactive) {
|
|
101
|
+
results = results.filter(it => it.status !== 'INACTIVE');
|
|
102
|
+
}
|
|
103
|
+
|
|
91
104
|
const prevCounts = this.countsByStatus;
|
|
92
105
|
this.results = sortBy(results, 'sortOrder');
|
|
93
106
|
this.lastRun = minBy(results, 'dateComputed')?.dateComputed ?? null;
|
|
@@ -161,7 +161,7 @@ export class AppContainerModel extends HoistModel {
|
|
|
161
161
|
}
|
|
162
162
|
|
|
163
163
|
try {
|
|
164
|
-
await installServicesAsync([FetchService
|
|
164
|
+
await installServicesAsync([FetchService]);
|
|
165
165
|
|
|
166
166
|
// consult (optional) pre-auth init for app
|
|
167
167
|
const modelClass: any = this.appSpec.modelClass;
|
|
@@ -205,7 +205,7 @@ export class AppContainerModel extends HoistModel {
|
|
|
205
205
|
|
|
206
206
|
// Complete initialization process
|
|
207
207
|
this.setAppState('INITIALIZING');
|
|
208
|
-
await installServicesAsync([ConfigService]);
|
|
208
|
+
await installServicesAsync([ConfigService, LocalStorageService]);
|
|
209
209
|
await installServicesAsync(TrackService);
|
|
210
210
|
await installServicesAsync([EnvironmentService, PrefService, JsonBlobService]);
|
|
211
211
|
|
|
@@ -2,9 +2,13 @@ import { MonitorResults, MonitorStatus } from '@xh/hoist/admin/tabs/monitor/Type
|
|
|
2
2
|
import { HoistModel, LoadSpec } from '@xh/hoist/core';
|
|
3
3
|
import { Timer } from '@xh/hoist/utils/async';
|
|
4
4
|
export declare class MonitorTabModel extends HoistModel {
|
|
5
|
+
persistWith: {
|
|
6
|
+
localStorageKey: string;
|
|
7
|
+
};
|
|
5
8
|
results: MonitorResults[];
|
|
6
9
|
lastRun: number;
|
|
7
10
|
timer: Timer;
|
|
11
|
+
showInactive: boolean;
|
|
8
12
|
showEditorDialog: boolean;
|
|
9
13
|
get passed(): number;
|
|
10
14
|
get warned(): number;
|
|
@@ -15,6 +15,7 @@ export type HAlign = 'left' | 'right' | 'center';
|
|
|
15
15
|
export type VAlign = 'top' | 'bottom' | 'center';
|
|
16
16
|
/** Convenience type for common pattern of accepting a single T, or an array of Ts. */
|
|
17
17
|
export type Some<T> = T | T[];
|
|
18
|
+
export type NonEmptyArray<T> = [T, ...T[]];
|
|
18
19
|
export type Thunkable<T> = T | (() => T);
|
|
19
20
|
export type Awaitable<T> = Promise<T> | T;
|
|
20
21
|
/** Convenience type for a "plain", string-keyed object holding any kind of values. */
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
export
|
|
1
|
+
import { HoistBase } from '@xh/hoist/core';
|
|
2
|
+
import { Token, TokenMap } from '@xh/hoist/security/Token';
|
|
3
|
+
export type LoginMethod = 'REDIRECT' | 'POPUP';
|
|
4
|
+
export interface BaseOAuthClientConfig<S> {
|
|
4
5
|
/** Client ID (GUID) of your app registered with your Oauth provider. */
|
|
5
6
|
clientId: string;
|
|
6
7
|
/**
|
|
@@ -15,20 +16,31 @@ export interface BaseOAuthClientConfig {
|
|
|
15
16
|
*/
|
|
16
17
|
postLogoutRedirectUrl?: 'APP_BASE_URL' | string;
|
|
17
18
|
/** The method used for logging in on desktop. Default is 'REDIRECT'. */
|
|
18
|
-
loginMethodDesktop?:
|
|
19
|
+
loginMethodDesktop?: LoginMethod;
|
|
19
20
|
/** The method used for logging in on mobile. Default is 'REDIRECT'. */
|
|
20
|
-
loginMethodMobile?:
|
|
21
|
+
loginMethodMobile?: LoginMethod;
|
|
21
22
|
/**
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
23
|
+
* Governs an optional refresh timer that will work to keep the tokens fresh.
|
|
24
|
+
*
|
|
25
|
+
* A typical refresh will use the underlying provider cache, and should not result in
|
|
26
|
+
* network activity. However, if any token lifetime falls below`autoRefreshSkipCacheSecs`,
|
|
27
|
+
* this client will force a call to the underlying provider to get the token.
|
|
28
|
+
*
|
|
29
|
+
* In order to allow aging tokens to be replaced in a timely manner, this value should be
|
|
30
|
+
* significantly shorter than both the minimum token lifetime that will be
|
|
31
|
+
* returned by the underlying API and `autoRefreshSkipCacheSecs`.
|
|
32
|
+
*
|
|
33
|
+
* Default is -1, disabling this behavior.
|
|
25
34
|
*/
|
|
26
|
-
|
|
35
|
+
autoRefreshSecs?: number;
|
|
27
36
|
/**
|
|
28
|
-
*
|
|
29
|
-
*
|
|
37
|
+
* During auto-refresh, if the remaining lifetime for any token is below this threshold,
|
|
38
|
+
* force the provider to skip the local cache and go directly to the underlying provider for
|
|
39
|
+
* new tokens and refresh tokens.
|
|
40
|
+
*
|
|
41
|
+
* Default is -1, disabling this behavior.
|
|
30
42
|
*/
|
|
31
|
-
|
|
43
|
+
autoRefreshSkipCacheSecs?: number;
|
|
32
44
|
/**
|
|
33
45
|
* Scopes to request - if any - beyond the core `['openid', 'email']` scopes, which
|
|
34
46
|
* this client will always request.
|
|
@@ -44,17 +56,12 @@ export interface BaseOAuthClientConfig {
|
|
|
44
56
|
*
|
|
45
57
|
* Use this map to gain targeted access tokens for different back-end resources.
|
|
46
58
|
*/
|
|
47
|
-
accessTokens?: Record<string,
|
|
48
|
-
/**
|
|
49
|
-
* True to display a warning banner to the user if tokens expire. May be specified as a boolean
|
|
50
|
-
* or a partial banner spec. Defaults to false.
|
|
51
|
-
*/
|
|
52
|
-
expiryWarning?: boolean | Partial<BannerSpec>;
|
|
59
|
+
accessTokens?: Record<string, S>;
|
|
53
60
|
}
|
|
54
61
|
/**
|
|
55
62
|
* Implementations of this class coordinate OAuth-based login and token provision. Apps can use a
|
|
56
|
-
* suitable concrete implementation to power a client-side OauthService
|
|
57
|
-
*
|
|
63
|
+
* suitable concrete implementation to power a client-side OauthService. See `MsalClient` and
|
|
64
|
+
* `AuthZeroClient`
|
|
58
65
|
*
|
|
59
66
|
* Initialize such a service and this client within the `preAuthInitAsync()` lifecycle method of
|
|
60
67
|
* `AppModel` to use the tokens it acquires to authenticate with the Hoist server. (Note this
|
|
@@ -62,42 +69,62 @@ export interface BaseOAuthClientConfig {
|
|
|
62
69
|
* actually resolve the user.) On init, the client implementation will initiate a pop-up or redirect
|
|
63
70
|
* flow as necessary.
|
|
64
71
|
*/
|
|
65
|
-
export declare abstract class BaseOAuthClient<
|
|
72
|
+
export declare abstract class BaseOAuthClient<C extends BaseOAuthClientConfig<S>, S> extends HoistBase {
|
|
66
73
|
/** Config loaded from UI server + init method. */
|
|
67
|
-
protected config:
|
|
68
|
-
/** Scopes */
|
|
74
|
+
protected config: C;
|
|
75
|
+
/** Id Scopes */
|
|
69
76
|
protected idScopes: string[];
|
|
70
|
-
|
|
71
|
-
protected
|
|
77
|
+
/** Specification for Access Tokens **/
|
|
78
|
+
protected accessSpecs: Record<string, S>;
|
|
72
79
|
private timer;
|
|
73
|
-
private expiryWarningDisplayed;
|
|
74
80
|
private lastRefreshAttempt;
|
|
75
81
|
private TIMER_INTERVAL;
|
|
76
|
-
|
|
77
|
-
* ID token in JWT format. Observable.
|
|
78
|
-
*/
|
|
79
|
-
get idToken(): string;
|
|
80
|
-
/**
|
|
81
|
-
* Get a configured Access token in JWT format. Observable.
|
|
82
|
-
*/
|
|
83
|
-
getAccessToken(key: string): string;
|
|
84
|
-
constructor(config: T);
|
|
82
|
+
constructor(config: C);
|
|
85
83
|
/**
|
|
86
84
|
* Main entry point for this object.
|
|
87
85
|
*/
|
|
88
86
|
initAsync(): Promise<void>;
|
|
87
|
+
/**
|
|
88
|
+
* Request an interactive login with the underlying OAuth provider.
|
|
89
|
+
*/
|
|
90
|
+
loginAsync(method?: LoginMethod): Promise<void>;
|
|
89
91
|
/**
|
|
90
92
|
* Request a full logout from the underlying OAuth provider.
|
|
91
93
|
*/
|
|
92
94
|
logoutAsync(): Promise<void>;
|
|
93
|
-
|
|
95
|
+
/**
|
|
96
|
+
* Get an ID token.
|
|
97
|
+
*/
|
|
98
|
+
getIdTokenAsync(): Promise<Token>;
|
|
99
|
+
/**
|
|
100
|
+
* Get a Access token.
|
|
101
|
+
*/
|
|
102
|
+
getAccessTokenAsync(key: string): Promise<Token>;
|
|
103
|
+
/**
|
|
104
|
+
* Get all available tokens.
|
|
105
|
+
*/
|
|
106
|
+
getAllTokensAsync(): Promise<TokenMap>;
|
|
107
|
+
/**
|
|
108
|
+
* The last authenticated OAuth username.
|
|
109
|
+
*
|
|
110
|
+
* Provided to facilitate more efficient re-login via SSO or otherwise. Cleared on logout.
|
|
111
|
+
* Note: not necessarily a currently authenticated user, and not necessarily the Hoist username.
|
|
112
|
+
*/
|
|
113
|
+
getSelectedUsername(): string;
|
|
114
|
+
/**
|
|
115
|
+
* Set the last authenticated OAuth username.
|
|
116
|
+
* See `getSelectedUsername()`.
|
|
117
|
+
*/
|
|
118
|
+
setSelectedUsername(username: string): void;
|
|
119
|
+
protected abstract doInitAsync(): Promise<TokenMap>;
|
|
120
|
+
protected abstract doLoginPopupAsync(): Promise<void>;
|
|
121
|
+
protected abstract doLoginRedirectAsync(): Promise<void>;
|
|
122
|
+
protected abstract fetchIdTokenAsync(useCache: boolean): Promise<Token>;
|
|
123
|
+
protected abstract fetchAccessTokenAsync(spec: S, useCache: boolean): Promise<Token>;
|
|
94
124
|
protected abstract doLogoutAsync(): Promise<void>;
|
|
95
|
-
protected abstract getIdTokenAsync(useCache: boolean): Promise<string>;
|
|
96
|
-
protected abstract getAccessTokenAsync(spec: PlainObject, useCache: boolean): Promise<string>;
|
|
97
125
|
protected get redirectUrl(): string;
|
|
98
126
|
protected get postLogoutRedirectUrl(): string;
|
|
99
|
-
protected get loginMethod():
|
|
100
|
-
protected get usesRedirect(): boolean;
|
|
127
|
+
protected get loginMethod(): LoginMethod;
|
|
101
128
|
protected get baseUrl(): string;
|
|
102
129
|
protected popupBlockerErrorMessage: String;
|
|
103
130
|
protected defaultErrorMsg: String;
|
|
@@ -114,13 +141,10 @@ export declare abstract class BaseOAuthClient<T extends BaseOAuthClientConfig> e
|
|
|
114
141
|
* @param key - key for re-accessing this state, as round-tripped with redirect.
|
|
115
142
|
*/
|
|
116
143
|
protected restoreRedirectState(key: string): void;
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
* network request to fetch a fresh token.
|
|
122
|
-
*/
|
|
123
|
-
protected loadTokensAsync(useCache?: boolean): Promise<void>;
|
|
144
|
+
protected fetchAllTokensAsync(useCache?: boolean): Promise<TokenMap>;
|
|
145
|
+
protected getLocalStorage(key: string, defaultValue?: any): any;
|
|
146
|
+
protected setLocalStorage(key: string, value: any): void;
|
|
147
|
+
private fetchIdTokenSafeAsync;
|
|
124
148
|
private onTimerAsync;
|
|
125
|
-
private
|
|
149
|
+
private logTokensDebug;
|
|
126
150
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { PlainObject } from '@xh/hoist/core';
|
|
2
|
+
export type TokenMap = Record<string, Token>;
|
|
3
|
+
export declare class Token {
|
|
4
|
+
readonly value: string;
|
|
5
|
+
readonly decoded: PlainObject;
|
|
6
|
+
readonly expiry: number;
|
|
7
|
+
constructor(value: string);
|
|
8
|
+
expiresWithin(interval: number): boolean;
|
|
9
|
+
get formattedExpiry(): string;
|
|
10
|
+
get forLog(): PlainObject;
|
|
11
|
+
equals(other: Token): boolean;
|
|
12
|
+
}
|
|
@@ -1,9 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Token, TokenMap } from '@xh/hoist/security/Token';
|
|
2
2
|
import { BaseOAuthClient, BaseOAuthClientConfig } from '../BaseOAuthClient';
|
|
3
|
-
export interface AuthZeroClientConfig extends BaseOAuthClientConfig {
|
|
3
|
+
export interface AuthZeroClientConfig extends BaseOAuthClientConfig<AuthZeroTokenSpec> {
|
|
4
4
|
/** Domain of your app registered with Auth0 */
|
|
5
5
|
domain: string;
|
|
6
|
-
|
|
6
|
+
}
|
|
7
|
+
export interface AuthZeroTokenSpec {
|
|
8
|
+
/** Scopes for the desired access token.*/
|
|
9
|
+
scopes: string[];
|
|
10
|
+
/**
|
|
11
|
+
* Audience (i.e. API) identifier for AccessToken. Must be registered with Auth0.
|
|
12
|
+
*
|
|
13
|
+
* Note that this is required to ensure that issued token is a JWT and not
|
|
14
|
+
* an opaque string.
|
|
15
|
+
*/
|
|
7
16
|
audience: string;
|
|
8
17
|
}
|
|
9
18
|
/**
|
|
@@ -11,15 +20,20 @@ export interface AuthZeroClientConfig extends BaseOAuthClientConfig {
|
|
|
11
20
|
* via Google, GitHub, Microsoft, and various other OAuth providers *or* via a username/password
|
|
12
21
|
* combo stored and managed within Auth0's own database. Supported options will depend on the
|
|
13
22
|
* configuration of your Auth0 app.
|
|
23
|
+
*
|
|
24
|
+
* Note: If developing on localhost and using Access Tokens will need to configure your browser to
|
|
25
|
+
* allow third-party cookies.
|
|
14
26
|
*/
|
|
15
|
-
export declare class AuthZeroClient extends BaseOAuthClient<AuthZeroClientConfig> {
|
|
27
|
+
export declare class AuthZeroClient extends BaseOAuthClient<AuthZeroClientConfig, AuthZeroTokenSpec> {
|
|
16
28
|
private client;
|
|
17
|
-
doInitAsync(): Promise<
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
29
|
+
protected doInitAsync(): Promise<TokenMap>;
|
|
30
|
+
protected doLoginRedirectAsync(): Promise<void>;
|
|
31
|
+
protected doLoginPopupAsync(): Promise<void>;
|
|
32
|
+
protected fetchIdTokenAsync(useCache?: boolean): Promise<Token>;
|
|
33
|
+
protected fetchAccessTokenAsync(spec: AuthZeroTokenSpec, useCache?: boolean): Promise<Token>;
|
|
34
|
+
protected doLogoutAsync(): Promise<void>;
|
|
21
35
|
private createClient;
|
|
22
|
-
private completeViaRedirectAsync;
|
|
23
|
-
private completeViaPopupAsync;
|
|
24
36
|
private get loginScope();
|
|
37
|
+
private returningFromRedirect;
|
|
38
|
+
private noteUserAuthenticatedAsync;
|
|
25
39
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './AuthZeroClient';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { LogLevel } from '@azure/msal-
|
|
2
|
-
import {
|
|
1
|
+
import { LogLevel } from '@azure/msal-browser';
|
|
2
|
+
import { Token, TokenMap } from '@xh/hoist/security/Token';
|
|
3
3
|
import { BaseOAuthClient, BaseOAuthClientConfig } from '../BaseOAuthClient';
|
|
4
|
-
export interface MsalClientConfig extends BaseOAuthClientConfig {
|
|
4
|
+
export interface MsalClientConfig extends BaseOAuthClientConfig<MsalTokenSpec> {
|
|
5
5
|
/** Tenant ID (GUID) of your organization */
|
|
6
6
|
tenantId: string;
|
|
7
7
|
/**
|
|
@@ -10,23 +10,80 @@ export interface MsalClientConfig extends BaseOAuthClientConfig {
|
|
|
10
10
|
* MSAL Browser Lib defaults authority to "https://login.microsoftonline.com/common"
|
|
11
11
|
*/
|
|
12
12
|
authority?: string;
|
|
13
|
-
/**
|
|
13
|
+
/**
|
|
14
|
+
* A hint about the tenant or domain that the user should use to sign in.
|
|
15
|
+
* The value of the domain hint is a registered domain for the tenant.
|
|
16
|
+
*/
|
|
17
|
+
domainHint?: string;
|
|
18
|
+
/**
|
|
19
|
+
* If specified, the client will use this value when initializing the app to enforce a minimum
|
|
20
|
+
* amount of time during which no further auth flow with the provider should be necessary.
|
|
21
|
+
*
|
|
22
|
+
* Use this argument to front-load any necessary auth flow to the apps initialization stage
|
|
23
|
+
* thereby minimizing disruption to user activity during application use.
|
|
24
|
+
*
|
|
25
|
+
* This value may be set to anything up to 86400 (24 hours), the maximum lifetime
|
|
26
|
+
* of an Azure refresh token. Set to -1 to disable (default).
|
|
27
|
+
*
|
|
28
|
+
* Note that setting to *any* non-disabled amount will require the app to do *some* communication
|
|
29
|
+
* with the login provider at *every* app load. This may just involve loading new tokens via
|
|
30
|
+
* fetch, however, setting to higher values will increase the frequency with which
|
|
31
|
+
* a new refresh token will also need to be requested via a hidden iframe/redirect/popup. This
|
|
32
|
+
* can be time-consuming and potentially disruptive and applications should therefore use with
|
|
33
|
+
* care and typically set to some value significantly less than the max.
|
|
34
|
+
*
|
|
35
|
+
* See https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/token-lifetimes.md
|
|
36
|
+
*/
|
|
37
|
+
initRefreshTokenExpirationOffsetSecs?: number;
|
|
38
|
+
/** The log level of MSAL. Default is LogLevel.Warning. */
|
|
14
39
|
msalLogLevel?: LogLevel;
|
|
15
40
|
}
|
|
41
|
+
export interface MsalTokenSpec {
|
|
42
|
+
/** Scopes for the desired access token. */
|
|
43
|
+
scopes: string[];
|
|
44
|
+
/**
|
|
45
|
+
* Scopes to be added to the scopes requested during interactive and SSO logins.
|
|
46
|
+
* See the `scopes` property on `PopupRequest`, `RedirectRequest`, and `SSORequest`
|
|
47
|
+
* for more info.
|
|
48
|
+
*/
|
|
49
|
+
loginScopes?: string[];
|
|
50
|
+
/**
|
|
51
|
+
* Scopes to be added to the scopes requested during interactive and SSO login.
|
|
52
|
+
*
|
|
53
|
+
* See the `extraScopesToConsent` property on `PopupRequest`, `RedirectRequest`, and
|
|
54
|
+
* `SSORequest` for more info.
|
|
55
|
+
*/
|
|
56
|
+
extraScopesToConsent?: string[];
|
|
57
|
+
}
|
|
16
58
|
/**
|
|
17
59
|
* Service to implement OAuth authentication via MSAL.
|
|
60
|
+
*
|
|
61
|
+
* See the following helpful information relevant to our use of this tricky API --
|
|
62
|
+
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/
|
|
63
|
+
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/token-lifetimes.md
|
|
64
|
+
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/login-user.md
|
|
65
|
+
*
|
|
66
|
+
* TODO: The handling of `ssoSilent` and `initRefreshTokenExpirationOffsetSecs` in this library
|
|
67
|
+
* require 3rd party cookies to be enabled in the browser so that MSAL can load contact in a
|
|
68
|
+
* hidden iFrame If its *not* enabled, we may be doing extra work. Consider checking 3rd party
|
|
69
|
+
* cookie support and adding conditional behavior?
|
|
18
70
|
*/
|
|
19
|
-
export declare class MsalClient extends BaseOAuthClient<MsalClientConfig> {
|
|
71
|
+
export declare class MsalClient extends BaseOAuthClient<MsalClientConfig, MsalTokenSpec> {
|
|
20
72
|
private client;
|
|
21
73
|
private account;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
74
|
+
private initialTokenLoad;
|
|
75
|
+
constructor(config: MsalClientConfig);
|
|
76
|
+
protected doInitAsync(): Promise<TokenMap>;
|
|
77
|
+
protected doLoginPopupAsync(): Promise<void>;
|
|
78
|
+
protected doLoginRedirectAsync(): Promise<void>;
|
|
79
|
+
protected fetchIdTokenAsync(useCache?: boolean): Promise<Token>;
|
|
80
|
+
protected fetchAccessTokenAsync(spec: MsalTokenSpec, useCache?: boolean): Promise<Token>;
|
|
81
|
+
protected doLogoutAsync(): Promise<void>;
|
|
82
|
+
private loginSsoAsync;
|
|
26
83
|
private createClientAsync;
|
|
27
|
-
private completeViaRedirectAsync;
|
|
28
|
-
private completeViaPopupAsync;
|
|
29
84
|
private logFromMsal;
|
|
30
85
|
private get loginScopes();
|
|
31
|
-
private get
|
|
86
|
+
private get loginExtraScopesToConsent();
|
|
87
|
+
private get refreshOffsetArgs();
|
|
88
|
+
private noteUserAuthenticated;
|
|
32
89
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './MsalClient';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FetchResponse, HoistService, LoadSpec, PlainObject } from '@xh/hoist/core';
|
|
1
|
+
import { Awaitable, FetchResponse, HoistService, LoadSpec, PlainObject } from '@xh/hoist/core';
|
|
2
2
|
import { PromiseTimeoutSpec } from '@xh/hoist/promise';
|
|
3
3
|
import { StatusCodes } from 'http-status-codes';
|
|
4
4
|
import { IStringifyOptions } from 'qs';
|
|
@@ -26,19 +26,19 @@ export declare class FetchService extends HoistService {
|
|
|
26
26
|
static instance: FetchService;
|
|
27
27
|
NO_JSON_RESPONSES: StatusCodes[];
|
|
28
28
|
private autoAborters;
|
|
29
|
-
defaultHeaders: (PlainObject | ((arg: FetchOptions) => PlainObject))[];
|
|
29
|
+
defaultHeaders: (PlainObject | ((arg: FetchOptions) => Awaitable<PlainObject>))[];
|
|
30
30
|
defaultTimeout: any;
|
|
31
31
|
/**
|
|
32
32
|
* Set default headers to be sent with all subsequent requests.
|
|
33
33
|
* @param headers - to be sent with all fetch requests, or a function to generate.
|
|
34
34
|
* @deprecated use addDefaultHeaders instead.
|
|
35
35
|
*/
|
|
36
|
-
setDefaultHeaders(headers: PlainObject | ((arg: FetchOptions) => PlainObject)): void;
|
|
36
|
+
setDefaultHeaders(headers: PlainObject | ((arg: FetchOptions) => Awaitable<PlainObject>)): void;
|
|
37
37
|
/**
|
|
38
38
|
* Add default headers to be sent with all subsequent requests.
|
|
39
39
|
* @param headers - to be sent with all fetch requests, or a function to generate.
|
|
40
40
|
*/
|
|
41
|
-
addDefaultHeaders(headers: PlainObject | ((arg: FetchOptions) => PlainObject)): void;
|
|
41
|
+
addDefaultHeaders(headers: PlainObject | ((arg: FetchOptions) => Awaitable<PlainObject>)): void;
|
|
42
42
|
/**
|
|
43
43
|
* Set the timeout (default 30 seconds) to be used for all requests made via this service that
|
|
44
44
|
* do not themselves spec a custom timeout.
|
|
@@ -84,7 +84,7 @@ export declare class FetchService extends HoistService {
|
|
|
84
84
|
* @returns false if no request pending for the given key.
|
|
85
85
|
*/
|
|
86
86
|
abort(autoAbortKey: string): boolean;
|
|
87
|
-
private
|
|
87
|
+
private withDefaultsAsync;
|
|
88
88
|
private managedFetchAsync;
|
|
89
89
|
private fetchInternalAsync;
|
|
90
90
|
private sendJsonInternalAsync;
|
package/core/types/Types.ts
CHANGED
|
@@ -26,6 +26,7 @@ export type VAlign = 'top' | 'bottom' | 'center';
|
|
|
26
26
|
|
|
27
27
|
/** Convenience type for common pattern of accepting a single T, or an array of Ts. */
|
|
28
28
|
export type Some<T> = T | T[];
|
|
29
|
+
export type NonEmptyArray<T> = [T, ...T[]];
|
|
29
30
|
|
|
30
31
|
export type Thunkable<T> = T | (() => T);
|
|
31
32
|
export type Awaitable<T> = Promise<T> | T;
|
|
@@ -57,24 +57,37 @@
|
|
|
57
57
|
background-color: var(--xh-backdrop-bg);
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
//
|
|
61
|
-
//
|
|
62
|
-
//
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
.
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
60
|
+
// On Popover and Tooltip components, squelch scale & opacity transition - unlike Dialog with its `transitionName` prop,
|
|
61
|
+
// Popover, and consequently Tooltip, do not appear to provide a way to turn off
|
|
62
|
+
// their transitions without also losing the (often desirable) arrow pointing to their targets.
|
|
63
|
+
// These rules override the scale and opacity starting values of enter and exit classes by setting them to
|
|
64
|
+
// their final 'end of transition' values, which has the effect of preventing/short circuiting transitions.
|
|
65
|
+
// BlueprintJs' own Popover.minimal uses this same approach to disable its transitions.
|
|
66
|
+
// (it appears from testing that overriding these rules with any value will squelch the transition, but
|
|
67
|
+
// we might as well use the final values to be explicit.)
|
|
68
|
+
.bp5-popover-transition-container {
|
|
69
|
+
&.bp5-popover-enter,
|
|
70
|
+
&.bp5-popover-appear {
|
|
71
|
+
opacity: 1;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
&.bp5-popover-exit {
|
|
75
|
+
opacity: 0;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
&.bp5-popover-enter,
|
|
79
|
+
&.bp5-popover-appear {
|
|
80
|
+
> .bp5-popover {
|
|
81
|
+
transform: scale(1);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
&.bp5-popover-exit-active,
|
|
86
|
+
&.bp5-popover-exit {
|
|
87
|
+
> .bp5-popover {
|
|
88
|
+
transform: scale(0);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
78
91
|
}
|
|
79
92
|
|
|
80
93
|
//------------------------
|