@equinor/fusion-framework-module-msal 10.0.1 → 10.0.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.
Files changed (58) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/esm/MsalClient.js +9 -0
  3. package/dist/esm/MsalClient.js.map +1 -1
  4. package/dist/esm/MsalConfigurator.js +3 -1
  5. package/dist/esm/MsalConfigurator.js.map +1 -1
  6. package/dist/esm/MsalProvider.js +20 -1
  7. package/dist/esm/MsalProvider.js.map +1 -1
  8. package/dist/esm/create-client-log-callback.js +1 -0
  9. package/dist/esm/create-client-log-callback.js.map +1 -1
  10. package/dist/esm/create-proxy-provider.js +5 -0
  11. package/dist/esm/create-proxy-provider.js.map +1 -1
  12. package/dist/esm/module.js +3 -2
  13. package/dist/esm/module.js.map +1 -1
  14. package/dist/esm/util/normalize-uri.js.map +1 -1
  15. package/dist/esm/v2/Logger.js +173 -0
  16. package/dist/esm/v2/Logger.js.map +1 -0
  17. package/dist/esm/v2/create-proxy-client.js +6 -0
  18. package/dist/esm/v2/create-proxy-client.js.map +1 -1
  19. package/dist/esm/v2/create-proxy-provider.js +4 -0
  20. package/dist/esm/v2/create-proxy-provider.js.map +1 -1
  21. package/dist/esm/v2/types.js +3 -124
  22. package/dist/esm/v2/types.js.map +1 -1
  23. package/dist/esm/v4/create-proxy-provider.js +1 -0
  24. package/dist/esm/v4/create-proxy-provider.js.map +1 -1
  25. package/dist/esm/v4/types.js.map +1 -1
  26. package/dist/esm/version.js +1 -1
  27. package/dist/esm/versioning/VersionError.js.map +1 -1
  28. package/dist/esm/versioning/resolve-version.js +4 -0
  29. package/dist/esm/versioning/resolve-version.js.map +1 -1
  30. package/dist/tsconfig.tsbuildinfo +1 -1
  31. package/dist/types/MsalClient.d.ts +4 -0
  32. package/dist/types/MsalConfigurator.d.ts +1 -0
  33. package/dist/types/MsalProvider.d.ts +13 -0
  34. package/dist/types/create-proxy-provider.d.ts +3 -0
  35. package/dist/types/static.d.ts +1 -1
  36. package/dist/types/v2/IAuthClient.interface.d.ts +2 -2
  37. package/dist/types/v2/Logger.d.ts +92 -0
  38. package/dist/types/v2/MsalProvider.interface.d.ts +1 -1
  39. package/dist/types/v2/types.d.ts +3 -49
  40. package/dist/types/v4/types.d.ts +1 -3
  41. package/dist/types/version.d.ts +1 -1
  42. package/package.json +7 -7
  43. package/src/MsalClient.ts +9 -0
  44. package/src/MsalConfigurator.ts +3 -1
  45. package/src/MsalProvider.ts +20 -1
  46. package/src/create-client-log-callback.ts +1 -0
  47. package/src/create-proxy-provider.ts +5 -0
  48. package/src/module.ts +3 -2
  49. package/src/v2/IAuthClient.interface.ts +2 -2
  50. package/src/v2/Logger.ts +204 -0
  51. package/src/v2/MsalProvider.interface.ts +1 -1
  52. package/src/v2/create-proxy-client.ts +6 -0
  53. package/src/v2/create-proxy-provider.ts +4 -0
  54. package/src/v2/types.ts +8 -158
  55. package/src/v4/create-proxy-provider.ts +1 -0
  56. package/src/v4/types.ts +3 -3
  57. package/src/version.ts +1 -1
  58. package/src/versioning/resolve-version.ts +4 -0
@@ -100,6 +100,8 @@ export declare class MsalClient extends PublicClientApplication implements IMsal
100
100
  * - **Redirect**: Navigates browser to Microsoft login page. Returns `void` because the browser
101
101
  * navigates to a new page. After redirect completes, the result will be available via
102
102
  * `handleRedirectPromise()` when the app loads on the new page.
103
+ *
104
+ * @throws {Error} If an invalid `options.behavior` value is provided.
103
105
  */
104
106
  login(options: Required<LoginOptions>): Promise<LoginResult>;
105
107
  /**
@@ -153,6 +155,8 @@ export declare class MsalClient extends PublicClientApplication implements IMsal
153
155
  *
154
156
  * The default silent behavior is determined by presence of account in the request.
155
157
  * This provides optimal UX by minimizing unnecessary user interactions.
158
+ *
159
+ * @throws {Error} If no `request` is provided in `options`.
156
160
  */
157
161
  acquireToken(options: AcquireTokenOptions): Promise<AcquireTokenResult>;
158
162
  }
@@ -37,6 +37,7 @@ export declare class MsalConfigurator extends BaseConfigBuilder<MsalConfig> {
37
37
  * The MSAL module version being configured.
38
38
  *
39
39
  * @default Latest
40
+ * @returns The configured MSAL module version.
40
41
  */
41
42
  get version(): string;
42
43
  /**
@@ -37,6 +37,8 @@ export declare class MsalProvider extends BaseModuleProvider<MsalConfig> impleme
37
37
  * Default OAuth scopes used when the caller provides no scopes.
38
38
  *
39
39
  * Resolves to the app's Entra ID configured permissions via the `/.default` scope.
40
+ *
41
+ * @returns The default OAuth scopes derived from the configured client ID.
40
42
  */
41
43
  get defaultScopes(): string[];
42
44
  /**
@@ -54,6 +56,8 @@ export declare class MsalProvider extends BaseModuleProvider<MsalConfig> impleme
54
56
  *
55
57
  * Provides access to the underlying MSAL PublicClientApplication for advanced use cases.
56
58
  * Prefer using provider methods for standard authentication operations.
59
+ *
60
+ * @returns The underlying MSAL client instance.
57
61
  */
58
62
  get client(): IMsalClient;
59
63
  /**
@@ -61,6 +65,8 @@ export declare class MsalProvider extends BaseModuleProvider<MsalConfig> impleme
61
65
  *
62
66
  * Returns the active account if a user is authenticated, or null if no user is logged in.
63
67
  * This is a shorthand for `client.getActiveAccount()`.
68
+ *
69
+ * @returns The currently authenticated account, or `null` if no user is logged in.
64
70
  */
65
71
  get account(): AccountInfo | null;
66
72
  /**
@@ -93,6 +99,8 @@ export declare class MsalProvider extends BaseModuleProvider<MsalConfig> impleme
93
99
  *
94
100
  * The provider will attempt automatic login with empty scopes if requiresAuth is true.
95
101
  * Apps should call acquireToken with actual scopes after initialization completes.
102
+ *
103
+ * @throws {Error} If auth code exchange requires a client ID but none is configured.
96
104
  */
97
105
  initialize(): Promise<void>;
98
106
  /**
@@ -126,6 +134,8 @@ export declare class MsalProvider extends BaseModuleProvider<MsalConfig> impleme
126
134
  * @remark Empty scopes are currently tracked as telemetry exceptions but execution continues for monitoring purposes.
127
135
  * This behavior will be changed to throw exceptions once sufficient metrics are collected.
128
136
  *
137
+ * @throws {Error} Re-throws any error encountered during token acquisition after tracking it via telemetry.
138
+ *
129
139
  * @example
130
140
  * ```typescript
131
141
  * // Modern API format
@@ -257,6 +267,9 @@ export declare class MsalProvider extends BaseModuleProvider<MsalConfig> impleme
257
267
  * - Version compatibility is tracked via telemetry
258
268
  * - Throws error if unsupported version is requested
259
269
  *
270
+ * @template T - The provider interface type expected by the target version.
271
+ * @throws {Error} If the requested version cannot be resolved or the proxy fails to create.
272
+ *
260
273
  * @example
261
274
  * ```typescript
262
275
  * // Create v2-compatible proxy
@@ -10,6 +10,9 @@ import type { IMsalProvider } from './MsalProvider.interface';
10
10
  * @param version - The target version string (e.g., '2.0.0', '4.0.0')
11
11
  * @returns A proxy provider compatible with the specified version
12
12
  *
13
+ * @template T - The provider interface type expected by the target version.
14
+ * @throws {Error} If the resolved version is not supported.
15
+ *
13
16
  * @example
14
17
  * ```typescript
15
18
  * const baseProvider = new MsalProvider(config);
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * This constant is used to register and identify the MSAL module within the Fusion Framework.
5
5
  */
6
- export declare const ModuleName: "msal";
6
+ export declare const ModuleName: 'msal';
7
7
  /**
8
8
  * Enumeration of supported MSAL module versions.
9
9
  *
@@ -79,7 +79,7 @@ export interface IAuthClient extends IPublicClientApplication {
79
79
  * @param silent - Whether to attempt silent authentication first
80
80
  * @returns Promise resolving to authentication result or void
81
81
  */
82
- login(options?: AuthRequest, behavior?: AuthBehavior, silent?: boolean): Promise<AuthenticationResult | void>;
82
+ login(options?: AuthRequest, behavior?: AuthBehavior, silent?: boolean): Promise<AuthenticationResult | undefined>;
83
83
  /**
84
84
  * Acquire access token with optional silent authentication fallback
85
85
  * @param options - Authentication request options
@@ -87,5 +87,5 @@ export interface IAuthClient extends IPublicClientApplication {
87
87
  * @param silent - Whether to attempt silent authentication first
88
88
  * @returns Promise resolving to authentication result or void
89
89
  */
90
- acquireToken(options?: AuthRequest, behavior?: AuthBehavior, silent?: boolean): Promise<AuthenticationResult | void>;
90
+ acquireToken(options?: AuthRequest, behavior?: AuthBehavior, silent?: boolean): Promise<AuthenticationResult | undefined>;
91
91
  }
@@ -0,0 +1,92 @@
1
+ import { LogLevel, type LoggerOptions } from './types.js';
2
+ /**
3
+ * Class which facilitates logging of messages to a specific place
4
+ */
5
+ export declare class Logger {
6
+ private correlationId;
7
+ private level;
8
+ private piiLoggingEnabled;
9
+ private localCallback;
10
+ private packageName;
11
+ private packageVersion;
12
+ /**
13
+ * Creates a new `Logger`.
14
+ *
15
+ * @param loggerOptions - Logger configuration, including callback, PII, and log level settings.
16
+ * @param packageName - Name of the package implementing this logger.
17
+ * @param packageVersion - Version of the package implementing this logger.
18
+ */
19
+ constructor(loggerOptions: LoggerOptions, packageName?: string, packageVersion?: string);
20
+ /**
21
+ * Creates the default logger options used when none are provided.
22
+ *
23
+ * @returns Default logger options with a no-op callback, PII logging disabled, and Info level.
24
+ */
25
+ private static createDefaultLoggerOptions;
26
+ /**
27
+ * Create new Logger with existing configurations.
28
+ *
29
+ * @param packageName - Name of the package implementing this logger.
30
+ * @param packageVersion - Version of the package implementing this logger.
31
+ * @param correlationId - Correlation ID to use, falling back to this logger's own if omitted.
32
+ * @returns A new `Logger` instance cloned from this one.
33
+ */
34
+ clone(packageName: string, packageVersion: string, correlationId?: string): Logger;
35
+ /**
36
+ * Logs error messages.
37
+ *
38
+ * @param message - The message to log.
39
+ * @param correlationId - Correlation ID to attach to the log entry, if any.
40
+ */
41
+ error(message: string, correlationId?: string): void;
42
+ /**
43
+ * Logs warning messages.
44
+ *
45
+ * @param message - The message to log.
46
+ * @param correlationId - Correlation ID to attach to the log entry, if any.
47
+ */
48
+ warning(message: string, correlationId?: string): void;
49
+ /**
50
+ * Logs info messages.
51
+ *
52
+ * @param message - The message to log.
53
+ * @param correlationId - Correlation ID to attach to the log entry, if any.
54
+ */
55
+ info(message: string, correlationId?: string): void;
56
+ /**
57
+ * Logs verbose messages.
58
+ *
59
+ * @param message - The message to log.
60
+ * @param correlationId - Correlation ID to attach to the log entry, if any.
61
+ */
62
+ verbose(message: string, correlationId?: string): void;
63
+ /**
64
+ * Logs trace messages.
65
+ *
66
+ * @param message - The message to log.
67
+ * @param correlationId - Correlation ID to attach to the log entry, if any.
68
+ */
69
+ trace(message: string, correlationId?: string): void;
70
+ /**
71
+ * Returns whether PII Logging is enabled or not.
72
+ *
73
+ * @returns `true` when PII logging is enabled, otherwise `false`.
74
+ */
75
+ isPiiLoggingEnabled(): boolean;
76
+ /**
77
+ * Formats and dispatches a log message to the configured callback, unless it
78
+ * is below the configured log level or contains PII while PII logging is disabled.
79
+ *
80
+ * @param logMessage - The raw message to log.
81
+ * @param options - Log level, PII flag, and correlation ID for this message.
82
+ */
83
+ private logMessage;
84
+ /**
85
+ * Execute callback with message.
86
+ *
87
+ * @param level - The log level of the message.
88
+ * @param message - The formatted message to send to the callback.
89
+ * @param containsPii - Whether the message contains personally identifiable information.
90
+ */
91
+ executeCallback(level: LogLevel, message: string, containsPii: boolean): void;
92
+ }
@@ -81,7 +81,7 @@ export interface IMsalProvider {
81
81
  /**
82
82
  * Handle authentication redirect
83
83
  */
84
- handleRedirect(): Promise<void | null>;
84
+ handleRedirect(): Promise<undefined | null>;
85
85
  /**
86
86
  * Create a proxy provider for version compatibility
87
87
  * @param version - Version string
@@ -4,6 +4,7 @@
4
4
  * This file contains all types required to type IPublicClientApplication
5
5
  * from MSAL v2.38.4 to maintain backward compatibility while using MSAL v4 implementation.
6
6
  */
7
+ import type { Logger } from './Logger.js';
7
8
  /**
8
9
  * Key-Value type to support queryParams, extraQueryParams and claims
9
10
  */
@@ -291,9 +292,7 @@ export declare enum LogLevel {
291
292
  /**
292
293
  * Callback to send the messages to
293
294
  */
294
- export interface ILoggerCallback {
295
- (level: LogLevel, message: string, containsPii: boolean): void;
296
- }
295
+ export type ILoggerCallback = (level: LogLevel, message: string, containsPii: boolean) => void;
297
296
  /**
298
297
  * Options for logger messages
299
298
  */
@@ -303,52 +302,7 @@ export type LoggerOptions = {
303
302
  logLevel?: LogLevel;
304
303
  correlationId?: string;
305
304
  };
306
- /**
307
- * Class which facilitates logging of messages to a specific place
308
- */
309
- export declare class Logger {
310
- private correlationId;
311
- private level;
312
- private piiLoggingEnabled;
313
- private localCallback;
314
- private packageName;
315
- private packageVersion;
316
- constructor(loggerOptions: LoggerOptions, packageName?: string, packageVersion?: string);
317
- private static createDefaultLoggerOptions;
318
- /**
319
- * Create new Logger with existing configurations.
320
- */
321
- clone(packageName: string, packageVersion: string, correlationId?: string): Logger;
322
- /**
323
- * Logs error messages.
324
- */
325
- error(message: string, correlationId?: string): void;
326
- /**
327
- * Logs warning messages.
328
- */
329
- warning(message: string, correlationId?: string): void;
330
- /**
331
- * Logs info messages.
332
- */
333
- info(message: string, correlationId?: string): void;
334
- /**
335
- * Logs verbose messages.
336
- */
337
- verbose(message: string, correlationId?: string): void;
338
- /**
339
- * Logs trace messages.
340
- */
341
- trace(message: string, correlationId?: string): void;
342
- /**
343
- * Returns whether PII Logging is enabled or not.
344
- */
345
- isPiiLoggingEnabled(): boolean;
346
- private logMessage;
347
- /**
348
- * Execute callback with message.
349
- */
350
- executeCallback(level: LogLevel, message: string, containsPii: boolean): void;
351
- }
305
+ export { Logger } from './Logger.js';
352
306
  /**
353
307
  * Performance event type
354
308
  */
@@ -395,9 +395,7 @@ export declare enum LogLevel {
395
395
  /**
396
396
  * Callback to send the messages to
397
397
  */
398
- export interface ILoggerCallback {
399
- (level: LogLevel, message: string, containsPii: boolean): void;
400
- }
398
+ export type ILoggerCallback = (level: LogLevel, message: string, containsPii: boolean) => void;
401
399
  /**
402
400
  * Event callback function type
403
401
  */
@@ -1 +1 @@
1
- export declare const version = "10.0.1";
1
+ export declare const version = "10.0.2";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@equinor/fusion-framework-module-msal",
3
- "version": "10.0.1",
3
+ "version": "10.0.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",
@@ -48,18 +48,18 @@
48
48
  "devDependencies": {
49
49
  "@types/semver": "^7.7.1",
50
50
  "semver": "^7.7.4",
51
- "typescript": "^6.0.3",
51
+ "typescript": "^7.0.2",
52
52
  "zod": "^4.4.3",
53
- "@equinor/fusion-framework-module": "^6.1.0",
54
- "@equinor/fusion-framework-module-telemetry": "^7.0.0"
53
+ "@equinor/fusion-framework-module": "^6.1.1",
54
+ "@equinor/fusion-framework-module-telemetry": "^7.0.1"
55
55
  },
56
56
  "peerDependencies": {
57
57
  "@types/semver": "^7.0.0",
58
58
  "semver": "^7.0.0",
59
- "typescript": "^5.0.0",
59
+ "typescript": ">=5.0.0",
60
60
  "zod": "^4.0.0",
61
- "@equinor/fusion-framework-module": "^6.1.0",
62
- "@equinor/fusion-framework-module-telemetry": "^7.0.0"
61
+ "@equinor/fusion-framework-module": "^6.1.1",
62
+ "@equinor/fusion-framework-module-telemetry": "^7.0.1"
63
63
  },
64
64
  "peerDependenciesMeta": {
65
65
  "@equinor/fusion-framework-module-telemetry": {
package/src/MsalClient.ts CHANGED
@@ -145,11 +145,14 @@ export class MsalClient extends PublicClientApplication implements IMsalClient {
145
145
  * - **Redirect**: Navigates browser to Microsoft login page. Returns `void` because the browser
146
146
  * navigates to a new page. After redirect completes, the result will be available via
147
147
  * `handleRedirectPromise()` when the app loads on the new page.
148
+ *
149
+ * @throws {Error} If an invalid `options.behavior` value is provided.
148
150
  */
149
151
  async login(options: Required<LoginOptions>): Promise<LoginResult> {
150
152
  // Attempt silent authentication first if enabled
151
153
  // This provides better UX by avoiding unnecessary popups/redirects
152
154
  if (options.silent) {
155
+ // Warn early when neither an account nor login hint is available for silent SSO
153
156
  if (!options.request.account && !options.request.loginHint) {
154
157
  this.getLogger().warning(
155
158
  'No account or login hint provided, please provide an account or login hint in the request',
@@ -216,6 +219,7 @@ export class MsalClient extends PublicClientApplication implements IMsalClient {
216
219
  * ```
217
220
  */
218
221
  async logout(options?: LogoutOptions): Promise<void> {
222
+ // Warn when no account was supplied since the active account will be used instead
219
223
  if (!options?.account) {
220
224
  this.getLogger().warning(
221
225
  'No account available for logout, please provide an account in the options',
@@ -253,14 +257,18 @@ export class MsalClient extends PublicClientApplication implements IMsalClient {
253
257
  *
254
258
  * The default silent behavior is determined by presence of account in the request.
255
259
  * This provides optimal UX by minimizing unnecessary user interactions.
260
+ *
261
+ * @throws {Error} If no `request` is provided in `options`.
256
262
  */
257
263
  async acquireToken(options: AcquireTokenOptions): Promise<AcquireTokenResult> {
258
264
  const { behavior = 'redirect', silent = !!options.request?.account, request } = options;
259
265
 
266
+ // A request is required to know which scopes/account to acquire a token for
260
267
  if (!request) {
261
268
  throw new Error('No request provided, please provide a request in the options');
262
269
  }
263
270
 
271
+ // Warn when no scopes are requested, since MSAL requires at least one scope
264
272
  if (request.scopes.length === 0) {
265
273
  this.getLogger().warning(
266
274
  'No scopes provided, please provide scopes in the request option, see options.request for more information.',
@@ -271,6 +279,7 @@ export class MsalClient extends PublicClientApplication implements IMsalClient {
271
279
  // Attempt silent token acquisition first
272
280
  // This fetches from cache or uses refresh token without user interaction
273
281
  if (silent) {
282
+ // Only silent-acquire when an account is available to look up cached tokens for
274
283
  if (request.account) {
275
284
  try {
276
285
  this.getLogger().verbose(
@@ -84,6 +84,7 @@ export class MsalConfigurator extends BaseConfigBuilder<MsalConfig> {
84
84
  * The MSAL module version being configured.
85
85
  *
86
86
  * @default Latest
87
+ * @returns The configured MSAL module version.
87
88
  */
88
89
  public get version(): string {
89
90
  return version;
@@ -100,6 +101,7 @@ export class MsalConfigurator extends BaseConfigBuilder<MsalConfig> {
100
101
  this._set('version', async () => this.version);
101
102
  // Auto-detect and integrate telemetry module if available
102
103
  this._set('telemetry.provider', async (args) => {
104
+ // Only resolve the telemetry instance when the telemetry module is registered
103
105
  if (args.hasModule('telemetry')) {
104
106
  const telemetry = await args.requireInstance('telemetry');
105
107
  return telemetry;
@@ -335,7 +337,7 @@ export class MsalConfigurator extends BaseConfigBuilder<MsalConfig> {
335
337
 
336
338
  // Auto-create client if config provided but no client instance
337
339
  // This allows users to provide configuration without manually instantiating the client
338
- if (!config.client && !!this.#msalConfig) {
340
+ if (!config.client && this.#msalConfig) {
339
341
  const clientConfig = this.#msalConfig;
340
342
 
341
343
  config.telemetry.provider?.trackEvent({
@@ -67,6 +67,8 @@ export class MsalProvider extends BaseModuleProvider<MsalConfig> implements IMsa
67
67
  * Default OAuth scopes used when the caller provides no scopes.
68
68
  *
69
69
  * Resolves to the app's Entra ID configured permissions via the `/.default` scope.
70
+ *
71
+ * @returns The default OAuth scopes derived from the configured client ID.
70
72
  */
71
73
  get defaultScopes(): string[] {
72
74
  const clientId = this.#client.clientId;
@@ -91,6 +93,8 @@ export class MsalProvider extends BaseModuleProvider<MsalConfig> implements IMsa
91
93
  *
92
94
  * Provides access to the underlying MSAL PublicClientApplication for advanced use cases.
93
95
  * Prefer using provider methods for standard authentication operations.
96
+ *
97
+ * @returns The underlying MSAL client instance.
94
98
  */
95
99
  get client(): IMsalClient {
96
100
  return this.#client;
@@ -101,6 +105,8 @@ export class MsalProvider extends BaseModuleProvider<MsalConfig> implements IMsa
101
105
  *
102
106
  * Returns the active account if a user is authenticated, or null if no user is logged in.
103
107
  * This is a shorthand for `client.getActiveAccount()`.
108
+ *
109
+ * @returns The currently authenticated account, or `null` if no user is logged in.
104
110
  */
105
111
  get account(): AccountInfo | null {
106
112
  return this.#client.getActiveAccount();
@@ -175,6 +181,8 @@ export class MsalProvider extends BaseModuleProvider<MsalConfig> implements IMsa
175
181
  *
176
182
  * The provider will attempt automatic login with empty scopes if requiresAuth is true.
177
183
  * Apps should call acquireToken with actual scopes after initialization completes.
184
+ *
185
+ * @throws {Error} If auth code exchange requires a client ID but none is configured.
178
186
  */
179
187
  async initialize(): Promise<void> {
180
188
  // Guard: skip authentication when running inside MSAL's hidden iframe.
@@ -204,6 +212,7 @@ export class MsalProvider extends BaseModuleProvider<MsalConfig> implements IMsa
204
212
  // Use MSAL's acquireTokenByCode to exchange backend auth code for tokens
205
213
  // This follows Microsoft's standard SPA Auth Code Flow pattern
206
214
  const clientId = this.#client.clientId;
215
+ // A client ID is required to build the default scope used for the auth code exchange
207
216
  if (!clientId) {
208
217
  throw new Error('Client ID is required for auth code exchange');
209
218
  }
@@ -248,6 +257,7 @@ export class MsalProvider extends BaseModuleProvider<MsalConfig> implements IMsa
248
257
  // Priority 1: Check if returning from redirect-based authentication
249
258
  // This handles cases where user just completed a login/acquireToken via redirect
250
259
  const handleRedirectResult = await this.handleRedirect();
260
+ // A returned account means the user just completed a redirect-based login
251
261
  if (handleRedirectResult?.account) {
252
262
  // Successfully authenticated via redirect - set as active account
253
263
  // This means the user was redirected to Microsoft and came back authenticated
@@ -263,6 +273,7 @@ export class MsalProvider extends BaseModuleProvider<MsalConfig> implements IMsa
263
273
  // Note: Using default scopes here as we don't know what scopes the app needs yet
264
274
  // App should call acquireToken with actual scopes after initialization
265
275
  const loginResult = await this.login({ request: { scopes: this.defaultScopes } });
276
+ // Only set the active account when the automatic login actually returned one
266
277
  if (loginResult?.account) {
267
278
  // Automatic login successful - set as active account
268
279
  this.#client.setActiveAccount(loginResult.account);
@@ -315,6 +326,8 @@ export class MsalProvider extends BaseModuleProvider<MsalConfig> implements IMsa
315
326
  * @remark Empty scopes are currently tracked as telemetry exceptions but execution continues for monitoring purposes.
316
327
  * This behavior will be changed to throw exceptions once sufficient metrics are collected.
317
328
  *
329
+ * @throws {Error} Re-throws any error encountered during token acquisition after tracking it via telemetry.
330
+ *
318
331
  * @example
319
332
  * ```typescript
320
333
  * // Modern API format
@@ -371,6 +384,7 @@ export class MsalProvider extends BaseModuleProvider<MsalConfig> implements IMsa
371
384
 
372
385
  // Handle empty scopes - currently monitoring for telemetry, will throw in future
373
386
  if (candidateScopes.length === 0) {
387
+ // Fall back to the client-id-derived default scope when one is available
374
388
  if (defaultScopes.length > 0) {
375
389
  this._trackEvent('acquireToken.missing-scope.defaulted', TelemetryLevel.Warning, {
376
390
  properties: { ...telemetryProperties, defaultScopes },
@@ -383,7 +397,7 @@ export class MsalProvider extends BaseModuleProvider<MsalConfig> implements IMsa
383
397
  exception,
384
398
  properties: telemetryProperties,
385
399
  });
386
- // TODO: throw exception when sufficient metrics are collected
400
+ // TODO(#5113): throw exception when sufficient metrics are collected
387
401
  // This allows us to monitor how often empty scopes are provided before enforcing validation
388
402
  }
389
403
  }
@@ -521,6 +535,7 @@ export class MsalProvider extends BaseModuleProvider<MsalConfig> implements IMsa
521
535
  }
522
536
  }
523
537
 
538
+ // Perform interactive authentication based on specified behavior
524
539
  switch (behavior) {
525
540
  case 'popup':
526
541
  return await this.#client.loginPopup(request);
@@ -606,6 +621,7 @@ export class MsalProvider extends BaseModuleProvider<MsalConfig> implements IMsa
606
621
  async handleRedirect(): Promise<AuthenticationResult | null> {
607
622
  // Process any pending redirect from authentication flow
608
623
  const result = await this.client.handleRedirectPromise();
624
+ // Only track/log when a redirect result is actually returned
609
625
  if (result) {
610
626
  // Track successful redirect completion for monitoring
611
627
  this._trackEvent('handleRedirect.success', TelemetryLevel.Information, {
@@ -632,6 +648,9 @@ export class MsalProvider extends BaseModuleProvider<MsalConfig> implements IMsa
632
648
  * - Version compatibility is tracked via telemetry
633
649
  * - Throws error if unsupported version is requested
634
650
  *
651
+ * @template T - The provider interface type expected by the target version.
652
+ * @throws {Error} If the requested version cannot be resolved or the proxy fails to create.
653
+ *
635
654
  * @example
636
655
  * ```typescript
637
656
  * // Create v2-compatible proxy
@@ -32,6 +32,7 @@ const parseMsalMessage = (message: string) => {
32
32
  /^\[([^\]]+)\]\s*:\s*\[([^\]]*)\]\s*:\s*([^:]+)\s*:\s*(\w+)\s*-\s*([^-]+)\s*-\s*(.*)$/,
33
33
  );
34
34
 
35
+ // A structured match means we can extract package/component/message parts
35
36
  if (match) {
36
37
  const [, _timestamp, _correlationId, packageInfo, _logLevel, component, logMessage] = match;
37
38
  return {
@@ -15,6 +15,9 @@ import { createProxyProvider as createProxyProvider_v4 } from './v4/create-proxy
15
15
  * @param version - The target version string (e.g., '2.0.0', '4.0.0')
16
16
  * @returns A proxy provider compatible with the specified version
17
17
  *
18
+ * @template T - The provider interface type expected by the target version.
19
+ * @throws {Error} If the resolved version is not supported.
20
+ *
18
21
  * @example
19
22
  * ```typescript
20
23
  * const baseProvider = new MsalProvider(config);
@@ -28,6 +31,7 @@ export function createProxyProvider<T = IMsalProvider>(
28
31
  // Resolve the requested version to determine which proxy to create
29
32
  const { enumVersion } = resolveVersion(version);
30
33
 
34
+ // Build the version-appropriate proxy based on the resolved MSAL module version
31
35
  switch (enumVersion) {
32
36
  case MsalModuleVersion.V2:
33
37
  // Create v2-compatible proxy with legacy API adapters
@@ -39,6 +43,7 @@ export function createProxyProvider<T = IMsalProvider>(
39
43
  // Create transparent proxy for v5 - passes through to original provider
40
44
  return new Proxy(provider, {
41
45
  get: (target: IMsalProvider, prop: keyof IMsalProvider) => {
46
+ // Delegate every known property/method to the underlying provider
42
47
  switch (prop) {
43
48
  case 'version': {
44
49
  return target.version;
package/src/module.ts CHANGED
@@ -45,6 +45,7 @@ export const module: MsalModule = {
45
45
  // Priority 2: Check if provider exists in parent module (proxy compatibility)
46
46
  // This allows child applications to reuse parent's authentication provider
47
47
  const hostProvider = init.ref?.auth;
48
+ // Reuse the parent's provider (via a version-compatible proxy) when available
48
49
  if (hostProvider) {
49
50
  try {
50
51
  const proxyProvider = hostProvider.createProxyProvider(config.version);
@@ -52,7 +53,7 @@ export const module: MsalModule = {
52
53
  } catch (error) {
53
54
  console.error('MsalModule::Failed to create proxy provider', error);
54
55
  // Fallback to host provider to prevent app breakage during migration
55
- // TODO: Consider throwing error instead once all apps are migrated to v4
56
+ // TODO(#5114): Consider throwing error instead once all apps are migrated to v4
56
57
  return hostProvider;
57
58
  }
58
59
  }
@@ -105,7 +106,7 @@ export type AuthConfigFn<TRef = unknown> = (
105
106
  * ```
106
107
  */
107
108
  export const enableMSAL = (
108
- // @biome-ignore lint/suspicious/noExplicitAny: must be any to support all module types
109
+ // biome-ignore lint/suspicious/noExplicitAny: must be any to support all module types
109
110
  configurator: IModulesConfigurator<any, any>,
110
111
  configure?: AuthConfigFn,
111
112
  ): void => {
@@ -97,7 +97,7 @@ export interface IAuthClient extends IPublicClientApplication {
97
97
  options?: AuthRequest,
98
98
  behavior?: AuthBehavior,
99
99
  silent?: boolean,
100
- ): Promise<AuthenticationResult | void>;
100
+ ): Promise<AuthenticationResult | undefined>;
101
101
 
102
102
  /**
103
103
  * Acquire access token with optional silent authentication fallback
@@ -110,5 +110,5 @@ export interface IAuthClient extends IPublicClientApplication {
110
110
  options?: AuthRequest,
111
111
  behavior?: AuthBehavior,
112
112
  silent?: boolean,
113
- ): Promise<AuthenticationResult | void>;
113
+ ): Promise<AuthenticationResult | undefined>;
114
114
  }