@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,102 +0,0 @@
1
- import type { SemVer } from 'semver';
2
- import type { MsalModuleVersion } from '../static';
3
- import type { AccountInfo, AuthenticationResult } from './types';
4
- import type { IAuthClient } from './IAuthClient.interface';
5
-
6
- /**
7
- * Interface for MSAL v2 compatible authentication provider.
8
- *
9
- * This interface defines the contract for authentication providers that maintain
10
- * backward compatibility with MSAL v2 API while using MSAL v4 implementation
11
- * under the hood. Used by the v2 proxy layer during gradual migration scenarios.
12
- *
13
- * @example
14
- * ```typescript
15
- * // Obtain a v2 proxy from the current provider
16
- * const v2Provider: IMsalProvider = provider.createProxyProvider('2.0.0');
17
- *
18
- * // Use v2-style API
19
- * await v2Provider.login();
20
- * const token = await v2Provider.acquireAccessToken({ scopes: ['User.Read'] });
21
- * ```
22
- */
23
- export interface IMsalProvider {
24
- /** Current version of the provider (MSAL module version) */
25
- version: string | SemVer;
26
-
27
- /** Current MSAL module version */
28
- msalVersion: MsalModuleVersion;
29
-
30
- /**
31
- * The MSAL PublicClientApplication instance (v2 compatible)
32
- */
33
- readonly client: IAuthClient;
34
-
35
- /**
36
- * The current authenticated account (v2 compatibility)
37
- * @deprecated Use activeAccount instead
38
- */
39
- readonly defaultAccount: AccountInfo | undefined;
40
-
41
- /**
42
- * The client configuration used to initialize this provider
43
- * @deprecated Configuration should not be exposed
44
- */
45
- readonly defaultConfig: unknown | undefined;
46
-
47
- /**
48
- * The MSAL client instance (v2 compatibility)
49
- * @deprecated Use client instead
50
- */
51
- readonly defaultClient: IAuthClient;
52
-
53
- /**
54
- * Create a new MSAL client instance
55
- * @deprecated This method is deprecated in MSAL v4
56
- */
57
- createClient(): IAuthClient;
58
-
59
- /**
60
- * Acquire an access token for the specified scopes
61
- * @param req - Auth request options (v2 compatible)
62
- */
63
- acquireAccessToken(req: { scopes: string[]; account?: AccountInfo }): Promise<string | undefined>;
64
-
65
- /**
66
- * Acquire full authentication result
67
- * @param req - Auth request options (v2 compatible)
68
- */
69
- acquireToken(req: {
70
- scopes: string[];
71
- account?: AccountInfo;
72
- }): Promise<AuthenticationResult | undefined>;
73
-
74
- /**
75
- * Login user interactively
76
- * @param options - Login options (v2 compatible)
77
- */
78
- login(options?: { onlyIfRequired?: boolean }): Promise<void>;
79
-
80
- /**
81
- * Logout user
82
- * @param options - Logout options (v2 compatible)
83
- */
84
- logout(options?: { redirectUri?: string }): Promise<void>;
85
-
86
- /**
87
- * Handle authentication redirect
88
- */
89
- handleRedirect(): Promise<undefined | null>;
90
-
91
- /**
92
- * Create a proxy provider for version compatibility
93
- * @param version - Version string
94
- * @returns Proxy provider
95
- */
96
- createProxyProvider<T = IMsalProvider>(version: string): T;
97
-
98
- /**
99
- * Dispose of the provider and clean up resources
100
- */
101
- dispose(): void;
102
- }
@@ -1,195 +0,0 @@
1
- import type { IMsalClient } from '../MsalClient.interface';
2
- import type { IAuthClient } from './IAuthClient.interface';
3
- import { mapAccountInfo } from './map-account-info';
4
- import { mapAuthenticationResult } from './map-authentication-result';
5
- import type { AccountInfo } from './types';
6
-
7
- /**
8
- * Creates a v2-compatible proxy wrapper around an MSAL v4 client.
9
- *
10
- * The proxy intercepts property access on the v4 `IMsalClient` and adapts method
11
- * signatures, return types, and account data to match the v2 `IAuthClient` interface.
12
- * This allows consumer code written against MSAL v2 to continue working unchanged
13
- * while the underlying implementation uses MSAL v4/v5.
14
- *
15
- * @param client - The MSAL v4 `IMsalClient` instance to wrap
16
- * @returns A proxy implementing the v2-compatible `IAuthClient` interface
17
- *
18
- * @example
19
- * ```typescript
20
- * const v4Client = new MsalClient(config);
21
- * const v2Client = createProxyClient(v4Client);
22
- *
23
- * // Use v2-compatible methods
24
- * const accounts = v2Client.getAllAccounts();
25
- * const result = await v2Client.acquireTokenSilent({ scopes: ['User.Read'], account });
26
- * ```
27
- */
28
- export function createProxyClient(client: IMsalClient): IAuthClient {
29
- const proxy = new Proxy(client, {
30
- get: (target: IMsalClient, prop: keyof IAuthClient) => {
31
- // Adapt each v4 client member to the v2-compatible shape expected by callers
32
- switch (prop) {
33
- case 'getAllAccounts': {
34
- return () => {
35
- // Map each v4 account shape to its v2-compatible equivalent
36
- return target.getAllAccounts().map(mapAccountInfo);
37
- };
38
- }
39
-
40
- case 'acquireTokenSilent': {
41
- return async (request: { scopes: string[]; account: AccountInfo }) => {
42
- const result = await target.acquireTokenSilent({
43
- scopes: request.scopes,
44
- account: request.account, // AccountInfo is compatible between v2/v4
45
- });
46
-
47
- return mapAuthenticationResult(result);
48
- };
49
- }
50
-
51
- case 'loginPopup': {
52
- return async (request?: { scopes?: string[] }) => {
53
- const result = await target.loginPopup({
54
- scopes: request?.scopes || [],
55
- });
56
-
57
- return mapAuthenticationResult(result);
58
- };
59
- }
60
-
61
- case 'logoutRedirect': {
62
- return async (request?: { postLogoutRedirectUri?: string; account?: AccountInfo }) => {
63
- await target.logoutRedirect({
64
- postLogoutRedirectUri: request?.postLogoutRedirectUri,
65
- account: request?.account,
66
- });
67
- };
68
- }
69
-
70
- case 'handleRedirectPromise': {
71
- return async () => {
72
- const result = await target.handleRedirectPromise();
73
-
74
- // No redirect result means there's nothing pending to map
75
- if (!result) {
76
- return null;
77
- }
78
-
79
- return mapAuthenticationResult(result);
80
- };
81
- }
82
-
83
- case 'getActiveAccount': {
84
- return () => {
85
- const account = target.getActiveAccount();
86
- // No active account means there's nothing to map
87
- if (!account) {
88
- return null;
89
- }
90
-
91
- return mapAccountInfo(account);
92
- };
93
- }
94
-
95
- case 'tenantId': {
96
- return target.tenantId;
97
- }
98
-
99
- case 'account': {
100
- const account = target.getActiveAccount();
101
- return account ? mapAccountInfo(account) : undefined;
102
- }
103
-
104
- case 'hasValidClaims': {
105
- return target.hasValidClaims;
106
- }
107
-
108
- case 'clientId': {
109
- return target.getConfiguration().auth.clientId;
110
- }
111
-
112
- case 'requestOrigin': {
113
- return target.getConfiguration().auth.redirectUri;
114
- }
115
-
116
- case 'login': {
117
- return async (
118
- options?: { scopes?: string[]; loginHint?: string },
119
- behavior?: 'popup' | 'redirect',
120
- silent?: boolean,
121
- ) => {
122
- return await target.login({
123
- request: {
124
- scopes: options?.scopes || [],
125
- loginHint: options?.loginHint,
126
- },
127
- behavior: behavior,
128
- silent: silent,
129
- });
130
- };
131
- }
132
-
133
- case 'acquireToken': {
134
- return async (
135
- options?: { scopes?: string[]; loginHint?: string },
136
- behavior?: 'popup' | 'redirect',
137
- silent?: boolean,
138
- ) => {
139
- return await target.acquireToken({
140
- request: {
141
- scopes: options?.scopes || [],
142
- account: target.getActiveAccount() ?? undefined,
143
- loginHint: options?.loginHint,
144
- },
145
- behavior: behavior,
146
- silent: silent,
147
- });
148
- };
149
- }
150
-
151
- case 'initialize':
152
- case 'acquireTokenPopup':
153
- case 'acquireTokenRedirect':
154
- case 'acquireTokenByCode':
155
- case 'addEventCallback':
156
- case 'removeEventCallback':
157
- case 'addPerformanceCallback':
158
- case 'removePerformanceCallback':
159
- case 'enableAccountStorageEvents':
160
- case 'disableAccountStorageEvents':
161
- case 'getConfiguration':
162
- case 'setActiveAccount':
163
- case 'getAccountByHomeId':
164
- case 'getAccountByLocalId':
165
- case 'getAccountByUsername':
166
- case 'loginRedirect':
167
- case 'logout':
168
- case 'logoutPopup':
169
- case 'ssoSilent':
170
- case 'getTokenCache':
171
- case 'setLogger':
172
- case 'getLogger':
173
- case 'initializeWrapperLibrary':
174
- case 'setNavigationClient':
175
- case 'hydrateCache':
176
- case 'clearCache': {
177
- // Cast through unknown to access properties that may exist on MSAL v4 but aren't in the type definition
178
- // or to handle v2-specific methods like clearCache that exist at runtime
179
- return (target as unknown as Record<string, unknown>)[prop];
180
- }
181
-
182
- default: {
183
- // TypeScript-only guard to catch missing properties at compile time
184
- const _exhaustiveCheck: never = prop;
185
- // For any other properties, return the original value
186
- return (target as unknown as IAuthClient)[_exhaustiveCheck];
187
- }
188
- }
189
- },
190
- });
191
-
192
- // The Proxy handler above implements every member of `IAuthClient` at runtime via its `get`
193
- // trap, but TypeScript can't verify a `Proxy<T>` satisfies `T` structurally.
194
- return proxy as unknown as IAuthClient;
195
- }
@@ -1,177 +0,0 @@
1
- import type { IMsalProvider } from '../MsalProvider.interface';
2
- import type { IMsalProvider as IMsalProvider_v2 } from './MsalProvider.interface';
3
- import type { AccountInfo as AccountInfo_v2 } from './types';
4
- import type { AcquireTokenOptions } from '../MsalClient.interface';
5
- import { createProxyClient } from './create-proxy-client';
6
- import { mapAccountInfo } from './map-account-info';
7
- import { MsalModuleVersion } from '../static';
8
-
9
- /**
10
- * Checks if a request is in MSAL v4 format.
11
- *
12
- * @param req - The request object to check
13
- * @returns True if the request is in v4 format (has a `request` property with `scopes` and `account`)
14
- */
15
- function isRequestV4(req: unknown): req is AcquireTokenOptions {
16
- // Non-object/null values can never match the v4 request shape
17
- if (typeof req !== 'object' || req === null) {
18
- return false;
19
- }
20
- const requestV4 = req as AcquireTokenOptions;
21
- return 'request' in requestV4;
22
- }
23
-
24
- /**
25
- * Creates a proxy provider for MSAL v2 compatibility.
26
- *
27
- * This function creates a Proxy that wraps the MSAL v4 provider and provides
28
- * v2-compatible method signatures and return types while using the latest
29
- * MSAL v4 implementation under the hood. The proxy handles type conversions
30
- * and method adaptations to maintain backward compatibility.
31
- *
32
- * @param provider - The base MSAL v4 provider instance to wrap
33
- * @returns A proxy provider implementing the v2-compatible interface
34
- *
35
- * @example
36
- * ```typescript
37
- * const baseProvider = new MsalProvider(config);
38
- * const v2Proxy = createProxyProvider(baseProvider);
39
- *
40
- * // Use v2-compatible API
41
- * await v2Proxy.login();
42
- * const token = await v2Proxy.acquireAccessToken({ scopes: ['User.Read'] });
43
- * ```
44
- */
45
- export function createProxyProvider(provider: IMsalProvider): IMsalProvider_v2 {
46
- // Create a v2-compatible client wrapper using the new client proxy
47
- const v2Client = createProxyClient(provider.client);
48
-
49
- // Use Proxy to intercept property access and provide v2-compatible implementations
50
- const proxy = new Proxy(provider, {
51
- get: (target: IMsalProvider, prop: keyof IMsalProvider_v2) => {
52
- // Adapt each v4 provider member to the v2-compatible shape expected by callers
53
- switch (prop) {
54
- case 'version': {
55
- return provider.version;
56
- }
57
- case 'msalVersion': {
58
- return MsalModuleVersion.V2;
59
- }
60
- case 'client': {
61
- // Return the v2-compatible client wrapper
62
- return v2Client as unknown as IMsalProvider_v2['client'];
63
- }
64
- case 'defaultClient': {
65
- // Deprecated property - redirect to client with warning
66
- console.warn('defaultClient is deprecated, use client instead');
67
- // Same v2-compatible client wrapper as the `client` case above.
68
- return v2Client as unknown as IMsalProvider_v2['defaultClient'];
69
- }
70
- case 'defaultAccount': {
71
- // Map v4 account to v2 format for backward compatibility
72
- const account = target.account;
73
- const defaultAccount: IMsalProvider_v2['defaultAccount'] = account
74
- ? mapAccountInfo(account)
75
- : undefined;
76
- return defaultAccount;
77
- }
78
- case 'defaultConfig': {
79
- // Deprecated property - not available in v4
80
- console.warn('defaultConfig is deprecated and not available in v4');
81
- return undefined;
82
- }
83
- case 'createClient': {
84
- // Deprecated method - return function that returns v2 client
85
- console.warn('createClient is deprecated in MSAL v4');
86
- const createClient: IMsalProvider_v2['createClient'] = () => v2Client;
87
- return createClient;
88
- }
89
- case 'acquireToken': {
90
- // Adapt v4 acquireToken to v2 signature with proper type mapping
91
- const acquireToken: IMsalProvider_v2['acquireToken'] = async (req: {
92
- scopes: string[];
93
- account?: AccountInfo_v2;
94
- }) => {
95
- const args = isRequestV4(req)
96
- ? req
97
- : { request: { scopes: req.scopes, account: req.account } };
98
- const result = await target.acquireToken(args);
99
-
100
- // Convert null to undefined for v2 compatibility
101
- return result || undefined;
102
- };
103
- return acquireToken;
104
- }
105
- case 'acquireAccessToken': {
106
- // Adapt v4 acquireAccessToken to v2 signature
107
- const acquireAccessToken: IMsalProvider_v2['acquireAccessToken'] = async (req: {
108
- scopes: string[];
109
- account?: AccountInfo_v2;
110
- }) => {
111
- const args = isRequestV4(req)
112
- ? req
113
- : { request: { scopes: req.scopes, account: req.account } };
114
- return await target.acquireAccessToken(args);
115
- };
116
- return acquireAccessToken;
117
- }
118
- case 'login': {
119
- // Adapt v4 login to v2 signature with optional parameters
120
- const login: IMsalProvider_v2['login'] = async (options?: {
121
- onlyIfRequired?: boolean;
122
- }) => {
123
- // Skip login if already authenticated and onlyIfRequired is true
124
- if (options?.onlyIfRequired && target.account) {
125
- return;
126
- }
127
- // Call v4 login with empty scopes (v2 behavior)
128
- await target.login({ request: { scopes: [] } });
129
- };
130
- return login;
131
- }
132
- case 'logout': {
133
- // Adapt v4 logout to v2 signature
134
- const logout: IMsalProvider_v2['logout'] = async (options?: { redirectUri?: string }) => {
135
- await target.logout({ redirectUri: options?.redirectUri });
136
- };
137
- return logout;
138
- }
139
- case 'handleRedirect': {
140
- // Adapt v4 handleRedirect to v2 signature
141
- const handleRedirect: IMsalProvider_v2['handleRedirect'] = async () => {
142
- await target.handleRedirect();
143
- // v2 expects null after redirect handling
144
- return null;
145
- };
146
- return handleRedirect;
147
- }
148
- case 'createProxyProvider': {
149
- // Generic method to create proxy providers for different versions
150
- const createProxyProvider: IMsalProvider_v2['createProxyProvider'] = <T = IMsalProvider>(
151
- version: string,
152
- ) => target.createProxyProvider(version as MsalModuleVersion) as T;
153
- return createProxyProvider;
154
- }
155
- case 'dispose': {
156
- // No-op dispose method for v2 compatibility
157
- return () => {
158
- /** noop */
159
- };
160
- }
161
- default: {
162
- // The proxy is awaited in some contexts, so `then` must resolve to undefined
163
- if (prop === 'then') {
164
- return undefined;
165
- }
166
- // Exhaustive check to ensure all v2 properties are handled
167
- const exhaustiveCheck: never = prop;
168
- // Fallback: return original property from target for any unhandled cases
169
- return (target as IMsalProvider)[exhaustiveCheck];
170
- }
171
- }
172
- },
173
- });
174
-
175
- // Return the proxy cast to v2 interface for type safety
176
- return proxy as unknown as IMsalProvider_v2;
177
- }
@@ -1,23 +0,0 @@
1
- import type { AccountInfo } from '@azure/msal-browser';
2
- import type { AccountInfo as AccountInfo_v2 } from './types';
3
-
4
- /**
5
- * Maps a current (v4/v5) `AccountInfo` object to the v2-compatible `AccountInfo` format.
6
- *
7
- * Strips properties that don't exist in v2, ensuring backward-compatible serialization
8
- * and type safety when passing account data through the v2 proxy layer.
9
- *
10
- * @param account - The current MSAL v4/v5 `AccountInfo` to convert
11
- * @returns A v2-compatible `AccountInfo` containing only fields recognised by MSAL v2 consumers
12
- */
13
- export function mapAccountInfo(account: AccountInfo): AccountInfo_v2 {
14
- return {
15
- homeAccountId: account.homeAccountId,
16
- environment: account.environment,
17
- tenantId: account.tenantId,
18
- username: account.username,
19
- localAccountId: account.localAccountId,
20
- name: account.name,
21
- idTokenClaims: account.idTokenClaims,
22
- };
23
- }
@@ -1,28 +0,0 @@
1
- import type { AuthenticationResult } from '@azure/msal-browser';
2
- import type { AuthenticationResult as AuthenticationResult_v2 } from './types';
3
- import { mapAccountInfo } from './map-account-info';
4
-
5
- /**
6
- * Maps a current (v4/v5) `AuthenticationResult` to the v2-compatible format.
7
- *
8
- * Converts the full authentication result — including the nested account object —
9
- * to the subset of fields that MSAL v2 consumers expect. This enables the v2 proxy
10
- * layer to return type-safe results without exposing v4/v5-only properties.
11
- *
12
- * @param result - The current MSAL v4/v5 `AuthenticationResult` to convert
13
- * @returns A v2-compatible `AuthenticationResult` with mapped account and token fields
14
- */
15
- export function mapAuthenticationResult(result: AuthenticationResult): AuthenticationResult_v2 {
16
- return {
17
- authority: result.authority,
18
- uniqueId: result.uniqueId,
19
- tenantId: result.tenantId,
20
- scopes: result.scopes,
21
- account: result.account ? mapAccountInfo(result.account) : null,
22
- idToken: result.idToken,
23
- idTokenClaims: result.idTokenClaims,
24
- accessToken: result.accessToken,
25
- expiresOn: result.expiresOn,
26
- tokenType: result.tokenType,
27
- } as AuthenticationResult_v2;
28
- }