@equinor/fusion-framework-module-msal 7.2.0 → 7.2.2
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 +31 -0
- package/dist/esm/MsalProvider.js +50 -22
- package/dist/esm/MsalProvider.js.map +1 -1
- package/dist/esm/module.js.map +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/MsalProvider.d.ts +9 -3
- package/dist/types/MsalProvider.interface.d.ts +2 -2
- package/dist/types/module.d.ts +3 -20
- package/dist/types/version.d.ts +1 -1
- package/package.json +3 -3
- package/src/MsalProvider.interface.ts +6 -2
- package/src/MsalProvider.ts +67 -28
- package/src/module.ts +5 -18
- package/src/version.ts +1 -1
|
@@ -3,7 +3,7 @@ import { TelemetryLevel } from '@equinor/fusion-framework-module-telemetry';
|
|
|
3
3
|
import { BaseModuleProvider } from '@equinor/fusion-framework-module/provider';
|
|
4
4
|
import type { MsalConfig } from './MsalConfigurator';
|
|
5
5
|
import type { AcquireTokenOptionsLegacy, IMsalProvider } from './MsalProvider.interface';
|
|
6
|
-
import type { AcquireTokenResult, IMsalClient, LoginOptions, LoginResult, LogoutOptions } from './MsalClient.interface';
|
|
6
|
+
import type { AcquireTokenOptions, AcquireTokenResult, IMsalClient, LoginOptions, LoginResult, LogoutOptions } from './MsalClient.interface';
|
|
7
7
|
import type { AccountInfo, AuthenticationResult } from './types';
|
|
8
8
|
import type { MsalModuleVersion } from './static';
|
|
9
9
|
export type { IMsalProvider };
|
|
@@ -33,6 +33,12 @@ export type { IMsalProvider };
|
|
|
33
33
|
*/
|
|
34
34
|
export declare class MsalProvider extends BaseModuleProvider<MsalConfig> implements IMsalProvider {
|
|
35
35
|
#private;
|
|
36
|
+
/**
|
|
37
|
+
* Default OAuth scopes used when the caller provides no scopes.
|
|
38
|
+
*
|
|
39
|
+
* Resolves to the app's Entra ID configured permissions via the `/.default` scope.
|
|
40
|
+
*/
|
|
41
|
+
get defaultScopes(): string[];
|
|
36
42
|
/**
|
|
37
43
|
* The MSAL module version enum value indicating the API compatibility level.
|
|
38
44
|
*
|
|
@@ -106,7 +112,7 @@ export declare class MsalProvider extends BaseModuleProvider<MsalConfig> impleme
|
|
|
106
112
|
* }
|
|
107
113
|
* ```
|
|
108
114
|
*/
|
|
109
|
-
acquireAccessToken(options
|
|
115
|
+
acquireAccessToken(options?: AcquireTokenOptions | AcquireTokenOptionsLegacy): Promise<string | undefined>;
|
|
110
116
|
/**
|
|
111
117
|
* Acquire full authentication result for the specified scopes
|
|
112
118
|
*
|
|
@@ -136,7 +142,7 @@ export declare class MsalProvider extends BaseModuleProvider<MsalConfig> impleme
|
|
|
136
142
|
* });
|
|
137
143
|
* ```
|
|
138
144
|
*/
|
|
139
|
-
acquireToken(options
|
|
145
|
+
acquireToken(options?: AcquireTokenOptions | AcquireTokenOptionsLegacy): Promise<AcquireTokenResult>;
|
|
140
146
|
/**
|
|
141
147
|
* Authenticates a user using Microsoft Authentication Library.
|
|
142
148
|
*
|
|
@@ -83,7 +83,7 @@ export interface IMsalProvider extends IProxyProvider {
|
|
|
83
83
|
* });
|
|
84
84
|
* ```
|
|
85
85
|
*/
|
|
86
|
-
acquireAccessToken(options
|
|
86
|
+
acquireAccessToken(options?: AcquireTokenOptions | AcquireTokenOptionsLegacy): Promise<string | undefined>;
|
|
87
87
|
/**
|
|
88
88
|
* Acquires a full authentication result including token and account information.
|
|
89
89
|
*
|
|
@@ -101,7 +101,7 @@ export interface IMsalProvider extends IProxyProvider {
|
|
|
101
101
|
* });
|
|
102
102
|
* ```
|
|
103
103
|
*/
|
|
104
|
-
acquireToken(options
|
|
104
|
+
acquireToken(options?: AcquireTokenOptions | AcquireTokenOptionsLegacy): Promise<AcquireTokenResult>;
|
|
105
105
|
/**
|
|
106
106
|
* Authenticates a user interactively with Microsoft Identity Platform.
|
|
107
107
|
*
|
package/dist/types/module.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { type Module, type IModulesConfigurator } from '@equinor/fusion-framework-module';
|
|
1
|
+
import { type Module, type IModulesConfigurator, type ModuleConfigType } from '@equinor/fusion-framework-module';
|
|
2
2
|
import { MsalConfigurator } from './MsalConfigurator';
|
|
3
3
|
import { type IMsalProvider } from './MsalProvider';
|
|
4
|
-
import type { MsalClientConfig } from './MsalClient';
|
|
5
4
|
/**
|
|
6
5
|
* MSAL authentication module configuration.
|
|
7
6
|
*
|
|
@@ -28,23 +27,7 @@ export declare const module: MsalModule;
|
|
|
28
27
|
* This function receives a builder object with methods to configure the MSAL client
|
|
29
28
|
* and authentication requirements.
|
|
30
29
|
*/
|
|
31
|
-
export type AuthConfigFn = (
|
|
32
|
-
/**
|
|
33
|
-
* Set MSAL client configuration
|
|
34
|
-
* @param config - Client configuration with tenant ID, client ID, etc.
|
|
35
|
-
*/
|
|
36
|
-
setClientConfig: (config: MsalClientConfig) => void;
|
|
37
|
-
/**
|
|
38
|
-
* Set whether authentication is required for the application
|
|
39
|
-
* @param requiresAuth - If true, app will attempt automatic login on initialization
|
|
40
|
-
*/
|
|
41
|
-
setRequiresAuth: (requiresAuth: boolean) => void;
|
|
42
|
-
/**
|
|
43
|
-
* Set a default login hint used for silent SSO and pre-filled usernames
|
|
44
|
-
* @param loginHint - Preferred username/email to use for login hint
|
|
45
|
-
*/
|
|
46
|
-
setLoginHint: (loginHint: string) => void;
|
|
47
|
-
}) => void;
|
|
30
|
+
export type AuthConfigFn<TRef = unknown> = (configurator: ModuleConfigType<MsalModule>, ref?: TRef) => void;
|
|
48
31
|
/**
|
|
49
32
|
* Enables MSAL authentication module in the framework.
|
|
50
33
|
*
|
|
@@ -81,7 +64,7 @@ export declare const enableMSAL: (configurator: IModulesConfigurator<any, any>,
|
|
|
81
64
|
*/
|
|
82
65
|
export declare const configureMsal: (configure: AuthConfigFn) => {
|
|
83
66
|
module: MsalModule;
|
|
84
|
-
configure: AuthConfigFn
|
|
67
|
+
configure: AuthConfigFn<unknown>;
|
|
85
68
|
};
|
|
86
69
|
declare module '@equinor/fusion-framework-module' {
|
|
87
70
|
interface Modules {
|
package/dist/types/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "7.2.
|
|
1
|
+
export declare const version = "7.2.2";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@equinor/fusion-framework-module-msal",
|
|
3
|
-
"version": "7.2.
|
|
3
|
+
"version": "7.2.2",
|
|
4
4
|
"description": "Microsoft Authentication Library (MSAL) integration module for Fusion Framework",
|
|
5
5
|
"main": "dist/esm/index.js",
|
|
6
6
|
"types": "dist/types/index.d.ts",
|
|
@@ -58,8 +58,8 @@
|
|
|
58
58
|
"semver": "^7.5.4",
|
|
59
59
|
"typescript": "^5.8.2",
|
|
60
60
|
"zod": "^4.1.8",
|
|
61
|
-
"@equinor/fusion-framework-module": "^
|
|
62
|
-
"@equinor/fusion-framework-module
|
|
61
|
+
"@equinor/fusion-framework-module-telemetry": "^4.6.3",
|
|
62
|
+
"@equinor/fusion-framework-module": "^5.0.5"
|
|
63
63
|
},
|
|
64
64
|
"peerDependenciesMeta": {
|
|
65
65
|
"@equinor/fusion-framework-module-telemetry": {
|
|
@@ -95,7 +95,9 @@ export interface IMsalProvider extends IProxyProvider {
|
|
|
95
95
|
* });
|
|
96
96
|
* ```
|
|
97
97
|
*/
|
|
98
|
-
acquireAccessToken(
|
|
98
|
+
acquireAccessToken(
|
|
99
|
+
options?: AcquireTokenOptions | AcquireTokenOptionsLegacy,
|
|
100
|
+
): Promise<string | undefined>;
|
|
99
101
|
|
|
100
102
|
/**
|
|
101
103
|
* Acquires a full authentication result including token and account information.
|
|
@@ -114,7 +116,9 @@ export interface IMsalProvider extends IProxyProvider {
|
|
|
114
116
|
* });
|
|
115
117
|
* ```
|
|
116
118
|
*/
|
|
117
|
-
acquireToken(
|
|
119
|
+
acquireToken(
|
|
120
|
+
options?: AcquireTokenOptions | AcquireTokenOptionsLegacy,
|
|
121
|
+
): Promise<AcquireTokenResult>;
|
|
118
122
|
|
|
119
123
|
/**
|
|
120
124
|
* Authenticates a user interactively with Microsoft Identity Platform.
|
package/src/MsalProvider.ts
CHANGED
|
@@ -63,6 +63,16 @@ export class MsalProvider extends BaseModuleProvider<MsalConfig> implements IMsa
|
|
|
63
63
|
#authCode?: string;
|
|
64
64
|
#loginHint?: string;
|
|
65
65
|
|
|
66
|
+
/**
|
|
67
|
+
* Default OAuth scopes used when the caller provides no scopes.
|
|
68
|
+
*
|
|
69
|
+
* Resolves to the app's Entra ID configured permissions via the `/.default` scope.
|
|
70
|
+
*/
|
|
71
|
+
get defaultScopes(): string[] {
|
|
72
|
+
const clientId = this.#client.clientId;
|
|
73
|
+
return clientId ? [`${clientId}/.default`] : [];
|
|
74
|
+
}
|
|
75
|
+
|
|
66
76
|
/**
|
|
67
77
|
* The MSAL module version enum value indicating the API compatibility level.
|
|
68
78
|
*
|
|
@@ -236,9 +246,9 @@ export class MsalProvider extends BaseModuleProvider<MsalConfig> implements IMsa
|
|
|
236
246
|
} else if (!this.#client.hasValidClaims) {
|
|
237
247
|
// Priority 2: No valid session found - attempt automatic login
|
|
238
248
|
// This handles first-time app load when no authentication state exists
|
|
239
|
-
// Note: Using
|
|
249
|
+
// Note: Using default scopes here as we don't know what scopes the app needs yet
|
|
240
250
|
// App should call acquireToken with actual scopes after initialization
|
|
241
|
-
const loginResult = await this.login({ request: { scopes:
|
|
251
|
+
const loginResult = await this.login({ request: { scopes: this.defaultScopes } });
|
|
242
252
|
if (loginResult?.account) {
|
|
243
253
|
// Automatic login successful - set as active account
|
|
244
254
|
this.#client.setActiveAccount(loginResult.account);
|
|
@@ -271,7 +281,9 @@ export class MsalProvider extends BaseModuleProvider<MsalConfig> implements IMsa
|
|
|
271
281
|
* }
|
|
272
282
|
* ```
|
|
273
283
|
*/
|
|
274
|
-
async acquireAccessToken(
|
|
284
|
+
async acquireAccessToken(
|
|
285
|
+
options?: AcquireTokenOptions | AcquireTokenOptionsLegacy,
|
|
286
|
+
): Promise<string | undefined> {
|
|
275
287
|
const { accessToken } = (await this.acquireToken(options)) ?? {};
|
|
276
288
|
return accessToken;
|
|
277
289
|
}
|
|
@@ -305,47 +317,68 @@ export class MsalProvider extends BaseModuleProvider<MsalConfig> implements IMsa
|
|
|
305
317
|
* });
|
|
306
318
|
* ```
|
|
307
319
|
*/
|
|
308
|
-
async acquireToken(
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
320
|
+
async acquireToken(
|
|
321
|
+
options?: AcquireTokenOptions | AcquireTokenOptionsLegacy,
|
|
322
|
+
): Promise<AcquireTokenResult> {
|
|
323
|
+
// Determine behavior and silent options, with defaults (redirect and true respectively)
|
|
324
|
+
const behavior = options?.behavior ?? 'redirect';
|
|
325
|
+
|
|
326
|
+
// Silent mode defaults to true, meaning the provider will attempt silent token acquisition first
|
|
327
|
+
const silent = options?.silent ?? true;
|
|
328
|
+
|
|
329
|
+
const defaultScopes = this.defaultScopes;
|
|
330
|
+
|
|
331
|
+
const inputRequest = options?.request;
|
|
332
|
+
|
|
333
|
+
// Determine the account to use for token acquisition, prioritizing request-specific account, then active account
|
|
334
|
+
const account = inputRequest?.account ?? this.account ?? undefined;
|
|
335
|
+
|
|
336
|
+
// Extract caller-provided scopes from either new format (request.scopes) or legacy format (scopes)
|
|
337
|
+
const candidateScopes =
|
|
338
|
+
inputRequest?.scopes ?? (options as AcquireTokenOptionsLegacy)?.scopes ?? [];
|
|
314
339
|
|
|
315
|
-
const
|
|
316
|
-
|
|
317
|
-
const scopes = options.request?.scopes ?? options?.scopes ?? [];
|
|
340
|
+
const scopes =
|
|
341
|
+
candidateScopes.length > 0 ? candidateScopes : defaultScopes.length > 0 ? defaultScopes : [];
|
|
318
342
|
|
|
343
|
+
// Prepare telemetry properties for this token acquisition attempt
|
|
319
344
|
const telemetryProperties = { behavior, silent, scopes };
|
|
320
345
|
|
|
321
346
|
// Track usage of deprecated legacy scopes format for migration monitoring
|
|
322
|
-
if (options
|
|
347
|
+
if ((options as AcquireTokenOptionsLegacy)?.scopes) {
|
|
323
348
|
this._trackEvent('acquireToken.legacy-scopes-provided', TelemetryLevel.Warning, {
|
|
324
349
|
properties: telemetryProperties,
|
|
325
350
|
});
|
|
326
351
|
}
|
|
327
352
|
|
|
328
353
|
// Handle empty scopes - currently monitoring for telemetry, will throw in future
|
|
329
|
-
if (
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
|
|
354
|
+
if (candidateScopes.length === 0) {
|
|
355
|
+
if (defaultScopes.length > 0) {
|
|
356
|
+
this._trackEvent('acquireToken.missing-scope.defaulted', TelemetryLevel.Warning, {
|
|
357
|
+
properties: { ...telemetryProperties, defaultScopes },
|
|
358
|
+
});
|
|
359
|
+
} else {
|
|
360
|
+
const exception = new Error(
|
|
361
|
+
'Empty scopes provided and clientId is missing for default scope',
|
|
362
|
+
);
|
|
363
|
+
this._trackException('acquireToken.missing-scope', TelemetryLevel.Warning, {
|
|
364
|
+
exception,
|
|
365
|
+
properties: telemetryProperties,
|
|
366
|
+
});
|
|
367
|
+
// TODO: throw exception when sufficient metrics are collected
|
|
368
|
+
// This allows us to monitor how often empty scopes are provided before enforcing validation
|
|
369
|
+
}
|
|
337
370
|
}
|
|
338
371
|
|
|
339
372
|
try {
|
|
340
373
|
const measurement = this._trackMeasurement('acquireToken', TelemetryLevel.Information, {
|
|
341
374
|
properties: telemetryProperties,
|
|
342
375
|
});
|
|
343
|
-
// Merge account, original request options, and resolved scopes
|
|
344
|
-
// Account ensures context awareness, request preserves custom options, scopes uses resolved value
|
|
376
|
+
// Merge account, original request options, and resolved scopes.
|
|
377
|
+
// Account ensures context awareness, request preserves custom options, scopes uses resolved value.
|
|
345
378
|
const result = await this.#client.acquireToken({
|
|
346
379
|
behavior,
|
|
347
380
|
silent,
|
|
348
|
-
request: { ...
|
|
381
|
+
request: { ...inputRequest, account, scopes },
|
|
349
382
|
});
|
|
350
383
|
measurement?.measure();
|
|
351
384
|
return result;
|
|
@@ -411,6 +444,13 @@ export class MsalProvider extends BaseModuleProvider<MsalConfig> implements IMsa
|
|
|
411
444
|
request.loginHint ??=
|
|
412
445
|
this.#loginHint ?? this.account?.username ?? this.account?.loginHint ?? undefined;
|
|
413
446
|
|
|
447
|
+
const defaultScopes = this.defaultScopes;
|
|
448
|
+
|
|
449
|
+
// Fallback to app default scope when possible; empty scopes tracked for monitoring
|
|
450
|
+
if (!request.scopes || request.scopes.length === 0) {
|
|
451
|
+
request.scopes = defaultScopes.length > 0 ? defaultScopes : [];
|
|
452
|
+
}
|
|
453
|
+
|
|
414
454
|
// Determine if silent login is possible based on available account/hint information
|
|
415
455
|
// Silent login requires either an account object or a loginHint to work
|
|
416
456
|
const canLoginSilently = silent && (request.account || request.loginHint);
|
|
@@ -421,10 +461,9 @@ export class MsalProvider extends BaseModuleProvider<MsalConfig> implements IMsa
|
|
|
421
461
|
// This allows silent login to work automatically with existing authentication state
|
|
422
462
|
request.account ??= this.account ?? undefined;
|
|
423
463
|
|
|
424
|
-
//
|
|
425
|
-
//
|
|
426
|
-
if (
|
|
427
|
-
request.scopes = [];
|
|
464
|
+
// If scopes are still empty here, we couldn't derive a default scope (e.g. missing clientId).
|
|
465
|
+
// Track for monitoring; behavior will be enforced once we have sufficient metrics.
|
|
466
|
+
if (request.scopes.length === 0) {
|
|
428
467
|
this._trackEvent('login.missing-scope', TelemetryLevel.Warning, {
|
|
429
468
|
properties: telemetryProperties,
|
|
430
469
|
});
|
package/src/module.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type Module,
|
|
3
3
|
type IModulesConfigurator,
|
|
4
|
+
type ModuleConfigType,
|
|
4
5
|
SemanticVersion,
|
|
5
6
|
} from '@equinor/fusion-framework-module';
|
|
6
7
|
|
|
7
8
|
import { MsalConfigurator } from './MsalConfigurator';
|
|
8
9
|
import { MsalProvider, type IMsalProvider } from './MsalProvider';
|
|
9
|
-
import type { MsalClientConfig } from './MsalClient';
|
|
10
10
|
|
|
11
11
|
import { version } from './version';
|
|
12
12
|
|
|
@@ -80,23 +80,10 @@ export const module: MsalModule = {
|
|
|
80
80
|
* This function receives a builder object with methods to configure the MSAL client
|
|
81
81
|
* and authentication requirements.
|
|
82
82
|
*/
|
|
83
|
-
export type AuthConfigFn = (
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
*/
|
|
88
|
-
setClientConfig: (config: MsalClientConfig) => void;
|
|
89
|
-
/**
|
|
90
|
-
* Set whether authentication is required for the application
|
|
91
|
-
* @param requiresAuth - If true, app will attempt automatic login on initialization
|
|
92
|
-
*/
|
|
93
|
-
setRequiresAuth: (requiresAuth: boolean) => void;
|
|
94
|
-
/**
|
|
95
|
-
* Set a default login hint used for silent SSO and pre-filled usernames
|
|
96
|
-
* @param loginHint - Preferred username/email to use for login hint
|
|
97
|
-
*/
|
|
98
|
-
setLoginHint: (loginHint: string) => void;
|
|
99
|
-
}) => void;
|
|
83
|
+
export type AuthConfigFn<TRef = unknown> = (
|
|
84
|
+
configurator: ModuleConfigType<MsalModule>,
|
|
85
|
+
ref?: TRef,
|
|
86
|
+
) => void;
|
|
100
87
|
|
|
101
88
|
/**
|
|
102
89
|
* Enables MSAL authentication module in the framework.
|
package/src/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Generated by genversion.
|
|
2
|
-
export const version = '7.2.
|
|
2
|
+
export const version = '7.2.2';
|