@equinor/fusion-framework-module-msal 11.0.0 → 11.0.1

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 (60) hide show
  1. package/dist/esm/version.js +1 -1
  2. package/dist/tsconfig.tsbuildinfo +1 -1
  3. package/dist/types/version.d.ts +1 -1
  4. package/package.json +8 -5
  5. package/CHANGELOG.md +0 -1212
  6. package/docs/api-reference.md +0 -85
  7. package/docs/auth-code-flow.md +0 -86
  8. package/docs/migration-v2-to-v4.md +0 -115
  9. package/docs/testing.md +0 -191
  10. package/docs/troubleshooting.md +0 -17
  11. package/docs/version-management.md +0 -67
  12. package/src/MsalClient.interface.ts +0 -139
  13. package/src/MsalClient.ts +0 -326
  14. package/src/MsalConfigurator.ts +0 -486
  15. package/src/MsalProvider.interface.ts +0 -179
  16. package/src/MsalProvider.ts +0 -776
  17. package/src/MsalProxyProvider.interface.ts +0 -72
  18. package/src/__tests__/MsalConfigurator.test.ts +0 -222
  19. package/src/__tests__/MsalProvider.test.ts +0 -74
  20. package/src/__tests__/create-proxy-provider.test.ts +0 -77
  21. package/src/__tests__/mock/create-mock-user-from-token.test.ts +0 -46
  22. package/src/__tests__/mock/msal-mock.test.ts +0 -613
  23. package/src/__tests__/versioning/resolve-version.test.ts +0 -161
  24. package/src/create-client-log-callback.ts +0 -102
  25. package/src/create-proxy-provider.ts +0 -97
  26. package/src/index.ts +0 -48
  27. package/src/mock/MsalMockClient.ts +0 -618
  28. package/src/mock/MsalMockConfigurator.ts +0 -305
  29. package/src/mock/create-mock-token.ts +0 -92
  30. package/src/mock/create-mock-user-from-token.ts +0 -46
  31. package/src/mock/create-msal-mock-client.ts +0 -25
  32. package/src/mock/decode-jwt-segment.ts +0 -22
  33. package/src/mock/index.ts +0 -30
  34. package/src/mock/module.ts +0 -54
  35. package/src/module.ts +0 -142
  36. package/src/msal-config-schema.ts +0 -81
  37. package/src/static.ts +0 -38
  38. package/src/telemetry-config-schema.ts +0 -25
  39. package/src/types.ts +0 -16
  40. package/src/util/compare-origin.ts +0 -18
  41. package/src/util/normalize-uri.ts +0 -24
  42. package/src/util/redirect.ts +0 -19
  43. package/src/v2/IAuthClient.interface.ts +0 -114
  44. package/src/v2/Logger.ts +0 -204
  45. package/src/v2/MsalProvider.interface.ts +0 -102
  46. package/src/v2/create-proxy-client.ts +0 -195
  47. package/src/v2/create-proxy-provider.ts +0 -177
  48. package/src/v2/map-account-info.ts +0 -23
  49. package/src/v2/map-authentication-result.ts +0 -28
  50. package/src/v2/types.ts +0 -674
  51. package/src/v4/create-proxy-provider.ts +0 -75
  52. package/src/v4/index.ts +0 -13
  53. package/src/v4/types.ts +0 -727
  54. package/src/version.ts +0 -2
  55. package/src/versioning/VersionError.ts +0 -64
  56. package/src/versioning/index.ts +0 -29
  57. package/src/versioning/resolve-version.ts +0 -154
  58. package/src/versioning/types.ts +0 -60
  59. package/tsconfig.json +0 -18
  60. package/vitest.config.ts +0 -11
@@ -1,139 +0,0 @@
1
- import type {
2
- IPublicClientApplication,
3
- AccountInfo,
4
- AuthenticationResult,
5
- PopupRequest,
6
- RedirectRequest,
7
- AuthorizationCodeRequest,
8
- } from '@azure/msal-browser';
9
-
10
- /**
11
- * Authentication behavior type determining the interaction method.
12
- *
13
- * - 'popup': Opens authentication in a popup window (returns result immediately)
14
- * - 'redirect': Navigates browser to authentication page (returns void, result via handleRedirectPromise)
15
- */
16
- export type AuthBehavior = 'popup' | 'redirect';
17
-
18
- /**
19
- * Options for acquiring an access token.
20
- *
21
- * This type ensures either the legacy or modern approach is used by requiring
22
- * the request parameter while allowing optional configuration for behavior and silent mode.
23
- *
24
- * @property request - MSAL request object for popup or redirect authentication
25
- * @property behavior - Optional authentication method (popup or redirect). Defaults to 'redirect'
26
- * @property silent - Optional flag to attempt silent token acquisition first. Defaults to true if account is available
27
- */
28
- export type AcquireTokenOptions = {
29
- request: PopupRequest | RedirectRequest;
30
- behavior?: AuthBehavior;
31
- silent?: boolean;
32
- };
33
-
34
- /**
35
- * Result type for token acquisition operations.
36
- *
37
- * Returns the authentication result on success, or null/undefined on failure or redirect.
38
- * For redirect flows, returns undefined (void) because browser navigation interrupts execution.
39
- */
40
- export type AcquireTokenResult = AuthenticationResult | null | undefined;
41
-
42
- /**
43
- * Options for user login/authentication.
44
- *
45
- * @property request - MSAL request object for popup or redirect authentication
46
- * @property behavior - Optional authentication method (popup or redirect). Defaults to 'redirect'
47
- * @property silent - Optional flag to attempt silent SSO authentication first. Defaults to true
48
- */
49
- export type LoginOptions = {
50
- request: PopupRequest | RedirectRequest;
51
- behavior?: AuthBehavior;
52
- silent?: boolean;
53
- };
54
-
55
- /**
56
- * Options for user logout.
57
- *
58
- * @property redirectUri - Optional URI to redirect to after logout completes
59
- * @property account - Optional account to log out (defaults to active account if not provided)
60
- */
61
- export type LogoutOptions = {
62
- redirectUri?: string;
63
- account?: AccountInfo;
64
- };
65
-
66
- /**
67
- * Result type for login operations.
68
- *
69
- * Returns the authentication result on success, or undefined on redirect-based flows
70
- * (where the browser navigates away). For redirect flows, result is available via handleRedirectPromise.
71
- */
72
- export type LoginResult = AuthenticationResult | undefined;
73
-
74
- /**
75
- * Interface for MSAL v4 client with additional properties and methods.
76
- *
77
- * This interface extends the standard MSAL v4 PublicClientApplication
78
- * with additional properties and methods needed for the framework.
79
- *
80
- * @example
81
- * ```typescript
82
- * const client: IMsalClient = new MsalClient(config);
83
- *
84
- * // Access additional properties
85
- * const tenantId = client.tenantId;
86
- * const hasValidClaims = client.hasValidClaims;
87
- *
88
- * // Use enhanced methods
89
- * const result = await client.login({ request: { scopes: ['User.Read'] } });
90
- * ```
91
- */
92
- export interface IMsalClient extends IPublicClientApplication {
93
- /** Configured client ID */
94
- clientId: string | undefined;
95
-
96
- /** Tenant ID for the client domain */
97
- tenantId: string | undefined;
98
-
99
- /** Check if the current account has valid claims */
100
- hasValidClaims: boolean;
101
-
102
- /**
103
- * Login user with enhanced options
104
- * @param options - Login configuration options
105
- * @returns Promise resolving to authentication result or undefined
106
- */
107
- login(options: LoginOptions): Promise<LoginResult>;
108
-
109
- /**
110
- * Logout user with enhanced options
111
- * @param options - Logout configuration options
112
- * @returns Promise resolving to void
113
- */
114
- logout(options: LogoutOptions): Promise<void>;
115
-
116
- /**
117
- * Acquire access token with enhanced options
118
- * @param options - Token acquisition configuration options
119
- * @returns Promise resolving to authentication result or null/undefined
120
- */
121
- acquireToken(options: AcquireTokenOptions): Promise<AcquireTokenResult>;
122
-
123
- /**
124
- * Exchange a backend-issued authorization code for tokens (SPA Auth Code Flow).
125
- *
126
- * This method enables automatic sign-in using a backend-issued auth code without
127
- * requiring interactive MSAL flows. Primarily used during module initialization.
128
- *
129
- * @param request - Authorization code request with code and scopes
130
- * @returns Promise resolving to authentication result with tokens
131
- *
132
- * @remarks
133
- * - Auth codes are single-use and short-lived (typically 5-10 minutes)
134
- * - MSAL handles token validation, caching, and refresh token management
135
- * - Follows Microsoft's standard SPA Auth Code Flow pattern
136
- * - Inherited from PublicClientApplication (MSAL Browser v4+)
137
- */
138
- acquireTokenByCode(request: AuthorizationCodeRequest): Promise<AuthenticationResult>;
139
- }
package/src/MsalClient.ts DELETED
@@ -1,326 +0,0 @@
1
- import {
2
- PublicClientApplication,
3
- type CacheLookupPolicy,
4
- type SilentRequest,
5
- type Configuration,
6
- type EndSessionRequest,
7
- type PopupRequest,
8
- type RedirectRequest,
9
- } from '@azure/msal-browser';
10
-
11
- import type {
12
- IMsalClient,
13
- AcquireTokenResult,
14
- LoginOptions,
15
- LogoutOptions,
16
- LoginResult,
17
- AcquireTokenOptions,
18
- } from './MsalClient.interface';
19
-
20
- export type { IMsalClient };
21
-
22
- /**
23
- * Fallback correlation ID for logger calls when request doesn't provide one.
24
- * Empty string indicates a non-request-specific log (validation, configuration warnings).
25
- */
26
- const FUSION_CORRELATION_ID = '';
27
-
28
- /**
29
- * MSAL client configuration extending the standard MSAL Configuration.
30
- *
31
- * This type adds tenant-specific configuration options while maintaining
32
- * full compatibility with the base MSAL Configuration type.
33
- *
34
- * @remarks
35
- * The `tenantId` in the auth configuration is optional but recommended for
36
- * multi-tenant applications. When provided, it's used for better tenant isolation
37
- * and can be accessed via the client's `tenantId` property.
38
- */
39
- export type MsalClientConfig = Configuration & {
40
- auth: {
41
- /** Optional tenant identifier for Azure AD tenant */
42
- tenantId?: string;
43
- };
44
- /**
45
- * Cache lookup policy applied to every `acquireTokenSilent` call.
46
- *
47
- * Controls whether MSAL falls back to a hidden iframe when the refresh token
48
- * fails. When `undefined`, MSAL's built-in default applies (cache → refresh
49
- * token → iframe). Set to `CacheLookupPolicy.AccessTokenAndRefreshToken` to
50
- * skip the iframe step and fail immediately with `InteractionRequiredAuthError`
51
- * when the refresh token is revoked — avoiding the ~10–20 s
52
- * `monitor_window_timeout` delay.
53
- *
54
- * When using {@link MsalConfigurator}, this defaults to
55
- * `CacheLookupPolicy.AccessTokenAndRefreshToken`. When constructing `MsalClient`
56
- * directly, the field is optional and defaults to `undefined` (MSAL default).
57
- *
58
- * Per-request `cacheLookupPolicy` on `SilentRequest` takes precedence over this value.
59
- */
60
- cacheLookupPolicy?: CacheLookupPolicy;
61
- };
62
-
63
- /**
64
- * MSAL v4 client implementation with extended properties and methods.
65
- *
66
- * This class extends the standard MSAL PublicClientApplication to provide
67
- * additional properties (tenantId, clientId, hasValidClaims) and enhanced
68
- * authentication methods with better options for behavior and silent flows.
69
- *
70
- * @example
71
- * ```typescript
72
- * const config: MsalClientConfig = {
73
- * auth: {
74
- * clientId: 'your-client-id',
75
- * authority: 'https://login.microsoftonline.com/your-tenant-id',
76
- * tenantId: 'your-tenant-id'
77
- * }
78
- * };
79
- * const client = new MsalClient(config);
80
- * await client.initialize();
81
- * ```
82
- */
83
- export class MsalClient extends PublicClientApplication implements IMsalClient {
84
- #tenantId?: string;
85
- #clientId?: string;
86
- #cacheLookupPolicy?: CacheLookupPolicy;
87
-
88
- /**
89
- * Creates a new MSAL client instance.
90
- *
91
- * @param config - MSAL client configuration including auth settings
92
- */
93
- constructor(config: MsalClientConfig) {
94
- super(config);
95
- this.#tenantId = config.auth?.tenantId;
96
- this.#clientId = config.auth?.clientId;
97
- this.#cacheLookupPolicy = config.cacheLookupPolicy;
98
- }
99
-
100
- /**
101
- * Tenant identifier for the configured Azure AD tenant.
102
- *
103
- * @returns The tenant ID string if configured, undefined otherwise
104
- */
105
- get tenantId(): string | undefined {
106
- return this.#tenantId;
107
- }
108
-
109
- /**
110
- * Client identifier (application ID) for the configured Azure AD application.
111
- *
112
- * @returns The client ID string if configured, undefined otherwise
113
- */
114
- get clientId(): string | undefined {
115
- return this.#clientId;
116
- }
117
-
118
- /**
119
- * Checks if the currently active account has valid ID token claims.
120
- *
121
- * This property validates that the ID token's expiration claim (exp) is in the future,
122
- * indicating the account is still authenticated and the session is valid.
123
- *
124
- * @returns True if the account has unexpired token claims, false otherwise
125
- */
126
- get hasValidClaims(): boolean {
127
- const idTokenClaims = this.getActiveAccount()?.idTokenClaims;
128
- // Compare token expiration time (seconds since epoch) with current time
129
- return Number(idTokenClaims?.exp) > Number(Math.ceil(Date.now() / 1000));
130
- }
131
-
132
- /**
133
- * Authenticates user with support for silent SSO, popup, and redirect flows.
134
- *
135
- * @param options - Login configuration with request, behavior, and silent flag
136
- * @returns Promise resolving to authentication result
137
- *
138
- * @remarks
139
- * Authentication flow priority:
140
- * 1. Silent SSO (if enabled and account/loginHint provided)
141
- * 2. Interactive method based on behavior (popup or redirect)
142
- *
143
- * **Behavior differences:**
144
- * - **Popup**: Opens authentication popup window and returns authentication result immediately
145
- * - **Redirect**: Navigates browser to Microsoft login page. Returns `void` because the browser
146
- * navigates to a new page. After redirect completes, the result will be available via
147
- * `handleRedirectPromise()` when the app loads on the new page.
148
- *
149
- * @throws {Error} If an invalid `options.behavior` value is provided.
150
- */
151
- async login(options: Required<LoginOptions>): Promise<LoginResult> {
152
- // Attempt silent authentication first if enabled
153
- // This provides better UX by avoiding unnecessary popups/redirects
154
- if (options.silent) {
155
- // Warn early when neither an account nor login hint is available for silent SSO
156
- if (!options.request.account && !options.request.loginHint) {
157
- this.getLogger().warning(
158
- 'No account or login hint provided, please provide an account or login hint in the request',
159
- options.request.correlationId || FUSION_CORRELATION_ID,
160
- );
161
- }
162
- try {
163
- return await this.ssoSilent(options.request as SilentRequest);
164
- } catch {
165
- // Silent login failed - continue to interactive flow
166
- this.getLogger().warning(
167
- 'Silent login failed, falling back to interactive',
168
- options.request.correlationId || FUSION_CORRELATION_ID,
169
- );
170
- }
171
- }
172
-
173
- // Perform interactive authentication based on specified behavior
174
- switch (options.behavior) {
175
- case 'popup':
176
- // Popup flow - returns result immediately after user completes authentication
177
- return await this.loginPopup(options.request as PopupRequest);
178
- case 'redirect':
179
- // Redirect flow - browser navigates to Microsoft login page
180
- // Returns void because browser navigation interrupts execution on current page
181
- // Result will be available via handleRedirectPromise() after app loads on new page
182
- await this.loginRedirect(options.request as RedirectRequest);
183
- break;
184
- default:
185
- throw new Error(
186
- `Invalid behavior provided: ${options.behavior}, please provide a valid behavior, see options.behavior for more information.`,
187
- );
188
- }
189
- }
190
-
191
- /**
192
- * Logs out the current user and clears authentication state.
193
- *
194
- * This method initiates a logout flow using the redirect mechanism, which navigates
195
- * the user to the Microsoft logout endpoint to clear session cookies and tokens.
196
- *
197
- * @param options - Logout configuration options
198
- * @param options.account - Account to log out (defaults to active account if not provided)
199
- * @param options.redirectUri - URI to redirect to after logout completes
200
- * @returns Promise that resolves when logout redirect is initiated
201
- *
202
- * @remarks
203
- * - This method always uses redirect flow for logout (more reliable than popup)
204
- * - **Returns `void`**: The browser navigates to Microsoft's logout page, which interrupts
205
- * execution on the current page. This is expected behavior for redirect-based logout.
206
- * - If no account is provided, uses the currently active account
207
- * - After redirect, user will be logged out at Microsoft's identity provider
208
- * - Application state and local tokens are cleared during this process
209
- *
210
- * @example
211
- * ```typescript
212
- * // Basic logout with active account
213
- * await client.logout();
214
- *
215
- * // Logout with custom redirect URI
216
- * await client.logout({
217
- * redirectUri: 'https://app.com/logged-out'
218
- * });
219
- * ```
220
- */
221
- async logout(options?: LogoutOptions): Promise<void> {
222
- // Warn when no account was supplied since the active account will be used instead
223
- if (!options?.account) {
224
- this.getLogger().warning(
225
- 'No account available for logout, please provide an account in the options',
226
- FUSION_CORRELATION_ID,
227
- );
228
- }
229
-
230
- const logoutRequest: EndSessionRequest = {
231
- account: options?.account,
232
- postLogoutRedirectUri: options?.redirectUri,
233
- };
234
-
235
- // Browser will navigate to Microsoft logout page
236
- // Returns void because navigation interrupts execution on current page
237
- await this.logoutRedirect(logoutRequest);
238
- }
239
-
240
- /**
241
- * Acquires an access token with smart silent/interactive fallback.
242
- *
243
- * @param options - Token acquisition configuration
244
- * @returns Promise resolving to authentication result or null/undefined
245
- *
246
- * @remarks
247
- * Token acquisition flow:
248
- * 1. If silent=true and account available, attempt silent token acquisition from cache
249
- * 2. On silent failure (or not attempted), use interactive method based on behavior
250
- * 3. Interactive method based on behavior (popup or redirect)
251
- *
252
- * **Behavior differences:**
253
- * - **Popup**: Opens authentication popup window and returns token result immediately
254
- * - **Redirect**: Navigates browser to Microsoft login page. Returns `void` because the browser
255
- * navigates to a new page. After redirect completes, handle the result via
256
- * `handleRedirectPromise()` when the app loads on the new page.
257
- *
258
- * The default silent behavior is determined by presence of account in the request.
259
- * This provides optimal UX by minimizing unnecessary user interactions.
260
- *
261
- * @throws {Error} If no `request` is provided in `options`.
262
- */
263
- async acquireToken(options: AcquireTokenOptions): Promise<AcquireTokenResult> {
264
- const { behavior = 'redirect', silent = !!options.request?.account, request } = options;
265
-
266
- // A request is required to know which scopes/account to acquire a token for
267
- if (!request) {
268
- throw new Error('No request provided, please provide a request in the options');
269
- }
270
-
271
- // Warn when no scopes are requested, since MSAL requires at least one scope
272
- if (request.scopes.length === 0) {
273
- this.getLogger().warning(
274
- 'No scopes provided, please provide scopes in the request option, see options.request for more information.',
275
- request.correlationId || FUSION_CORRELATION_ID,
276
- );
277
- }
278
-
279
- // Attempt silent token acquisition first
280
- // This fetches from cache or uses refresh token without user interaction
281
- if (silent) {
282
- // Only silent-acquire when an account is available to look up cached tokens for
283
- if (request.account) {
284
- try {
285
- this.getLogger().verbose(
286
- 'Attempting to acquire token silently',
287
- request.correlationId || FUSION_CORRELATION_ID,
288
- );
289
- return await this.acquireTokenSilent({
290
- // Instance-level policy is the default; request-level cacheLookupPolicy takes precedence
291
- cacheLookupPolicy: this.#cacheLookupPolicy,
292
- ...(request as SilentRequest),
293
- });
294
- } catch {
295
- // Silent acquisition failed - fall back to interactive
296
- this.getLogger().warning(
297
- 'Silent token acquisition failed, falling back to interactive',
298
- request.correlationId || FUSION_CORRELATION_ID,
299
- );
300
- }
301
- } else {
302
- this.getLogger().warning(
303
- 'Cannot acquire token silently, no account provided, falling back to interactive.',
304
- request.correlationId || FUSION_CORRELATION_ID,
305
- );
306
- }
307
- }
308
-
309
- // Perform interactive token acquisition
310
- switch (behavior) {
311
- case 'popup':
312
- // Popup flow - returns token immediately after user grants permission
313
- return await this.acquireTokenPopup(request);
314
- case 'redirect':
315
- // Redirect flow - browser navigates to Microsoft login page
316
- // Returns void because browser navigation interrupts execution on current page
317
- // Token will be available via handleRedirectPromise() after app loads on new page
318
- await this.acquireTokenRedirect(request);
319
- break;
320
- default:
321
- throw new Error(
322
- `Invalid behavior provided: ${behavior}, please provide a valid behavior, see options.behavior for more information.`,
323
- );
324
- }
325
- }
326
- }